Skip to content

Provider options and defaults

Each signal package exposes one constructor with the same shape. Options are functional and optional; every default below applies when you pass none.

tracing.NewProvider

func NewProvider(
    ctx context.Context,
    res *resource.Resource,
    s otelcore.Settings,
    opts ...Option,
) (*sdktrace.TracerProvider, error)

Returns a *sdktrace.TracerProvider with a batch span processor over the OTLP/HTTP trace exporter, the supplied resource, and a parent-based ratio sampler.

Option Default Range Effect
tracing.WithSampling(ratio float64) tracing.DefaultSampling = 0.1 clamped, see below Head sampling ratio, wrapped in ParentBased

What WithSampling does at the edges

The ratio is passed to sdktrace.TraceIDRatioBased, which does not reject an out-of-range value — it clamps:

Value Behaviour
>= 1.0 every root trace is recorded and sampled
<= 0.0 (including negative) every root trace is dropped
between that fraction of trace IDs is sampled

No error is returned for a nonsense ratio, so a typo like WithSampling(10) silently means "sample everything" and WithSampling(-1) silently means "sample nothing".

Why the sampler is parent-based

The ratio sampler is wrapped in sdktrace.ParentBased, so the decision is only made for a root span. A span that arrives with a sampled parent is always recorded, and one with an unsampled parent is always dropped. A trace therefore stays whole across service boundaries instead of being decided again at each hop — which is what makes 10% mean "10% of complete traces" rather than "10% of every span independently".

One consequence catches people out: for a service that only ever receives requests carrying trace context, its own WithSampling ratio changes nothing. The decision was made upstream.

OTEL_TRACES_SAMPLER has no effect

NewProvider always passes sdktrace.WithSampler(...), and that option overrides OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG. Setting them changes nothing; use WithSampling instead. See Environment variables.

metrics.NewProvider

func NewProvider(
    ctx context.Context,
    res *resource.Resource,
    s otelcore.Settings,
    opts ...Option,
) (*sdkmetric.MeterProvider, error)

Returns a *sdkmetric.MeterProvider with a PeriodicReader pushing to the OTLP/HTTP metric exporter.

Option Default Effect
metrics.WithInterval(d time.Duration) metrics.DefaultInterval = 60s Interval between periodic exports

A duration of zero or less is ignored by the SDK's WithInterval, which then leaves the interval at whatever it already held — so WithInterval(0) is a no-op, not an error and not "export continuously".

There is no option for the per-export timeout. It stays at the SDK's default of 30 seconds, and unlike the interval it can be changed with OTEL_METRIC_EXPORT_TIMEOUT.

OTEL_METRIC_EXPORT_INTERVAL has no effect

NewProvider always passes sdkmetric.WithInterval(...) — with DefaultInterval when you supply no option — and that overrides OTEL_METRIC_EXPORT_INTERVAL. An operator setting the variable to one second still gets exports every sixty.

logs.NewProvider and logs.Handler

func NewProvider(
    ctx context.Context,
    res *resource.Resource,
    s otelcore.Settings,
) (*sdklog.LoggerProvider, error)

func Handler(lp *sdklog.LoggerProvider, name string) slog.Handler

NewProvider takes no options — there is no variadic parameter to add one to, so gaining one later is a signature change. It returns a provider with a batch processor over the OTLP/HTTP log exporter.

Handler wraps the provider in otelslog.NewHandler(name, ...), where name becomes the instrumentation scope on every record. Three things about it are worth knowing:

  • It replaces your output, it does not add to it. slog.New(logs.Handler(lp, …)) produces a logger that writes only to OTLP. Nothing reaches stderr. To keep local output you need your own fan-out handler; this module does not ship one.
  • It applies no severity floor. slog's usual LevelInfo default belongs to the text and JSON handlers, not to this bridge — a logger.Debug(...) call is exported. Filter with slog.HandlerOptions on a handler you control, or by not calling.
  • It takes the concrete *sdklog.LoggerProvider, not the log.LoggerProvider interface, so a no-op provider cannot be substituted in tests.

Trace correlation depends on how you call the logger, not on how you build it — see Correlate logs with traces.

Defaults that come from the OpenTelemetry SDK

These are not this module's values; they are what the SDK uses because this module passes no option for them. They move when the pinned OTel version moves. All were read from otel/sdk and the OTLP/HTTP exporters at v1.44.0 (sdk/log v0.20.0).

Span batching

Setting Default Tunable with
Max queue size 2048 spans OTEL_BSP_MAX_QUEUE_SIZE
Schedule delay 5s OTEL_BSP_SCHEDULE_DELAY
Export timeout 30s OTEL_BSP_EXPORT_TIMEOUT
Max export batch size 512 spans OTEL_BSP_MAX_EXPORT_BATCH_SIZE

Log-record batching

Setting Default Tunable with
Max queue size 2048 records OTEL_BLRP_MAX_QUEUE_SIZE
Export interval 1s OTEL_BLRP_SCHEDULE_DELAY
Export timeout 30s OTEL_BLRP_EXPORT_TIMEOUT
Max export batch size 512 records OTEL_BLRP_MAX_EXPORT_BATCH_SIZE

OTLP/HTTP transport, all three signals

Setting Default Tunable with
Collector host and port when nothing is configured localhost:4318 OTEL_EXPORTER_OTLP_ENDPOINT
Request timeout 10s OTEL_EXPORTER_OTLP_TIMEOUT
Compression none OTEL_EXPORTER_OTLP_COMPRESSION
HTTP proxy http.ProxyFromEnvironment HTTPS_PROXY, HTTP_PROXY, NO_PROXY
Max serialised request size before compression 64 MiB nothing — the SDK's WithMaxRequestSize is not exposed here
Retry enabled; 5s initial, 30s max interval, 60s max elapsed nothing — the SDK's WithRetry is not exposed here

The retry budget is worth reading twice: a batch that cannot be delivered within 60 seconds of retrying is discarded, silently as far as your service is concerned.

Who owns Shutdown

Every factory returns a provider whose Shutdown the caller owns. None of them registers a shutdown hook, a signal handler or a finaliser.

Skipping it loses whatever is still buffered: up to five seconds of spans, one second of log records, and up to a full interval of metrics. Call it — with a timeout, because Shutdown will sit through the retry budget above if the collector is unreachable:

shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

_ = tp.Shutdown(shutdownCtx)
_ = mp.Shutdown(shutdownCtx)
_ = lp.Shutdown(shutdownCtx)