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 usualLevelInfodefault belongs to the text and JSON handlers, not to this bridge — alogger.Debug(...)call is exported. Filter withslog.HandlerOptionson a handler you control, or by not calling. - It takes the concrete
*sdklog.LoggerProvider, not thelog.LoggerProviderinterface, 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)