telemetry

package module
v0.3.4 Latest Latest
Warning

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

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

Documentation

Overview

Package telemetry sets up OpenTelemetry tracing and metrics and structured logs correlated with requests and traces (ADR-0007).

Tracing is always on, so every log line written with a request context carries trace and span IDs. Spans and metrics are exported only when OTLP export is enabled; the exporters read the standard OTEL_EXPORTER_OTLP_* environment variables, as OpenTelemetry specifies. Metrics can also be scraped in the Prometheus format from Telemetry.MetricsHandler (ADR-0063).

By default Setup installs its providers and the W3C trace-context propagator as OpenTelemetry globals, which instrumentation libraries use. Call it once, from the composition root.

Stability: stable (ADR-0015, ADR-0054).

Index

Constants

View Source
const (
	KeyRequestID = "request_id"
	KeyTraceID   = "trace_id"
	KeySpanID    = "span_id"
	KeyOrgID     = "org_id"
)

Log attribute keys added from the context.

Variables

This section is empty.

Functions

func NewLogHandler

func NewLogHandler(h slog.Handler) slog.Handler

NewLogHandler wraps h so records logged with a context carry request_id, trace_id, span_id and org_id when available. Keys already present on the record are not added twice.

func RecordRoute

func RecordRoute(router http.Handler) http.Handler

RecordRoute wraps the application's router, normally the http.ServeMux at the end of the middleware chain, so Telemetry.HTTPMiddleware names the span and labels HTTP metrics with the route pattern the router matched, even when middleware between them replaced the request with r.WithContext:

handler := httpx.Chain(telemetry.RecordRoute(mux), tel.HTTPMiddleware(), auth, …)

Routes are the patterns registered in code, so they keep metric series bounded whatever paths clients request. Without HTTPMiddleware, RecordRoute does nothing.

Types

type LogFormat

type LogFormat string

LogFormat selects the log encoding.

const (
	LogFormatJSON LogFormat = "json"
	LogFormatText LogFormat = "text"
)

Log formats.

type Option

type Option interface {
	// contains filtered or unexported methods
}

An Option configures Setup.

func WithLogFormat

func WithLogFormat(f LogFormat) Option

WithLogFormat sets JSON (default, for production) or text (for local development) logs.

func WithLogLevel

func WithLogLevel(l slog.Leveler) Option

WithLogLevel sets the minimum log level. Default: info.

func WithLogTee

func WithLogTee(h slog.Handler) Option

WithLogTee also sends every log record to h, such as the development console's buffer of recent records (ADR-0065) or the hourly log archive (ADR-0079). h's Enabled decides which records it receives, independently of WithLogLevel, and it sees the request, trace and organisation attributes the logger adds. Given more than once, every handler receives every record. A nil h adds nothing. Default: none.

func WithLogWriter

func WithLogWriter(w io.Writer) Option

WithLogWriter sets where logs are written. Default: os.Stdout.

func WithOTLPExport

func WithOTLPExport(enabled bool) Option

WithOTLPExport enables exporting spans and metrics over OTLP/HTTP, configured by the standard OTEL_EXPORTER_OTLP_* environment variables.

func WithPrometheus

func WithPrometheus(enabled bool) Option

WithPrometheus enables a Prometheus exporter, alongside OTLP export when both are on: Telemetry.MetricsHandler then serves every metric of the meter provider in the Prometheus text format (ADR-0063). The exporter uses its own registry, never Prometheus's global one. Default: off.

func WithRuntimeMetrics

func WithRuntimeMetrics() Option

WithRuntimeMetrics records Go runtime metrics (memory, garbage collector goal, goroutines, GOMAXPROCS) with the OpenTelemetry runtime instrumentation. They are read from runtime/metrics when metrics are collected, so they cost nothing while no exporter is on. Default: off.

func WithSampleRatio

func WithSampleRatio(r float64) Option

WithSampleRatio sets the fraction of new traces sampled, from 0 to 1. Child spans follow their parent's decision. Default: 1.

func WithSpanExporter

func WithSpanExporter(e sdktrace.SpanExporter) Option

WithSpanExporter adds a synchronous span exporter, for tests and custom backends.

func WithTraceContextFrom

func WithTraceContextFrom(callers []netip.Prefix) Option

WithTraceContextFrom makes Telemetry.HTTPMiddleware continue the trace, and accept the baggage, of requests whose client address is in callers: gateways and internal services that start traces. Match addresses as the middleware sees them, after httpx.TrustedProxies. Requests from other addresses start a new trace linked to the incoming one. Default: none.

func WithoutGlobals

func WithoutGlobals() Option

WithoutGlobals keeps Setup from installing OpenTelemetry globals.

type Telemetry

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

Telemetry holds the configured logger and OpenTelemetry providers.

func Setup

func Setup(ctx context.Context, service, version string, opts ...Option) (*Telemetry, error)

Setup configures telemetry for service at version.

func (*Telemetry) HTTPMiddleware

func (t *Telemetry) HTTPMiddleware() func(http.Handler) http.Handler

HTTPMiddleware starts a server span for each request, records standard HTTP server metrics, and names the span after the matched http.ServeMux pattern (for example "GET /v1/projects/{id}") so traces group by route, not by raw path.

Incoming W3C trace context is untrusted by default: each request starts a new trace, sampled by WithSampleRatio, that links to the caller's span, and incoming baggage is ignored. A client can't hide its requests from tracing, force sampling, or join another request's trace. Requests from the callers given to WithTraceContextFrom continue their trace instead.

The span's client.address is the request's RemoteAddr, as resolved by httpx.TrustedProxies when installed before this middleware, never a raw X-Forwarded-For header. Metrics leave out server.address and server.port, which come from the client's Host header, and carry http.route, the matched pattern without its method, instead of the path.

http.ServeMux sets the pattern on the request it routes, which is a copy when a middleware between this one and the mux calls r.WithContext. Wrap the mux with RecordRoute so the route reaches spans and metrics anyway.

func (*Telemetry) Logger

func (t *Telemetry) Logger() *slog.Logger

Logger returns the correlated structured logger.

func (*Telemetry) MeterProvider

func (t *Telemetry) MeterProvider() metric.MeterProvider

MeterProvider returns the meter provider.

func (*Telemetry) MetricsHandler

func (t *Telemetry) MetricsHandler() http.Handler

MetricsHandler returns the Prometheus scrape handler enabled by WithPrometheus, or a handler answering 404 when it's off. Serve it on a separate listener bound to a private interface, never on the API's public address: it has no authentication, and metrics describe the service's routes and load.

Scrapes collect on demand. At most four run at once (others get 503), each for up to 10 seconds; collection errors are logged and the metrics that could be collected are still served.

func (*Telemetry) Shutdown

func (t *Telemetry) Shutdown(ctx context.Context) error

Shutdown flushes and stops exporters. Register it first on the cleanup stack so it runs last.

func (*Telemetry) TracerProvider

func (t *Telemetry) TracerProvider() trace.TracerProvider

TracerProvider returns the tracer provider.

Jump to

Keyboard shortcuts

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