NewConsole bootstraps the OTel SDK with three logz-backed exporters: - Trace: WithSyncer, one log line per closed span (immediate, no batch) - Metric: PeriodicReader (60s), flushed on shutdown - OTel log: BatchProcessor, for third-party libs using OTel log API ConsoleConfig requires only ServiceName — no OTLP endpoint needed. Adds logz v1.0.1 as direct dependency; module tier bumped 1 → 2.
5.1 KiB
5.1 KiB
telemetry
Bootstraps the full OpenTelemetry SDK (traces, metrics, logs) with OTLP gRPC exporters targeting Grafana Alloy.
Purpose
Sets the three OTel global providers so that all micro-libs using the OTel global API auto-instrument without any code changes. Returns a shutdown function that flushes all exporters on process exit. This module is the single place in an application where the OTel SDK is wired up.
Tier & Dependencies
Tier 2 — depends on Tier 1 logz (required by NewConsole). Must never be imported
by framework libraries.
Depends on:
code.nochebuena.dev/go/logz(Tier 1) — used byNewConsoleexportersgo.opentelemetry.io/oteland sub-packages — API and SDKgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpcgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpcgo.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpcgo.opentelemetry.io/otel/sdk/trace,.../metric,.../log
No launcher dependency — telemetry has no Component lifecycle.
Key Design Decisions
- Tier 2 / app-only (ADR-001): Libraries use only the OTel API (no-op default). This module activates the real SDK. Importing it from a library is a mistake.
- Three-signal OTLP bootstrap (ADR-002):
New(ctx, cfg)sets up traces → Tempo, metrics → Mimir, logs → Loki, all over a single OTLP gRPC endpoint. W3C TraceContext + Baggage propagation is set globally. - Global provider strategy (ADR-003): Libraries call
otel.Tracer(...)/otel.Meter(...)/global.Logger(...). Aftertelemetry.New, those calls route to the real SDK with no library changes required. - No
launcher.Component: Telemetry is not a lifecycle component. The caller defers the returned shutdown function directly inmain. This keeps the module dependency graph minimal and the interface simple. - Sequential error rollback: If any exporter fails to initialize, all previously created providers are shut down before the error is returned. The process never runs with a partial telemetry state.
Patterns
Standard application usage:
func main() {
ctx := context.Background()
shutdown, err := telemetry.New(ctx, telemetry.Config{
ServiceName: "order-service",
ServiceVersion: "1.4.2",
Environment: "production",
OTLPEndpoint: "alloy:4317",
OTLPInsecure: false,
})
if err != nil {
log.Fatalf("telemetry: %v", err)
}
defer shutdown(ctx)
// Rest of application wiring...
}
Local development — console mode (no collector required):
func main() {
ctx := context.Background()
logger := logz.New(logz.Options{})
shutdown, err := telemetry.NewConsole(ctx, logger, telemetry.ConsoleConfig{
ServiceName: "order-service",
})
if err != nil {
log.Fatalf("telemetry: %v", err)
}
defer shutdown(ctx)
// Traces, metrics, and OTel log records appear as logz log lines.
}
With launcher (wire shutdown into lifecycle):
shutdown, err := telemetry.New(ctx, cfg)
if err != nil {
return err
}
lc.BeforeStop(func() error { return shutdown(ctx) })
Config env vars:
| Variable | Required | Default | Description |
|---|---|---|---|
OTEL_SERVICE_NAME |
yes | — | Service name in all signals |
OTEL_SERVICE_VERSION |
no | unknown |
Deployed version |
OTEL_ENVIRONMENT |
no | development |
Deployment environment |
OTEL_EXPORTER_OTLP_ENDPOINT |
yes | — | OTLP gRPC collector address (e.g. alloy:4317) |
OTEL_EXPORTER_OTLP_INSECURE |
no | false |
Disable TLS (set true for local dev) |
What to Avoid
- Do not import this module from any non-
mainpackage. Libraries must use only OTel API packages. - Do not call
telemetry.Newortelemetry.NewConsolemore than once per process. Each call overwrites the global providers. - Do not omit the
defer shutdown(ctx). Without it, buffered spans and metrics are lost on exit. - Do not use a zero-value
Config. BothServiceNameandOTLPEndpointare required;Newwill return an error if the OTLP connection cannot be established. - Do not wrap this in a
launcher.Component. The shutdown function pattern is simpler and avoids adding alauncherdependency to this module. - Do not wire the OTel slog bridge alongside
NewConsole. The bridge routes logz/slog output through the OTel log API, whichlogLogExporterforwards back to logz — creating a feedback loop.
Testing Notes
telemetry_test.go— uses afakeCollectorTCP listener to avoid connection-refused; testsNewand the global provider assignments. Shutdown timeout 200ms is intentional since the fake server cannot complete a gRPC flush.console_test.go— uses astubLoggerthat captures log calls.TestNewConsole_ExportsSpanusesWithSyncerbehavior: span export is synchronous, so the log is recorded immediately onspan.End().TestNewConsole_ExportsMetrictriggers export via shutdown flush (PeriodicReader flushes on Shutdown).newResourceis tested separately as a pure function with no I/O.- Do not test against a real Alloy or Tempo instance in unit tests.