Skip to content

fix: apply filtering EmitLogRecord#4079

Open
proost wants to merge 25 commits into
open-telemetry:mainfrom
proost:fix-apply-filtering-emitlogrecord
Open

fix: apply filtering EmitLogRecord#4079
proost wants to merge 25 commits into
open-telemetry:mainfrom
proost:fix-apply-filtering-emitlogrecord

Conversation

@proost
Copy link
Copy Markdown
Contributor

@proost proost commented May 12, 2026

Fixes #2667

Changes

prev: #4011

fix: Note that EmitLogRecord() helpers never honor the Enabled() flag either.

A huge gap exists between API/SDK spec and C++ client for EmitLogRecord method.

  1. missing context parameter
  2. missing exception parameter
  3. support severity filtering in the "SDK".
  4. We currently handle ObservedTimestamp and Timestamp using same type. Spec tells both optional parameter how to divide which one is ObservedTimestamp and other one is Timestamp?

1 is easy, although ABI level breaking change is inevitable.
2 is quite confusing to me. Is just another attributes or std::exception? When i read data-model spec, It feels like one of attributes.
3 needs decision. Because In SDK level interface, we use "LogRecord" type. But we can't extract severity from "LogRecord" easily. Or do we have to overload "EmitLogRecord" with severity?
4 needs a design.

So i change very narrower code. Filtering in the severity filtering in the API level and trace based filtering in the sdk level.

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

@proost proost requested a review from a team as a code owner May 12, 2026 11:57
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 90.41096% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.07%. Comparing base (a6c8ec8) to head (d719d99).

Files with missing lines Patch % Lines
sdk/src/logs/logger.cc 86.28% 7 Missing ⚠️
api/include/opentelemetry/logs/logger.h 88.38% 5 Missing ⚠️
...pi/include/opentelemetry/logs/logger_type_traits.h 95.24% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4079      +/-   ##
==========================================
+ Coverage   81.99%   82.07%   +0.08%     
==========================================
  Files         385      386       +1     
  Lines       16025    16134     +109     
==========================================
+ Hits        13138    13240     +102     
- Misses       2887     2894       +7     
Files with missing lines Coverage Δ
...pentelemetry/sdk/logs/batch_log_record_processor.h 100.00% <100.00%> (ø)
sdk/include/opentelemetry/sdk/logs/processor.h 100.00% <100.00%> (ø)
...entelemetry/sdk/logs/simple_log_record_processor.h 100.00% <100.00%> (ø)
sdk/src/logs/multi_log_record_processor.cc 93.68% <100.00%> (+0.43%) ⬆️
...pi/include/opentelemetry/logs/logger_type_traits.h 97.62% <95.24%> (-2.38%) ⬇️
api/include/opentelemetry/logs/logger.h 79.60% <88.38%> (+10.74%) ⬆️
sdk/src/logs/logger.cc 89.72% <86.28%> (+0.31%) ⬆️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@proost proost changed the title Fix apply filtering emitlogrecord fix: apply filtering EmitLogRecord May 12, 2026
Copy link
Copy Markdown
Member

@dbarker dbarker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. We definitely need some discussion here (thanks for starting it) on how to align with the spec. As you noted there are a few gaps in how the full Context is handled and Enabled is checked when creating and emitting a log record. Generally it seems there should be a path to explicitly provide the full Context and avoid any implicit calls to get the current context from RuntimeContext.

Comment thread api/include/opentelemetry/logs/logger.h
Comment thread sdk/src/logs/logger.cc Outdated
Copy link
Copy Markdown
Member

@dbarker dbarker May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check above to logger_config_.IsEnabled() is not really complete and may be the wrong place to check if the logger is Enabled. The spec defines multiple conditions that can disable the logger.

https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/sdk.md#enabled

Enabled MUST return false when either:

The current implementation only checks for the LoggerConfig.enabled flag (skipping the other conditions).

Also creating a log record now (when enabled) will always implicitly get the current context, even if the user has explicitly provided the context (trace id, span id, trace flags) on emit. If a user is able to and provides a full context on emit then getting the current context should be avoided.

This may mean we need to update the Logger API EmitLogRecord API to accept a full context and only get the current context in the emit method if one is not provided.

