obs

package module
v0.0.0-...-3c749c4 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2026 License: Apache-2.0 Imports: 36 Imported by: 0

README

obs

standardized OTel configuration - nothing of interest

Documentation

Overview

Package obs is the atomdrift shared OpenTelemetry wiring.

Services call Init once during startup with a Config (zero values fall back to OTEL_* env vars), defer the returned shutdown into their signal-handling path, and then use Meter, Tracer, Job, and the HTTP helpers as needed.

With nothing configured, OTLP push defaults to the atomdrift Grafana Cloud gateway, authenticated with the base64 credential in ~/.tok/graf; all three signals share that single gateway (Mimir, Tempo, Loki behind it). Explicit configuration instead follows the self-hosted fleet's per-signal split:

  • Metrics use the base endpoint (Config.Endpoint or OTEL_EXPORTER_OTLP_ENDPOINT, else OTEL_EXPORTER_OTLP_METRICS_ENDPOINT), which points at the Prometheus OTLP receiver.
  • Logs go to Loki. A logs-specific OTEL_EXPORTER_OTLP_LOGS_ENDPOINT is honored verbatim; otherwise, whenever a base endpoint is set, logs fall back to DefaultLogsEndpoint instead of the base — the Prometheus OTLP receiver 404s logs.
  • Traces push only when a traces-specific OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set. The self-hosted fleet has no trace backend, so a bare base endpoint does not enable trace export (sending spans to Prometheus would 404). The Grafana Cloud default has Tempo, so it does.

Exported logs are gated to warn and above; stderr keeps the full debug stream. The Prometheus scrape handler exposed by MetricsHandler is always available so callers can mount /_/metrik regardless of OTLP wiring.

Index

Constants

View Source
const DefaultLogsEndpoint = "http://otel:3100/otlp/v1/logs"

DefaultLogsEndpoint is the atomdrift Loki OTLP logs receiver. obs routes logs here when self-hosted OTLP push is enabled (a base endpoint is set) but no logs-specific endpoint was given, because the base endpoint is the Prometheus OTLP receiver, which 404s logs. Override per service with OTEL_EXPORTER_OTLP_LOGS_ENDPOINT.

Variables

This section is empty.

Functions

func HTTPClient

func HTTPClient(c *http.Client) *http.Client

HTTPClient returns a copy of c with its transport wrapped by Transport, so outbound requests are instrumented while c's other settings (timeout, redirect policy, cookie jar) are preserved. A nil c clones http.DefaultClient. Use the returned client wherever the service makes HTTP calls and client metrics appear with no further wiring.

func Init

func Init(ctx context.Context, cfg Config) (func(context.Context) error, error)

Init wires up OpenTelemetry for the calling service. It is safe to call exactly once per process; a second call returns an error.

Init installs global trace, meter, and logger providers; replaces slog.Default with a handler that tees to stderr and the OTel log pipeline; and enables push exporters only for signals whose endpoint is configured. The returned shutdown should be invoked with a deadline-bearing context during graceful termination.

func Job

func Job(ctx context.Context, name string, fn func(context.Context) error) error

Job runs fn inside a span named after name and records both the duration histogram and an outcome-labeled counter. The returned error is fn's error verbatim, after being recorded on the span.

Safe to call before Init: if instruments are unset, Job still invokes fn but emits no telemetry.

func Meter

func Meter() metric.Meter

Meter returns a Meter scoped to the obs package. Callers that want a service-scoped meter should use otel.Meter("their-package") instead.

func MetricsHandler

func MetricsHandler() http.Handler

MetricsHandler returns the Prometheus scrape handler backed by the OTel meter provider. Mount it at /_/metrik on the service's existing mux. Responds 503 until Init has run.

func Middleware

func Middleware(next http.Handler) http.Handler

Middleware wraps an http.Handler with OTel HTTP server instrumentation: trace propagation, a span per request, and the standard http.server.request.duration / http.server.active_requests metrics. Each series is labeled by method, status code, and — crucially for useful dashboards — http.route, the matched route template.

The route label is populated automatically from the request pattern when the wrapped handler is an http.ServeMux registered with Go 1.22+ method/pattern routes (e.g. "GET /api/file/{sha256}"); the {sha256} stays a placeholder, so cardinality is bounded. Services using a third-party router that does not set http.Request.Pattern get the metrics without a route label — wrap individual routes with otelhttp.WithRouteTag to restore it.

