# How to Configure Distributed Tracing with OpenTelemetry in Fabrica-Kit

> Learn to configure distributed tracing with OpenTelemetry in Fabrica-Kit using trace.Init() for automatic log correlation with Redis and PostgreSQL. Simplify your observability.

- Repository: [Pantheon/fabrica-kit](https://github.com/go-pantheon/fabrica-kit)
- Tags: how-to-guide
- Published: 2026-03-02

---

**TLDR:** Fabrica-Kit provides a built-in OpenTelemetry integration through `trace.Init()` that configures an OTLP HTTP exporter, plus specialized helpers for Redis (`traceredis`) and PostgreSQL (`tracepg`) to create an end-to-end tracing pipeline with automatic log correlation.

The `go-pantheon/fabrica-kit` repository offers a lightweight, opinionated OpenTelemetry integration that enables distributed tracing across your entire service stack. By configuring the global tracer provider and instrumenting external dependencies, you can capture full request flows from HTTP handlers through database queries and cache operations. This guide covers the exact implementation details found in the source code to help you configure distributed tracing with OpenTelemetry in fabrica-kit.

## Initialize the Global Tracer Provider

All tracing functionality in Fabrica-Kit centers around the global tracer provider defined in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go). The `trace.Init()` function creates an OTLP HTTP exporter, configures resource attributes, and registers the provider with `otel.SetTracerProvider()`.

```go
package main

import (
    "log"
    "github.com/go-pantheon/fabrica-kit/trace"
)

func main() {
    // Endpoint for the OTLP collector (e.g., Jaeger, Tempo, or SaaS backend)
    // Format: host:port without scheme or trailing slashes
    const collectorURL = "otel-collector.example.com:4318"

    // Initialize with service name, profile, and colour attributes
    if err := trace.Init(collectorURL, "order-service", "prod", "blue"); err != nil {
        log.Fatalf("failed to init tracing: %v", err)
    }

    // Subsequent OpenTelemetry calls now use this configured provider
}

```

Once initialized, every `context.Context` created by your application carries trace IDs. The provider remains active for the process lifetime, pushing spans to the configured collector endpoint.

## Instrument External Services

Fabrica-Kit ships dedicated instrumentation packages for Redis and PostgreSQL that automatically wrap clients with OpenTelemetry span creation.

### Redis Instrumentation

The [`trace/traceredis/redis.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/traceredis/redis.go) file exposes `WithTracing()`, which wraps any `redis.UniversalClient` with the `redisotel` instrumentation library. This creates automatic spans for each Redis command (e.g., `redis.get`, `redis.set`).

```go
import (
    "context"
    "github.com/go-pantheon/fabrica-kit/trace/traceredis"
    "github.com/redis/go-redis/v9"
)

func newRedisClient() redis.UniversalClient {
    rdb := redis.NewUniversalClient(&redis.UniversalOptions{
        Addrs: []string{"redis:6379"},
    })

    // Enable automatic span creation for all Redis commands
    if err := traceredis.WithTracing(rdb, nil); err != nil {
        panic(err)
    }
    return rdb
}

```

For connection-level observability, use `StartConnectionSpan()` or `StartClusterConnectionSpan()` to manually create spans around connection establishment events.

```go
func connectToShard(ctx context.Context, addr string) {
    ctx, span := traceredis.StartConnectionSpan(ctx, "connect", addr, 0)
    defer span.End()

    // Connection logic here emits a distinct "connect" span
}

```

### PostgreSQL Instrumentation

The [`trace/tracepg/postgresql.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/tracepg/postgresql.go) file provides `NewDB()`, which constructs a pgx connection pool instrumented with `otelpgx`. Every query execution generates a span (`db.postgresql.query`) with standard attributes like `db.system` and `db.statement`.

```go
import (
    "context"
    pgtrace "github.com/go-pantheon/fabrica-kit/trace/tracepg"
    "github.com/go-pantheon/fabrica-util/data/db/pg"
)

func newDB(ctx context.Context) (*pg.DB, func()) {
    cfg := pg.Config{
        DSN:    "postgres://user:pass@pg:5432/orders?sslmode=disable",
        DBName: "orders",
    }

    // Build PostgreSQL-specific tracing configuration
    pgCfg := pgtrace.DefaultPostgreSQLConfig(cfg)
    
    // Optional: include query parameters in spans for debugging
    pgCfg.IncludeQueryParameters = true

    db, cleanup, err := pgtrace.NewDB(ctx, pgCfg)
    if err != nil {
        panic(err)
    }
    return db, cleanup
}

```

The `IncludeQueryParameters` option allows sensitive query values to be attached as span attributes, useful for local debugging but typically disabled in production.

## Correlate Logs with Trace IDs

Distributed traces become actionable when joined with application logs. The [`xlog/log.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go) implementation automatically extracts the current trace ID from any `context.Context` using `tracing.TraceID()` and injects it into structured log output.

```go
import "github.com/go-pantheon/fabrica-kit/xlog"

func handler(w http.ResponseWriter, r *http.Request) {
    // Pass the request context to inherit the trace ID
    logger := xlog.NewLogger(r.Context())
    logger.Info("processing order", "order_id", "12345")
    
    // Output includes: {"msg":"processing order","trace":"abc123...","order_id":"12345"}
}

```

Because the context propagates through Redis and PostgreSQL calls, log entries emitted from within those operations carry the same root trace ID, enabling end-to-end correlation across services.

## Summary

- **`trace.Init()`** in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) configures the global OTLP exporter and service attributes once at startup.
- **`traceredis.WithTracing()`** wraps Redis clients to emit automatic command spans, while `StartConnectionSpan()` handles manual connection tracing.
- **`tracepg.NewDB()`** creates instrumented PostgreSQL pools with optional query parameter logging via `IncludeQueryParameters`.
- **`xlog.NewLogger()`** extracts `tracing.TraceID()` from context to correlate every log line with its distributed trace.

## Frequently Asked Questions

### What OpenTelemetry protocol does fabrica-kit use for trace export?

The `trace.Init()` function in [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) configures an **OTLP HTTP exporter** that pushes spans to the collector URL you specify (default port 4318). This follows the standard OpenTelemetry Protocol specification and is compatible with Jaeger, Grafana Tempo, Datadog, and other OTLP-compliant backends.

### Can I include raw SQL parameters in my PostgreSQL trace spans?

Yes. When configuring the PostgreSQL tracer in [`trace/tracepg/postgresql.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/tracepg/postgresql.go), set `pgCfg.IncludeQueryParameters = true` before passing the config to `NewDB()`. This attaches query arguments as span attributes, though you should disable this in production to prevent leaking sensitive data.

### How do I create custom spans for Redis connection events?

Use `traceredis.StartConnectionSpan()` or `traceredis.StartClusterConnectionSpan()` from [`trace/traceredis/redis.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/traceredis/redis.go). These functions accept a context, operation name, and address, returning a decorated context and span that you must close with `defer span.End()`.

### Does fabrica-kit automatically propagate trace context through all log outputs?

Yes. When you use `xlog.NewLogger(ctx)` as shown in [`xlog/log.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go), the logger automatically calls `tracing.TraceID()` to extract the current OpenTelemetry trace identifier from the context. Every log entry emitted through that logger includes a `trace` field, enabling seamless correlation between logs and traces without manual instrumentation.