The Enabled check may then need to live in the EmitLogRecord method and guard creating a log record. If a user decides to call CreateLogRecord directly then perhaps it is their responsibility to call the Enabled method if needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, here is what i understand. We align spec through API level using current "Enabled".
47d1e37

Is this correct?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Looks reasonable. There still seems to be a gap in handling user provided Context in parts (as SpanContext or TraceID + SpanID + TraceFlags):

  1. User calls EmitLogRecord(args...) with SpanContext -> Enabled called with Current Runtime Context instead of a Context with the provided SpanContext?
  2. User calls EmitLogRecord(args...) with TraceID, SpanID, and TraceFlags -> Enabled is checked against Current Runtime Context instead of a Context from the provided trace parts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d4578ad

We should cover this too. Thank you.

Copy link
Copy Markdown
Member

@dbarker dbarker May 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. This change appears to result in the right behavior. If a user provides trace args independently as SpanContext or TraceID+SpanID+TraceFlags then those args are used to determine if the record should be created and to set the record's traceid, spanid, and traceflags.

The downside for current users is the extra dynamic memory allocations to reconstruct a full Context from the args (allocating a DefaultSpan just to later get the trace data from it). We should avoid this cost for those users if possible.

One option: Instead of constructing a full Context object when a user provides SpanContext or TraceID+SpanID+TraceFlags we pass just a SpanContext (construct if needed) through to the sdk methods. We could do this with a nostd::variant<SpanContext, context::Context> similar to StartSpanOptions or define new struct. That would require updating the ABIv2 interface for CreateLogRecord, EnabledImplementation, and IsAllowedByTraceBasedFiltering to take this object referencing either a full Context or SpanContext.

What do you think? @lalitb any thoughts on this approach?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree in principle. The behavior looks correct after the latest update, but the extra allocation for DefaultSpan is unfortunate, especially because we only need the SpanContext for trace_based filtering and stamping the record.

A lightweight wrapper/variant for “full Context vs explicit SpanContext” seems reasonable to me. Since this is ABIv2-only surface, I think it is better to get that shape right now. The important part is preserving the current precedence: explicit Context first, explicit trace args next, and RuntimeContext only when nothing explicit was provided.

Comment thread sdk/src/logs/logger.cc Outdated
@dbarker dbarker added the discuss To discuss in SIG meeting label May 12, 2026
@proost proost requested a review from dbarker May 14, 2026 08:24
Copy link
Copy Markdown
Member

@owent owent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

Comment thread api/include/opentelemetry/logs/logger.h Outdated
{
const EventId *event_id_ptr = detail::FindEventIdInArgs(args...);
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
const bool is_enabled =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid making every enabled log call pay for the full Enabled(...) chain? The disabled and below-min-severity paths stay cheap, but the normal enabled path now pays virtual/context/processor cost before record creation. It may be better to cache whether full filtering is actually needed, and only call the full chain when trace-based filtering or processor-level filtering is configured.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx, 5e67680

how about go through trace based filtering using current option?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this helps with the trace-based case, but I think the original concern is only partially addressed. We still need the cache to account for processor-level filtering too, otherwise the normal non-trace-based path can skip processor Enabled(...) checks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, now i understand your intention.
dc306d5

Later, i will add logger configuration.. this option might be needed in the configuration. But this is not in the spec. is ok?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not add a new logger configuration option just for this. The spec defines the observable Enabled behavior, but the C++ SDK can still implement it efficiently.

For performance, I think this should stay as an internal cache/capability decision: full filtering is needed when trace_based is enabled or when a processor actually implements Enabled filtering. Otherwise the normal enabled path should avoid paying the full virtual/context/processor chain.

One thing to watch is that the cache must stay correct if processors are added after a logger is created.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I integrate Context And SpanContext both allowed. 8a18dc7

@proost proost requested a review from lalitb May 15, 2026 07:02
Comment thread sdk/src/logs/logger.cc Outdated
Comment thread api/test/logs/logger_test.cc
@proost proost requested a review from dbarker May 17, 2026 13:59
Comment thread api/include/opentelemetry/logs/logger.h Outdated
@proost proost requested a review from lalitb May 20, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

discuss To discuss in SIG meeting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API] Support for Logger::Enabled() is incomplete

5 participants