What this module does not do¶
Most of the questions this module attracts are about something it deliberately does not have. This page is the list, with the reasoning, so "no" is an answer you can find rather than infer from silence.
OTLP/gRPC is not supported¶
Only OTLP/HTTP is built. All three signal packages construct otlptracehttp,
otlpmetrichttp and otlploghttp exporters, and otelcore.ParseEndpoint accepts only
the http and https schemes. A grpc://collector:4317 endpoint is rejected at
startup with ErrInvalidEndpoint, not silently downgraded.
Nor is it a matter of pointing at the gRPC port: https://collector:4317 parses, and
then fails at export because the collector's gRPC receiver does not answer OTLP/HTTP.
The reason is dependency weight. Adding the gRPC exporters means every consumer of this
module links google.golang.org/grpc, whether or not they use it, and the module's
first invariant is that it carries the OpenTelemetry SDK and nothing else. A service
that needs gRPC export can build the exporter itself and use otelcore.Resource and
otelcore.ResolveSettings for the rest.
No exporter other than OTLP¶
There is no Prometheus exporter, no stdout exporter, no Jaeger exporter, and no way to
swap in one of your own — NewProvider builds its exporter internally and returns only
the finished provider. Point OTLP at a collector and let the collector fan out; that is
what collectors are for.
If you want Prometheus pull metrics rather than OTLP push, that is a different
module: go/transport-metrics covers the /metrics endpoint family.
No automatic instrumentation¶
Building a provider produces zero telemetry on its own. This module wires up the export side and stops there — it creates no spans, records no metrics, and installs no middleware.
Getting data in is somebody else's job:
- HTTP and gRPC server and client spans and metrics come from
go.opentelemetry.io/contrib/instrumentation/…(otelhttp,otelgrpc). - Go runtime and process metrics come from
go.opentelemetry.io/contrib/instrumentation/runtime. - Database, cache and business-operation instrumentation is hand-rolled against the
metric.Meteryou get from the provider.
None of those are dependencies here, and none is re-exported.
Nothing is registered globally for you¶
NewProvider returns a provider. It does not call otel.SetTracerProvider,
otel.SetMeterProvider or global.SetLoggerProvider, and it does not install an error
handler or a propagator.
That is deliberate — a library that mutates process-global state on construction cannot be used twice in one process, and cannot be tested in parallel. But it does mean instrumentation libraries reading the global provider see a no-op until you register one yourself.
Trace context propagation is a particular gap to be aware of: without
otel.SetTextMapPropagator, the SDK's default propagator is a no-op, so trace context
never crosses a service boundary and every service starts its own trace.
Settings.Enabled does nothing by itself¶
ResolveSettings computes it, Settings carries it, and no code in this module reads
it. NewProvider builds a working provider — complete with a background export
goroutine — for a signal whose Enabled is false.
Honouring it is the caller's job: check the field and skip the constructor. There is no
"disabled provider" to build instead, and OTEL_SDK_DISABLED is not honoured either.
Sampling and export interval cannot be changed by an operator¶
Two settings are fixed at compile time in practice, because this module always passes the corresponding SDK option and an option always beats an environment variable:
OTEL_TRACES_SAMPLERandOTEL_TRACES_SAMPLER_ARGare overridden bytracing.WithSampling, which defaults to0.1.OTEL_METRIC_EXPORT_INTERVALis overridden bymetrics.WithInterval, which defaults to 60 seconds.
An operator who sets either variable sees no change, and gets no warning. If those need to be operator-tunable in your service, read them into your own configuration and pass them to the options. Full detail in Environment variables.
Only three exporter settings are configurable through Settings¶
Settings carries an endpoint, headers and an insecure flag. The OTLP/HTTP exporters
accept considerably more, and this module exposes none of it:
| Not exposed | Reachable instead through |
|---|---|
| Compression | OTEL_EXPORTER_OTLP_COMPRESSION |
| Request timeout | OTEL_EXPORTER_OTLP_TIMEOUT |
| Custom CA, client certificates for mTLS | OTEL_EXPORTER_OTLP_CERTIFICATE, …_CLIENT_CERTIFICATE, …_CLIENT_KEY |
| Proxy | HTTPS_PROXY / HTTP_PROXY / NO_PROXY |
| Retry policy | nothing — the SDK default is fixed |
| Max request size | nothing — the SDK default is fixed |
A custom *http.Client |
nothing |
So a service needing mTLS to its collector can have it, but only as an environment-level decision, never as a value in its own configuration file.
An https:// endpoint does not guarantee TLS¶
ParseEndpoint validates the scheme, and the environment can then undo it. Both
OTEL_EXPORTER_OTLP_INSECURE=true and a stale OTEL_EXPORTER_OTLP_ENDPOINT=http://…
downgrade a configured https:// endpoint to plaintext, because the SDK applies
environment configuration before this module's options and this module never asserts
"secure".
The environment-variable reference has the full truth table. Treat it as a real constraint when threat-modelling: endpoint validation is a configuration-hygiene measure, not a transport guarantee.
Configured headers replace environment headers, they do not merge¶
A single entry in telemetry.headers discards everything in
OTEL_EXPORTER_OTLP_HEADERS. There is no per-key merge, and no way to ask for one. If
both need to contribute, combine them in your configuration layer before building
Settings.
The resource carries two attributes and no detectors¶
otelcore.Resource(name, version) sets service.name and service.version, and runs
no resource detectors. There is no telemetry.sdk.*, no host.name, no
service.instance.id, no container or cloud detection. OTEL_RESOURCE_ATTRIBUTES is
merged underneath, so an operator can add what a backend needs — but nothing is
discovered automatically, and OTEL_SERVICE_NAME is always beaten by the name you pass
in code.
The slog bridge replaces your output and filters nothing¶
logs.Handler returns a handler that writes only to OTLP. A logger built from it
alone stops writing to stderr, and this module ships no fan-out handler to keep both.
It also applies no severity floor. slog's familiar "info and above" default belongs to
the text and JSON handlers, not to the OTel bridge — logger.Debug(…) is exported.
And correlation is not automatic in the way the phrase suggests: a record only carries
trace_id and span_id if the call passed a context containing an active span, which
means the …Context variants. logger.Info(…) inside a span produces an uncorrelated
record. See Correlate logs with
traces.
No lifecycle management¶
Nothing here registers a shutdown hook, traps a signal, or ties a provider to a
lifecycle manager. Every provider's Shutdown is the caller's to call, and skipping it
drops whatever is buffered. Wiring that into a service's startup and shutdown ordering is
the consuming application's job.
No RED/USE instrumentation helpers — yet¶
There are no ready-made helpers for databases, caches, circuit breakers or queues, and no
generic business-operation wrapper. A design exists for them, but nothing is
implemented, and the metrics package hands you a metric.Meter and stops. Anything
domain-shaped is hand-rolled today.
Deliberately not a configuration library¶
The module reads no files, no flags and no environment of its own. It publishes the
telemetry.* key names and a merge function, and the
caller supplies the loader. That is the point of the extraction rather than an omission
— see Endpoint resolution and the config
model.