Safe to call before Init — the underlying otelhttp uses the global tracer/meter, which are no-op until Init replaces them.

func NoOp

func NoOp() func(context.Context) error

NoOp returns a shutdown that does nothing. Useful in tests so test binaries don't try to dial an OTLP endpoint.

func OTLPSlogHandler

func OTLPSlogHandler(serviceName string) slog.Handler

OTLPSlogHandler returns just the OTel-bridge slog handler — records passed through it are exported via the global LoggerProvider. Useful for services with bespoke logging chains (e.g. file + stderr fan-out) that want to add OTLP as one more sink. Compose it into your own fan-out and call slog.SetDefault yourself; pair with Config.DisableSlog = true so Init doesn't fight you.

func PoolStats

func PoolStats(name string, pool *pgxpool.Pool) error

PoolStats wires async gauges that expose pgxpool connection-pool counters under db.pool.*. Call once after the pool is created. Multiple pools may be registered with distinct names (e.g. "hopper", "replica"); the name becomes a "pool" attribute.

Safe to call before Init: the global no-op meter accepts the instrument creation and the callbacks do nothing useful until Init installs the real provider.

func Span

func Span(ctx context.Context, name string) (context.Context, trace.Span)

Span starts an internal span named name as a child of whatever span is already in ctx (for a request path, the otelhttp server span). It is the lightweight primitive for timing one phase of work: unlike Job it records no worker.job.* metrics, so it suits request-path phases whose aggregate latency is tracked elsewhere (or not worth a dedicated instrument) but which still need per-request attribution in a trace. The caller must end the returned span, conventionally via defer:

ctx, span := obs.Span(ctx, "prism.detail.lookup")
defer span.End()

Safe before Init: with no tracer provider installed the returned span is non-recording and does nothing.

func TeeSlog

func TeeSlog(base slog.Handler, serviceName string) slog.Handler

TeeSlog returns a handler that fans every record to base and to the OTel logger provider. The shape of base (stdout vs stderr, JSON vs text, level filter) is preserved exactly. Use this when you already have a configured handler and just want to add OTLP as a second sink.

func Tracer

func Tracer() trace.Tracer

Tracer returns a Tracer scoped to the obs package.

func Transport

func Transport(base http.RoundTripper) http.RoundTripper

Transport wraps base with OTel HTTP client instrumentation: a span per outbound request plus the standard http.client.request.duration and http.client.{request,response}.body.size metrics, labeled by method, status code, and server address (no URL path, so cardinality stays bounded). Pass nil to instrument http.DefaultTransport.

Safe to call before Init — otelhttp uses the global tracer/meter, which are no-op until Init installs the real providers.

Types

type Config

type Config struct {
	// ServiceName is the OTel service.name resource attribute. Required.
	ServiceName string

	// ServiceVersion is the OTel service.version resource attribute.
	// If empty, falls back to debug.ReadBuildInfo() then "devel".
	ServiceVersion string

	// Environment is the OTel deployment.environment resource attribute.
	// If empty, falls back to OTEL_DEPLOYMENT_ENVIRONMENT then "dev".
	Environment string

	// Endpoint is the OTLP/HTTP base endpoint applied to any signal
	// that does not have a signal-specific OTEL_EXPORTER_OTLP_*_ENDPOINT
	// override. If empty and no per-signal env vars are set, obs falls
	// back to the Grafana Cloud gateway when ~/.tok/graf holds a
	// credential, and otherwise leaves OTLP push disabled (the SDK still
	// installs no-op providers).
	Endpoint string

	// Insecure forces http:// when true. If false, the URL scheme of the
	// resolved endpoint decides.
	Insecure bool

	// SampleRatio is the trace head-sampling ratio (0.0–1.0). If zero,
	// falls back to OTEL_TRACES_SAMPLER_ARG, then 0.1.
	SampleRatio float64

	// DisableSlog skips Init's slog.SetDefault. Use this when the
	// caller has its own fan-out chain (e.g. stderr + file + OTLP) and
	// wants to compose [OTLPSlogHandler] in itself.
	DisableSlog bool
}

Config controls obs initialization. The zero value is invalid (ServiceName is required); all other fields fall back to env vars or sensible defaults.

Directories

Path Synopsis
cmd
smoketest command
Smoketest exercises obs end-to-end against a local OTLP receiver.
Smoketest exercises obs end-to-end against a local OTLP receiver.

Jump to

Keyboard shortcuts

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