tracing

package
v1.1.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

Documentation

Overview

Package tracing provides OpenTelemetry tracing initialization and helpers for all microservices. It configures OTLP exporters (HTTP or gRPC), sampling strategies, and context propagation. Each service calls InitProvider at startup to install a global TracerProvider, then uses GetTracer to obtain per-package tracers.

Configuration is resolved from a combination of struct fields and OTEL_* environment variables, with non-zero struct fields taking precedence. See Config for details.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeferShutdown

func DeferShutdown(shutdown func(context.Context) error) func()

DeferShutdown wraps a shutdown function (typically TracerProvider.Shutdown) with a 5-second timeout context and slog error logging. It returns a closure suitable for use with defer:

tracerShutdown, err := tracing.InitProvider(ctx, serviceName, getenv)
// ...
defer tracing.DeferShutdown(tracerShutdown)()

This intentionally creates a fresh context from context.Background() rather than reusing the caller's startup context. Shutdown runs because the startup context was cancelled (e.g. by SIGTERM), so reusing it would cause the span flush to return context.Canceled immediately without sending data. Unlike local cleanup (Close, Stop), the trace provider's Shutdown performs network I/O to flush buffered spans to the OTLP collector, so it needs a live context with its own deadline.

func DialOptionsWithTracing

func DialOptionsWithTracing() []grpc.DialOption

DialOptionsWithTracing returns the gRPC dial options needed to instrument outbound RPCs with OpenTelemetry tracing. It installs a renaming client stats handler that creates a client span for every non-health-check RPC and immediately renames it to the "grpc.<snake_case_method>" convention.

func GetTracer

func GetTracer(name string) trace.Tracer

GetTracer returns a named tracer from the global TracerProvider. Each package should call this once at init time with its package path to get a tracer for creating spans (e.g. tracing.GetTracer("auth-service/internal/service")).

func InitProvider

func InitProvider(ctx context.Context, serviceName string, getenv func(string) string) (func(context.Context) error, error)

InitProvider sets up the global OpenTelemetry TracerProvider and TextMapPropagator for a service. It creates an OTLP exporter, configures sampling, and registers everything with the otel global. Call this once at service startup. The provided context is used for exporter and resource initialization so that startup can be cancelled promptly (e.g. on SIGTERM).

The returned function flushes pending spans and shuts down the provider; call it during graceful shutdown (typically deferred in main).

Example

ExampleInitProvider shows the minimal call to install the global tracer provider at service startup. Configuration is resolved from OTEL_* environment variables with production defaults.

package main

import (
	"context"
	"os"

	"github.com/open-mrp/api/shared/tracing"
)

func main() {
	shutdown, err := tracing.InitProvider(context.Background(), "my-service", os.Getenv)
	if err != nil {
		panic(err)
	}
	defer tracing.DeferShutdown(shutdown)()
}

func RecordControllerError

func RecordControllerError(span trace.Span, err error)

RecordControllerError annotates a span with error information from an API controller. For structured APIErrors it records a rich "api.error" event with code, type, message, and optional fields (param, doc URL, internal error). For all other errors it falls back to the standard span.RecordError. In both cases the span status is set to Error.

func StartSpan

func StartSpan(ctx context.Context, tracer trace.Tracer, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span)

StartSpan creates a new span if tracing is enabled on the context, otherwise delegates to the no-op tracer so the caller receives a valid (but non-recording) span. This lets callers write straight-line code without checking [ShouldTrace] themselves — the returned span is always safe to use.

func Trace

func Trace(span trace.Span, err *apierror.APIError) *apierror.APIError

Trace records a structured "api.error" event on the given span and sets the span status to Error. It returns the same *APIError so callers can use it inline:

return tracing.Trace(span, apierror.NewBadRequest("invalid input"))

The recorded event carries attributes for every populated APIError field: error.code, error.type, error.public_message, error.internal_message, error.param, error.doc_url, error.internal_error. For 5xx errors a goroutine stacktrace is captured (truncated to [maxStacktraceAttrLen]) to aid post-mortem debugging.

A nil error or nil span is handled gracefully (returns nil / no-ops respectively).

func TracedConsumer

func TracedConsumer(delivery amqp.Delivery, queueName string, handler func(context.Context, amqp.Delivery) error) error

TracedConsumer wraps a RabbitMQ message handler with an OpenTelemetry consumer span. It extracts trace context from the delivery's AMQP headers (injected by TracedPublisher) so the consumer span becomes a child of the producer span, forming a complete publish → consume trace.

The span is named "rabbitmq.consume <normalized_name>" where the name is derived from the delivery's routing key (preferred) or the queueName fallback. Messaging semantic attributes (system, destination queue, exchange, routing key, operation) are attached.

Unlike TracedPublisher, this function does not check [ShouldTrace] because consumer spans are always desirable — the consumer has no way to know whether the publisher suppressed tracing, and the extracted parent context handles that naturally.

func TracedPublisher

func TracedPublisher(ctx context.Context, exchange, routingKey string, msg amqp.Publishing, publish func(context.Context, string, string, amqp.Publishing) error) error

TracedPublisher wraps a RabbitMQ publish call with an OpenTelemetry producer span. The span is named "rabbitmq.publish <normalized_routing_key>" and carries messaging.* semantic attributes (system, destination exchange, routing key, operation). Trace context is injected into the AMQP message headers so downstream consumers can link their spans to this publish.

If the context has tracing disabled via [WithNoTrace] (e.g. outbox background publishing), the publish function is called directly with no span overhead.

