slog

package
v0.21.0 Latest Latest
Warning

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

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

Documentation

Overview

Package slog sinks all three OpenTelemetry signals — Traces, Metrics, and Logs — into a log/slog logger, so every span, metric, and log line from any Scope module lands in one structured stream correlated by trace_id / span_id.

Three exporters, one per signal:

  • SpanExporter (sdktrace.SpanExporter) — install via WithSyncer / WithBatcher
  • MetricExporter (sdkmetric.Exporter) — install via a PeriodicReader
  • LogExporter (sdklog.Exporter) — install via a LoggerProvider processor

This is for local development and debugging, where forwarding to a full backend (Jaeger, Tempo, Datadog, ...) would be overkill. Routing logs through OTel (rather than writing slog directly) is deliberate: it makes logs as backend-swappable as traces/metrics — a production build swaps each exporter to OTLP with zero business-code change.

Usage

import (
    stdslog "log/slog"
    "go.opentelemetry.io/otel"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "github.com/Tangerg/scope/otel/slog"
)

tp := sdktrace.NewTracerProvider(
    sdktrace.WithSyncer(slog.NewSpanExporter(stdslog.Default())),
)
otel.SetTracerProvider(tp)
defer tp.Shutdown(context.Background())

The log path goes through the contrib otelslog bridge to a LoggerProvider configured with LogExporter.

Note: this package is named `slog` to match the otel/<backend> convention. Callers that also import the standard library's `log/slog` must alias one of them (commonly `stdslog "log/slog"`) to avoid the name collision.

For production use, prefer OTLP exporters to a real backend; these exporters are intended for local visibility, not long-term storage.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type LogExporter

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

LogExporter writes OpenTelemetry log records to a log/slog logger — the Logs leg of the dev observability triad, a sibling of SpanExporter and MetricExporter so all three OTel signals share one slog stream keyed by trace_id.

Application code keeps calling slog (via the contrib otelslog bridge that feeds a LoggerProvider); installing this exporter on that provider is what makes logs as backend-swappable as traces/metrics — a production build swaps it for an OTLP log exporter (→ Datadog / Cloud Logging / ...) with zero business-code change. That swappability is the whole reason logs go through OTel rather than straight to slog.

Install it on a LoggerProvider via a processor:

lp := sdklog.NewLoggerProvider(
    sdklog.WithProcessor(sdklog.NewSimpleProcessor(slog.NewLogExporter(logger))))

Each OTel log record becomes one slog record: the record body is the message, the severity maps to the slog level, and trace_id / span_id come from the record's own trace context (the SDK fills them from the emitting span — native correlation, no manual stamping).

func NewLogExporter

func NewLogExporter(logger *stdslog.Logger) *LogExporter

NewLogExporter writes records through log/slog so telemetry is readable in development without running a collector. The exporter adds no delivery guarantees of its own and is intended for local diagnostics.

func (*LogExporter) Export

func (l *LogExporter) Export(ctx context.Context, records []sdklog.Record) error

Export writes one slog record per OTel log record. It returns only context cancellation/deadline errors and sdklog.ErrExporterShutdown when closed.

func (*LogExporter) ForceFlush

func (l *LogExporter) ForceFlush(ctx context.Context) error

func (*LogExporter) Shutdown

func (l *LogExporter) Shutdown(ctx context.Context) error

type MetricExporter

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

MetricExporter writes OpenTelemetry metric data to a log/slog logger — the Metrics leg of the dev observability triad, a sibling of SpanExporter so all three signals share one slog stream.

Install it on a MeterProvider via a PeriodicReader:

reader := sdkmetric.NewPeriodicReader(slog.NewMetricExporter(logger))
mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))
otel.SetMeterProvider(mp)

Each metric becomes one slog record carrying the instrument name, unit, scope, and a compact rendering of its data points. Like SpanExporter this is for local visibility; production should use an OTLP metric exporter.

func NewMetricExporter

func NewMetricExporter(logger *stdslog.Logger) *MetricExporter

NewMetricExporter writes metrics through log/slog for the same reason as NewLogExporter — inspecting instrumentation locally without a collector — and carries the same caveat about production volume.

func (*MetricExporter) Aggregation

func (*MetricExporter) Export

Export writes one slog record per metric. It reports cancellation, nil input, and the SDK's shutdown state; slog handler failures are not exposed by log/slog and therefore cannot become collection errors.

func (*MetricExporter) ForceFlush

func (m *MetricExporter) ForceFlush(ctx context.Context) error

func (*MetricExporter) Shutdown

func (m *MetricExporter) Shutdown(ctx context.Context) error

func (*MetricExporter) Temporality

Temporality / Aggregation defer to the SDK defaults — this is a passive dev sink with no opinion on accumulation semantics.

type SpanExporter

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

SpanExporter writes finished OpenTelemetry spans to a log/slog logger — the Traces leg of the dev observability triad, a sibling of MetricExporter and LogExporter so all three OTel signals share one slog stream keyed by trace_id.

It implements sdktrace.SpanExporter and is intended to be installed on a TracerProvider via sdktrace.WithSyncer (for dev/debug, synchronous output) or sdktrace.WithBatcher (for production-ish batched output).

Each span becomes a single slog record. The record message is "span" for OK/Unset status and "span (error): <description>" for Error status, with the log level promoted to Error accordingly.

The following attributes are always included:

  • trace_id (span.SpanContext().TraceID())
  • span_id (span.SpanContext().SpanID())
  • name (span.Name())
  • duration (EndTime - StartTime)
  • parent_span_id (only if the span has a parent)

All span attributes and event names are attached as additional slog attributes, preserving their OTel key names (e.g. "gen_ai.provider.name").

func NewSpanExporter

func NewSpanExporter(logger *stdslog.Logger) *SpanExporter

NewSpanExporter writes spans through log/slog for the same reason as NewLogExporter. Each finished span becomes an independent record, so trace structure remains encoded in its identifiers rather than rendered as a tree.

func (*SpanExporter) ExportSpans

func (s *SpanExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error

ExportSpans writes each provided span as a single slog record. It returns context cancellation/deadline errors; slog handler failures are not exposed by log/slog. After Shutdown it performs no work.

func (*SpanExporter) Shutdown

func (s *SpanExporter) Shutdown(ctx context.Context) error

Jump to

Keyboard shortcuts

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