Configuration keys¶
What this module reads, and what it does not¶
This module reads no configuration and no environment of its own. It exports the
key names and the merge rule; loading YAML, flags or environment into those structs
is the caller's job. Nothing in otelcore, tracing, metrics or logs calls
os.Getenv — the only environment reading happens inside the OpenTelemetry SDK, and
that is covered in Environment variables.
That means a wrong key name is your loader's error, not this module's. The names below exist so every service in the toolkit spells them the same way.
The telemetry.* key tree¶
The prefix and the three signal names are exported constants, so a loader can build the keys without string literals:
otelcore.Root // "telemetry"
otelcore.SignalTracing // "tracing"
otelcore.SignalMetrics // "metrics"
otelcore.SignalLogs // "logs"
Shared keys sit directly under telemetry; each signal takes the same keys again under
telemetry.<signal>, plus enabled:
telemetry:
endpoint: https://collector.internal:4318
headers:
authorization: Bearer <token>
insecure: false
tracing:
enabled: true
endpoint: https://tail-sampler.internal:4318
metrics:
enabled: true
logs:
enabled: false
Config, SignalConfig and Settings carry mapstructure, yaml and json tags on
every field, so the same struct decodes from any of the three.
Shared keys: telemetry.*¶
These populate otelcore.Config.
| Key | Type | Go field | Default | If it is wrong |
|---|---|---|---|---|
telemetry.endpoint |
string | Config.Endpoint |
"" |
An empty value is valid and means "fall back to OTEL_EXPORTER_OTLP_*". A non-empty value that is not an http/https URL with a host fails at NewProvider, not at export — see Errors. |
telemetry.headers |
map[string]string | Config.Headers |
nil |
Sent verbatim as exporter HTTP headers. An empty map is indistinguishable from unset after resolution (see Empty maps). A non-empty map replaces OTEL_EXPORTER_OTLP_HEADERS entirely rather than merging with it. |
telemetry.insecure |
bool | Config.Insecure |
false |
true forces plaintext OTLP even against an https:// endpoint. false does not force TLS — OTEL_EXPORTER_OTLP_INSECURE=true can still downgrade the connection. |
There is no shared telemetry.enabled. Enabling is per-signal only, deliberately: see
ResolveSettings never inherits Enabled.
Per-signal keys: telemetry.<signal>.*¶
These populate otelcore.SignalConfig,
where <signal> is tracing, metrics or logs.
| Key | Type | Go field | Default | If it is wrong |
|---|---|---|---|---|
telemetry.<signal>.enabled |
bool | SignalConfig.Enabled |
false |
Nothing in this module reads it. NewProvider builds a provider whether or not Enabled is true; honouring it is the caller's job. See Enabled is yours to honour. |
telemetry.<signal>.endpoint |
string | SignalConfig.Endpoint |
inherits telemetry.endpoint |
Only overrides the shared value when the matching SignalOverrides.Endpoint bit is set. Same validation as the shared key. |
telemetry.<signal>.headers |
map[string]string | SignalConfig.Headers |
inherits telemetry.headers |
Only overrides when SignalOverrides.Headers is set, and then it replaces the shared map rather than merging into it. |
telemetry.<signal>.insecure |
bool | SignalConfig.Insecure |
inherits telemetry.insecure |
Only overrides when SignalOverrides.Insecure is set. |
Which fields inherit, and which do not¶
ResolveSettings(shared, signal, overrides)
produces the Settings
a provider factory consumes:
Settings field |
Comes from |
|---|---|
Enabled |
signal.Enabled — always, never inherited from shared config |
Endpoint |
signal.Endpoint when overrides.Endpoint is true, otherwise shared.Endpoint |
Headers |
signal.Headers when overrides.Headers is true, otherwise shared.Headers (copied, not aliased) |
Insecure |
signal.Insecure when overrides.Insecure is true, otherwise shared.Insecure |
SignalOverrides has no Enabled bit, because Enabled has nothing to inherit from.
What SignalOverrides is for¶
A zero SignalConfig.Endpoint is ambiguous: it could mean the operator set it to
empty or the operator did not set it at all. Go's zero values cannot tell those
apart, so ResolveSettings takes an explicit mask:
overrides := otelcore.SignalOverrides{
Endpoint: cfg.IsSet("telemetry.tracing.endpoint"),
Headers: cfg.IsSet("telemetry.tracing.headers"),
Insecure: cfg.IsSet("telemetry.tracing.insecure"),
}
You define what "set" means — a key present in the file, a changed flag, a non-empty
environment variable. A field with its bit false can never clobber the shared value
with a zero.
Getting the mask wrong is silent, not loud. Leaving Endpoint: false while supplying a
per-signal endpoint means the signal quietly exports to the shared collector.
Why an empty headers map behaves as unset¶
ResolveSettings copies header maps through cloneHeaders, which returns nil for a
map of length zero. After resolution, headers: {} and an absent headers key are the
same value. This matters in one place: it means you cannot use an empty per-signal map
to clear inherited shared headers. Setting SignalOverrides.Headers: true with an
empty SignalConfig.Headers gives the signal nil headers, which is the clearing
behaviour you want — but only because the override bit, not the map, does the work.
The copy is also why mutating the map you passed in afterwards does not change a
resolved Settings.
Enabled is yours to honour¶
Settings.Enabled is resolved and carried, and then no code in this module looks at it.
tracing.NewProvider, metrics.NewProvider and logs.NewProvider each build an
exporter and a provider regardless of its value.
Skip the call yourself:
Building a provider for a disabled signal is not harmless. The metric provider starts a background export goroutine on a 60-second ticker, and the trace and log providers start batch processors, all of which will try to reach a collector.
Resource attributes are not configuration keys¶
otelcore.Resource(name, version)
takes its two values as arguments, not from the telemetry.* tree. It sets exactly
service.name and service.version, against OpenTelemetry semantic-convention schema
https://opentelemetry.io/schemas/1.26.0.
Anything else you want on the resource — deployment.environment,
service.instance.id, host or process attributes — has to arrive through
OTEL_RESOURCE_ATTRIBUTES, which the SDK merges underneath these two. See
Environment variables.