Clarify that a disabled Tracer/Meter/Logger may still generate SDK internal telemetry - #5253
Conversation
f710950 to
a6d9f98
Compare
Pull request dashboard statusWaiting on the author · refreshed 2026-08-24 21:33 UTC Resolve merge conflicts. Respond to 4 review items (e.g. link a commit, explain why not, ask a follow-up): Status above doesn't look right?
|
jack-berg
left a comment
There was a problem hiding this comment.
On first glance this seems strange, but reviewers consider current behavior of otel.sdk.log.created without this:
In OpenTelemetry SDKs a Logger is enabled by default, and can be disabled via configuration i.e. LoggerConfig.enabled = false when supported; a disabled Logger is a No-op: emitting to it has no effect, so its records are not counted. Every log record submitted to an enabled Logger is counted, even if it is later filtered or dropped within the SDK (e.g. by minimum severity or trace-based rules, or by a processor or the export pipeline), making this metric the top of the log delivery funnel. Records not submitted to the SDK are not counted (e.g. a caller that skips calling Emit() based on an Enabled() check, or an upstream logging library that filters first).
Oddities:
- The counter is not counted if
LoggerConfig.enabled=false, but is counted if the log was filtered due to failing to meet minimum severity or due to trace-based rules. - A log that is never submitted to
Emit()due toEnabled()returning false is not counted, despite that same log record being counted if submitted and failing to meet minimum severity or due to trace-based rules.
I noted two potential solutions to this:
To me, it seems like the semantics should be one of:
- If Logger.enabled() returns false for any reason, do not increment otel.sdk.log.created.
- OR increment otel.sdk.log.created always, regardless of Logger.enabled(). (This would require changing / re-interpretting the "equivalently to a noop" clause)
This PR facilitates the second option by carving out an exception to the "MUST behave equivalently to a No-op" for internal telemetry.
I'll also note that otel.sdk.span.started is missing details about its interaction with TracerConfig.enabled.
…lemetry Signed-off-by: cijothomas <cijo.thomas@gmail.com>
a6d9f98 to
e8f3a1f
Compare
|
|
||
| If a `Logger` is disabled, it MUST behave equivalently | ||
| to [No-op Logger](./noop.md#logger). | ||
| to [No-op Logger](./noop.md#logger). Even so, the SDK MAY still emit its own |
There was a problem hiding this comment.
Changed emit->generate, to avoid confusion with "Emit LogRecord". Is that what you meant?
There was a problem hiding this comment.
I was trying to understand where you are heading:
For example:
- A library uses OpenTelemetry API package, an application using that library is not using/initializing the OpenTelemetry SDK. In this case, the API package might still handle things such as context propagation within/across process boundary. Do you want there to be some internal telemetry or not?
- If an application has been using the OpenTelemetry SDK and sending telemetry via OTLP exporter, now the dev decided to upgrade it to the latest version, do you expect the dev to see the internal telemetry from the OTLP exhaust, or they need to explicitly ask for / enable it?
b49fbf7 to
de51ee3
Compare
The thing that feels strange to me about this is mostly that we already miss any telemetry that is recorded with the enabled pattern: If mylogger.Enabled(ctx) {
mylogger.Emit(ctx, log.NewRecord("foo"))
}vs mylogger.Emit(ctx, log.NewRecord("foo"))Did you have a use-case in mind for this? I had initially thought it might be useful for estimating telemetry that you would get if you chose to enable the signal, but missing calls gated by Enabled makes this less useful for that. |
I think we'd need another metric to cover number of enabled calls to show the funnel. But it's tricky - |
|
This breaks my assumptions about No-ops. I feel opposed to counting anything when a Tracer/Meter/Logger/Profiler is disabled. The SDK instrument shouldn't be registered, I would say. |
|
Hi @cijothomas — just a friendly reminder that this pull request is waiting on you. The dashboard status comment has the open items and is kept current.
|
What
Clarifies that a disabled
Tracer,Meter, orLogger— which MUST behave as the corresponding No-op — MAY still generate the SDK's own internal (self-observability) telemetry about that disabled signal.Why
The SDK self-observability metrics (e.g.
otel.sdk.log.created, and the span/processor metrics) are intended to be a complete funnel: the top-of-funnel intake count must remain a true superset of everything downstream, so that any drop is visible as a gap. That requires the SDK to be able to record its own internal telemetry even when the application-facing signal is disabled — for example, counting a log record submitted to a disabledLogger, or a span created by a disabledTracer.Today the spec says a disabled
Tracer/Meter/LoggerMUST behave equivalently to a No-op. Read literally, "No-op" could be taken to forbid the SDK from generating any telemetry, including its own internal metrics, which would make these signals blind to records dropped at a disabled signal. This came up in open-telemetry/opentelemetry-java#8697 (discussion: open-telemetry/opentelemetry-java#8697 (comment)).What this changes
Adds one sentence to each of the Tracer / Meter / Logger config sections stating that, even when disabled, the SDK MAY still generate its own internal telemetry related to that disabled signal. This is a non-normative clarification (
MAY); it does not require any SDK to generate such telemetry, and it does not change the No-op behavior observed by the application.This unblocks defining the self-observability metrics (starting with
otel.sdk.log.created) to count at intake, independent of a signal's enabled/disabled state.Note on cost
A disabled signal is expected to behave as a No-op, which callers reasonably assume is at (or very near) zero cost. If an SDK's internal telemetry generation on the disabled path is not effectively free, that cost surfaces on the No-op path and erodes that assumption. Implementations that choose to generate internal telemetry for a disabled signal should therefore keep its cost as close to zero as possible (for example, a cheap counter increment, and no work at all when self-observability is turned off). The
MAYwording deliberately leaves room for an implementation to generate nothing on this path.