otelpgx

repository·main·Indexed 42 days ago

https://github.com/exaring/otelpgx

OpenTelemetry instrumentation for the jackc/pgx library. Provides tracing for database operations—including queries, copies, batches, and connections—and exports pgxpool statistics as metrics. Requires Go 1.25+ and pgx v5+.

Tokens
3.7K
Snippets
14
Records
16
Agent score
52%

What's inside otelpgx

  1. Instrument pgx with OpenTelemetry using otelpgx.NewTracer()

    main

    To enable OpenTelemetry instrumentation for your pgx connection or pool, you must assign a new tracer to the ConnConfig.Tracer field of your pgxpool.Config.

    After establishing the connection, you can call otelpgx.RecordStats(conn) to begin recording database statistics.

    cfg, err := pgxpool.ParseConfig(connString)
    if err != nil {
        return nil, fmt.Errorf("create connection pool: %w", err)
    }
    
    // Attach the OpenTelemetry tracer to the connection configuration
    cfg.ConnConfig.Tracer = otelpgx.NewTracer()
    
    conn, err := pgxpool.NewWithConfig(ctx, cfg)
    if err != nil {
        return nil, fmt.Errorf("connect to database: %w", err)
    }
    
    // Record database statistics
    if err := otelpgx.RecordStats(conn); err != nil {
        return nil, fmt.Errorf("unable to record database stats: %w", err)
    }
  2. Install otelpgx and requirements

    main

    To use otelpgx, ensure you meet the following requirements:

    • Go 1.25 or higher
    • pgx v5 or higher

    Install the necessary dependencies using go get:

    go get github.com/jackc/pgx/v5
    go get github.com/exaring/otelpgx
  3. How the Tracer instruments pgx operations

    main

    The Tracer implements several pgx tracer interfaces (like pgx.Tracer and pgxpool.AcquireTracer). It follows a Start/End lifecycle for every operation:

    1. Start Methods (e.g., TraceQueryStart, TraceConnectStart):
      • Creates a new OpenTelemetry span.
      • Attaches metadata like connection details, SQL text, and operation type.
      • Returns a new context.Context containing the active span and a timestamp used for duration calculation.
    2. End Methods (e.g., TraceQueryEnd, TraceConnectEnd):
      • Records the operation duration.
      • Increments error counters if the operation failed (excluding sql.ErrNoRows).
      • Records PostgreSQL error codes (SQLState) if a pgconn.PgError occurs.
      • Records the number of rows affected for successful queries.
      • Closes the span.
  4. Record database statistics with otelpgx.RecordStats()

    main

    Call otelpgx.RecordStats(conn) after creating your pgx connection or pool to enable the recording of database statistics via OpenTelemetry.

    if err := otelpgx.RecordStats(conn); err != nil {
        return nil, fmt.Errorf("unable to record database stats: %w", err)
    }
  5. Configure Tracer options

    main

    The NewTracer function accepts variadic Option arguments to customize instrumentation. Common configuration capabilities include:

    • SQL Statement Logging: Control whether the SQL text is included in spans using logSQLStatement (default: true).
    • Connection Details: Control whether connection attributes (host, port, user, etc.) are attached to spans using logConnectionDetails (default: true).
    • Query Parameters: Include query arguments in spans using includeParams (default: false). You can provide a queryParamsFilter to selectively include parameters based on the SQL string.
    • Span Naming:
      • fullQuerySpanName: If true, uses the full SQL statement as the span name.
      • prefixQuerySpanName: If true, prefixes the span name (e.g., query SELECT ...).
      • spanNameCtxFunc: A custom function to determine the span name from the context and SQL.
    • Acquire Tracing: Disable tracing for pgxpool.Acquire operations using disableAcquireTracer.
  6. Record database pool statistics with RecordStats

    main

    Use RecordStats to export pgxpool.Pool statistics to OpenTelemetry. By default, it records statistics at a 1-second interval.

    It automatically includes default attributes: db.system.name (set to postgresql) and db.client.connection.pool.name (derived from the connection configuration). You can override these or change the sampling interval using StatsOption functions.

    To use this, you must provide an object that implements the PoolStats interface, such as a *pgxpool.Pool.

    // Assuming db is a *pgxpool.Pool
    err := otelpgx.RecordStats(db)
    if err != nil {
        // handle error
    }
  7. Configure query parameter recording

    main

    You can control whether SQL query parameters are recorded in spans using WithIncludeQueryParameters. This adds the parameters to the span attribute pgx.query.parameters.

    • If WithDisableSQLStatementInAttributes() is used, query parameter recording is implicitly disabled.
    • You can provide a QueryParametersFilterFunc to selectively omit parameters for specific queries based on the SQL text.

    QueryParametersFilterFunc signature: func(sql string) bool (return true to record, false to omit).

    // Include parameters only for non-sensitive queries
    otelpgx.WithIncludeQueryParameters(func(sql string) bool {
    	return !strings.Contains(sql, "users")
    })
  8. Configure pgx statistics collection with StatsOption

    main

    Use otelpgx.StatsOption to manage how pgx connection pool statistics are collected and exported as metrics. These options are distinct from the standard otelpgx.Option used for tracing.

    Available options:

    • WithStatsMeterProvider(provider metric.MeterProvider): Sets the meter provider for pgx stat metric collection.
    • WithStatsAttributes(attrs ...attribute.KeyValue): Adds default attributes to all pgx stat metrics.
    • WithMinimumReadDBStatsInterval(interval time.Duration): Sets the minimum interval between calls to db.Stats(). Negative values are ignored.
    statsOpts := []otelpgx.StatsOption{
    	otelpgx.WithStatsMeterProvider(myMeterProvider),
    	otelpgx.WithStatsAttributes(attribute.String("component", "db_stats")),
    	otelpgx.WithMinimumReadDBStatsInterval(5 * time.Second),
    }
  9. Customize SQL span names in traces

    main

    By default, otelpgx uses a low-cardinality operation name (e.g., "SELECT") as the span name to follow OpenTelemetry database span conventions. You can change this behavior using the following options:

    • WithFullSQLInSpanName(): Uses the entire SQL statement as the span name. Warning: This is discouraged as it can leak sensitive data and creates high cardinality in your tracing backend.
    • WithSpanNameFunc(fn SpanNameFunc): Uses a custom function to generate the span name based on the SQL statement.
    • WithSpanNameCtxFunc(fn SpanNameCtxFunc): Uses a custom function that has access to both the context.Context and the SQL statement to generate the span name.
    • WithQuerySpanNamePrefix(): Prefixes the span name with the operation kind (e.g., "query ", "prepare ", or "batch query ").
    // Use a custom function to name spans
    otelpgx.WithSpanNameFunc(func(stmt string) string {
    	return "custom_name_for_" + stmt[:5]
    })
  10. Configure OpenTelemetry tracing and metrics with otelpgx.Option

    main

    Use otelpgx.Option functions to customize how OpenTelemetry traces and metrics are collected. These options are typically passed during the initialization of the instrumented pgx driver/pool. You can specify custom providers, add global attributes, and control how SQL statements are represented in spans.

    // Example of using various options
    opts := []otelpgx.Option{
    	otelpgx.WithTracerProvider(myTracerProvider),
    	otelpgx.WithMeterProvider(myMeterProvider),
    	otelpgx.WithTracerAttributes(attribute.String("env", "production")),
    	otelpgx.WithIncludeQueryParameters(func(sql string) bool {
    		return !strings.Contains(sql, "sensitive_table")
    	}),
    }
    // These options would then be used in your pgx configuration setup
  11. Initialize the pgx Tracer with NewTracer

    main

    Use NewTracer to create a Tracer instance that instruments pgx operations with OpenTelemetry traces and metrics. By default, it uses the global OpenTelemetry TracerProvider and MeterProvider. You can pass optional configuration functions to customize behavior such as SQL logging, parameter inclusion, and span naming.

    Supported operations instrumented include:

    • query (Query, QueryRow, Exec)
    • copy (CopyFrom)
    • batch (SendBatch)
    • connect (Connect, ConnectConfig)
    • prepare (Prepare)
    • acquire (pgxpool.Acquire)
    import "github.com/exaring/otelpgx"
    
    // Create a tracer with default options
    tracer := otelpgx.NewTracer()
    
    // Example: Use the tracer with pgx (implementation depends on how you pass the tracer to pgx)
  12. Reference: Tracer OpenTelemetry Attributes

    main

    The Tracer emits several custom OpenTelemetry attributes for enriched observability. These keys are used in both spans and metrics.

    // RowsAffectedKey represents the number of rows affected.
    RowsAffectedKey = attribute.Key("pgx.rows_affected")
    
    // QueryParametersKey represents the query parameters.
    QueryParametersKey = attribute.Key("pgx.query.parameters")
    
    // PrepareStmtNameKey represents the prepared statement name.
    PrepareStmtNameKey = attribute.Key("pgx.prepare_stmt.name")
    
    // SQLStateKey represents PostgreSQL error code.
    SQLStateKey = attribute.Key("pgx.sql_state")
    
    // PGXOperationTypeKey represents the pgx tracer operation type (e.g., "query", "copy").
    PGXOperationTypeKey = attribute.Key("pgx.operation.type")
    
    // DBClientOperationErrorsKey represents the count of operation errors.
    DBClientOperationErrorsKey = attribute.Key("db.client.operation.errors")