func UnarySpanRenamer

func UnarySpanRenamer() grpc.UnaryServerInterceptor

UnarySpanRenamer returns a gRPC unary server interceptor that renames the span created by the otelgrpc stats handler to the "grpc.<snake_case_method>" format. The stats handler creates spans with the raw "/package.Service/Method" name; this interceptor normalizes them so the trace backend shows concise, consistent names (e.g. "grpc.login_user" instead of "/auth.v1.AuthService/LoginUser"). Health-check RPCs are skipped since they are already filtered out at the stats handler level.

func WithTracingInterceptors

func WithTracingInterceptors() []grpc.ServerOption

WithTracingInterceptors returns the gRPC server options needed to instrument inbound RPCs with OpenTelemetry tracing. It installs an otelgrpc stats handler that creates a server span for every non-health-check RPC. Combine the returned options with any other server options when calling grpc.NewServer.

func WrapGatewayHandler

func WrapGatewayHandler(handler http.Handler) http.Handler

WrapGatewayHandler returns an http.Handler that wraps handler with OpenTelemetry tracing. For each inbound request it:

  1. Skips /healthz and OPTIONS requests (no span created).
  2. Extracts incoming trace context from HTTP headers (W3C TraceContext + Baggage).
  3. Starts a server span named "HTTP <METHOD> <route>" (e.g. "HTTP GET /api/v1/users").
  4. Calls the inner handler with the traced context.
  5. Records response status, route, host, query string, and user-agent as span attributes using both legacy and semantic-convention keys.
  6. Ends the span immediately after the response is written — not after downstream middleware completes — so span duration accurately reflects response latency.

Types

type Config

type Config struct {
	// ServiceName (required) identifies this service in trace backends (e.g. "auth-service"). Falls back to the serviceName argument passed to InitProvider.
	ServiceName string

	// Environment (optional; default: OTEL_ENVIRONMENT or "production") is the deployment environment (e.g. production, staging).
	Environment constants.PlatformMode

	// Endpoint (optional; default: OTEL_EXPORTER_OTLP_ENDPOINT) is the OTLP collector URL (e.g. "localhost:4318"). For HTTP, a full URL with scheme is parsed to extract host, path, and insecure mode automatically.
	Endpoint string

	// Protocol (optional; default: OTEL_EXPORTER_OTLP_PROTOCOL or "http") selects the OTLP transport: "http" or "grpc".
	Protocol constants.Protocol

	// Insecure (optional; default: OTEL_EXPORTER_OTLP_INSECURE) disables TLS for the exporter connection. The zero value (false) is treated as "unset" by withDefaults and falls back to the env var, so TLS cannot be forced on via this config when OTEL_EXPORTER_OTLP_INSECURE is truthy.
	Insecure bool

	// Headers (optional; default: OTEL_EXPORTER_OTLP_HEADERS) are sent with every export request (e.g. auth tokens). Env var format: comma-separated "key=value" pairs.
	Headers map[string]string

	// Sampler (optional; default: OTEL_TRACES_SAMPLER) selects the sampling strategy. Supported values: "parentbased_traceidratio", "traceidratio", "always_on", "always_off". When empty, a parent-based sampler with priority root sampling is used.
	Sampler string

	// SamplerArg (optional; default: OTEL_TRACES_SAMPLER_ARG or "0.1") is passed to ratio-based samplers as the sampling probability (0.0-1.0).
	SamplerArg string
}

Config holds all settings needed to initialize an OTLP trace exporter and sampler. Each field can be set explicitly or left zero to fall back to the corresponding OTEL_* environment variable (resolved in withDefaults). Explicit values take precedence over environment variables, except boolean fields (Insecure), whose false zero value falls back to the env var.

type WorkerTracerProvider

type WorkerTracerProvider struct {
	// contains filtered or unexported fields
}

WorkerTracerProvider wraps an independent TracerProvider for background workers (e.g. outbox processors, scheduled jobs). Using a separate provider causes worker traces to appear under a distinct service name ("{service}-worker") in the trace backend, keeping them visually separated from request-driven traces.

func NewWorkerTracerProvider

func NewWorkerTracerProvider(ctx context.Context, baseServiceName string, getenv func(string) string) (*WorkerTracerProvider, error)

NewWorkerTracerProvider creates a new TracerProvider configured identically to the global provider but registered under "{baseServiceName}-worker". The provided context is used for exporter and resource initialization. The caller is responsible for calling Shutdown on the returned provider during graceful shutdown.

func (*WorkerTracerProvider) DeferClose

func (w *WorkerTracerProvider) DeferClose() func()

DeferClose returns a closure that shuts down the worker provider with a 5-second timeout and logs any error. The double-parenthesis invocation pattern defers the returned closure so it runs at function exit:

defer workerTracer.DeferClose()()

func (*WorkerTracerProvider) Shutdown

func (w *WorkerTracerProvider) Shutdown(ctx context.Context) error

Shutdown flushes any pending spans and releases the worker provider's resources. The provided context controls the flush deadline; if it expires, pending spans may be lost. Typically called via WorkerTracerProvider.DeferClose.

func (*WorkerTracerProvider) Tracer

func (w *WorkerTracerProvider) Tracer(name string) trace.Tracer

Tracer returns a named tracer from the worker's independent TracerProvider. Spans created with this tracer appear under the "{service}-worker" service name in the trace backend, visually separating background work from request-driven traces.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL