# Pentagi Logging Mechanisms: Structured Logrus, HTTP Tracing, and OpenTelemetry Integration

> Explore Pentagi's advanced logging mechanisms, including structured Logrus, HTTP tracing, and OpenTelemetry. Gain comprehensive observability into the vxcontrol/pentagi platform.

- Repository: [VXControl/pentagi](https://github.com/vxcontrol/pentagi)
- Tags: internals
- Published: 2026-03-21

---

**Pentagi implements a four-layer logging architecture that combines structured Logrus logging with contextual flow tracking, HTTP/GraphQL request tracing, and full OpenTelemetry observability for comprehensive monitoring across the vxcontrol/pentagi platform.**

Pentagi is an open-source AI security automation platform that requires robust observability to track complex multi-step flows. The logging system in `vxcontrol/pentagi` uses a hierarchical approach where every log entry is enriched with flow, task, and subtask identifiers to enable precise debugging and distributed tracing.

## Layered Logging Architecture

The Pentagi logging mechanisms consist of four integrated layers that work together to provide consistent, searchable telemetry.

### Structured Logrus with Contextual Enrichment

At the foundation, Pentagi uses **Logrus** for structured JSON logging. The key innovation is the `enrichLogrusFields` function in [`backend/pkg/tools/tools.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/tools.go) (lines 1536-1549), which automatically injects execution context into every log entry.

When tools execute operations, they create loggers that include:

- `flow_id` – The unique identifier for the workflow execution
- `task_id` – The specific task within the flow  
- `subtask_id` – The granular subtask identifier
- `component` – The tool or service name

```go
// From backend/pkg/tools/traversaal.go (lines 57-67)
logger := logrus.WithContext(ctx).WithFields(
    enrichLogrusFields(t.flowID, t.taskID, t.subtaskID, logrus.Fields{
        "component": "traversaal",
    }),
)

if err := json.Unmarshal(action.Args, &args); err != nil {
    logger.WithError(err).Error("failed to unmarshal traversaal search action")
}

```

This enrichment ensures that every log line can be correlated back to its exact execution context, making it possible to trace a single flow through thousands of concurrent operations.

### HTTP Request Tracing via Gin Middleware

For API observability, Pentagi registers the `WithGinLogger` middleware in [`backend/pkg/server/logger/logger.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/logger/logger.go) (lines 23-63). This middleware intercepts every HTTP request and records:

- Client IP and request URI
- HTTP method and status code  
- Request duration
- Error details for 4xx/5xx responses

```go
// Registration in backend/pkg/server/router.go
router := gin.New()
router.Use(logger.WithGinLogger("pentagi-api"))

// Produces log entries like:
// {"component":"api","http_method":"GET","http_uri":"/flows/12/msglogs","duration":"12.3ms","http_status_code":200,"level":"debug"}

```

Successful requests log at the **debug** level, while failures (HTTP ≥ 400) automatically log at **error** level with full context.

### GraphQL Operation Logging

The GraphQL layer uses `WithGqlLogger` (lines 66-98 in [`backend/pkg/server/logger/logger.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/logger/logger.go)) to wrap resolver execution. This middleware captures:

- GraphQL operation name and type (query/mutation/subscription)
- Execution duration
- Resolver-level errors

```go
// From backend/pkg/server/logger/logger.go
srv := handler.NewDefaultServer(generated.NewExecutableSchema(...))
srv.AroundResponses(logger.WithGqlLogger("pentagi-gql"))

// Example output:
// {"component":"pentagi-gql","operation_name":"CreateFlow","operation_type":"mutation","duration":"45ms","level":"debug"}

```

This provides visibility into AI flow creation and management operations without exposing sensitive query parameters in logs.

### OpenTelemetry Integration

The fourth layer extends beyond traditional logging to full observability. The `observability.NewTelemetryClient` in [`backend/pkg/observability/otelclient.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/observability/otelclient.go) initializes an **OTLP (OpenTelemetry Protocol)** client that exports logs, metrics, and traces to compatible collectors like Grafana Tempo or Prometheus.

```go
cfg := config.Load() // Reads TELEMETRY_ENDPOINT env var
telemetry, _ := observability.NewTelemetryClient(context.Background(), cfg)

// Emit structured logs to OTLP collector
otelLogger := telemetry.Logger()
otelLogger.LogRecord(ctx, otellog.Record{
    Severity: otellog.SeverityInfo,
    Body:     otellog.StringValue("Flow started"),
    Attributes: []attribute.KeyValue{
        attribute.Int64("flow_id", flowID),
    },
})

```

This integration allows Pentagi to feed observability data into existing enterprise monitoring infrastructure while maintaining the same contextual fields used in Logrus logging.

## Key Implementation Files

Understanding the Pentagi logging mechanisms requires familiarity with these specific source files:

| File | Purpose |
|------|---------|
| [`backend/pkg/tools/tools.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/tools.go) | Contains `enrichLogrusFields` (lines 1536-1549) for adding flow/task/subtask IDs to log entries |
| [`backend/pkg/server/logger/logger.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/logger/logger.go) | Implements `WithGinLogger` and `WithGqlLogger` middleware, plus `FromContext` helper |
| [`backend/pkg/observability/otelclient.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/observability/otelclient.go) | OpenTelemetry client initialization and OTLP export configuration |
| [`backend/pkg/server/router.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/router.go) | Registers HTTP logging middleware with the Gin router |
| [`backend/pkg/tools/traversaal.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/traversaal.go) | Example tool implementation showing enriched logger usage (lines 57-67) |
| [`backend/pkg/server/services/msglogs.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/services/msglogs.go) | Demonstrates error logging patterns using `logger.FromContext` |

## Summary

The Pentagi logging mechanisms provide enterprise-grade observability through these key capabilities:

- **Structured JSON logging** via Logrus with automatic contextual enrichment using `enrichLogrusFields`
- **Request correlation** across HTTP (Gin) and GraphQL layers using middleware that captures timing and error states
- **Execution context tracking** that binds every log entry to specific flow, task, and subtask IDs for precise debugging
- **OpenTelemetry export** enabling integration with OTLP-compatible observability platforms for distributed tracing and metrics
- **Dual-level output** with debug logging for successes and automatic error escalation for failed operations

## Frequently Asked Questions

### How does Pentagi correlate logs across different tools and services?

Pentagi uses the `enrichLogrusFields` helper function defined in [`backend/pkg/tools/tools.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/tools.go) to inject consistent identifiers (`flow_id`, `task_id`, `subtask_id`) into every Logrus entry. When a tool like the Traversaal search in [`backend/pkg/tools/traversaal.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/traversaal.go) initializes its logger, it passes these IDs along with a component name. This ensures that logs from the web browser, terminal, or search tools can be filtered and joined using the same execution context fields.

### What environment variables configure Pentagi's OpenTelemetry export?

The OpenTelemetry client in [`backend/pkg/observability/otelclient.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/observability/otelclient.go) reads configuration via `config.Load()`, which typically sources the `TELEMETRY_ENDPOINT` environment variable to specify the OTLP collector URL. Additional standard OpenTelemetry environment variables (like `OTEL_SERVICE_NAME` or `OTEL_EXPORTER_OTLP_HEADERS`) are respected by the underlying SDK, allowing flexible deployment to Grafana Tempo, Prometheus, or other observability backends without code changes.

### How does Pentagi handle error logging versus debug logging in HTTP requests?

The `WithGinLogger` middleware in [`backend/pkg/server/logger/logger.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/logger/logger.go) (lines 23-63) implements automatic log level selection based on HTTP status codes. Requests completing with status < 400 are logged at the **debug** level to reduce noise in production logs, while any request returning 4xx or 5xx status codes automatically triggers **error** level logging with full request context including the URI, method, and duration. This provides immediate visibility into API failures without requiring manual log level configuration.

### Can Pentagi's logging mechanism trace individual AI operations within a flow?

Yes. Every AI operation, tool execution, and subtask within a Pentagi flow receives a unique `subtask_id` that propagates through the context. When tools log messages using `logrus.WithContext(ctx).WithFields()`, the `enrichLogrusFields` function extracts these IDs from the context and attaches them to the log entry. This allows operators to query for a specific `flow_id` in their log aggregation system (or OTLP backend) and retrieve the complete execution trace from flow initialization through individual AI tool calls to final output generation.