Skip to content

[CONFIGURATION] Implement EventToSpanEventBridge log record processor - #4309

Open
om7057 wants to merge 20 commits into
open-telemetry:mainfrom
om7057:fix/event-to-span-event-bridge-config
Open

[CONFIGURATION] Implement EventToSpanEventBridge log record processor#4309
om7057 wants to merge 20 commits into
open-telemetry:mainfrom
om7057:fix/event-to-span-event-bridge-config

Conversation

@om7057

@om7057 om7057 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #4155

Implements ExperimentalEventToSpanEventBridgeLogRecordProcessor from the declarative configuration spec. Mirrors the EventToSpanEventBridge processor already in the Java SDK: if a log record has an event name and its trace/span id match the currently active span, it gets copied over as a span event on that span. Doesn't export anything itself and doesn't stop the record from continuing through the rest of the pipeline.

YAML key is event_to_span_event_bridge/development, matching the schema in open-telemetry/opentelemetry-configuration (checked opentelemetry_configuration.json and meta_schema_language_cpp.yaml, which list this type as not_implemented with no extra properties).

New processor class under sdk/include/opentelemetry/sdk/logs/event_to_span_event_bridge_processor.h + .cc, wired into the config parser, the visitor, and SdkBuilder the same way simple/batch already are.

Tests: sdk/test/logs/event_to_span_event_bridge_processor_test.cc covers bridging onto the current matching span, ignoring records with no event name, and ignoring events from a different span. sdk/test/configuration/yaml_logs_test.cc covers the YAML parsing side.

Ran the full local test suite (915 tests) before pushing, all green. CHANGELOG updated.

Adds the ExperimentalEventToSpanEventBridgeLogRecordProcessor type from
the declarative configuration spec. When a log record carries an event
name and matches the trace and span id of the currently active span,
this processor copies it over as a span event, mirroring the existing
implementation in the Java SDK.

Fixes open-telemetry#4155
@om7057
om7057 requested a review from a team as a code owner July 26, 2026 05:12
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 59.02778% with 59 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.17%. Comparing base (57188aa) to head (1bbb8bf).

Files with missing lines Patch % Lines
...k/src/logs/event_to_span_event_bridge_processor.cc 53.69% 44 Missing ⚠️
sdk/src/configuration/sdk_builder.cc 0.00% 5 Missing ⚠️
...gs/event_to_span_event_bridge_processor_factory.cc 0.00% 3 Missing ⚠️
...ation/log_record_processor_configuration_visitor.h 0.00% 2 Missing ⚠️
...ry/sdk/logs/event_to_span_event_bridge_processor.h 50.00% 2 Missing ⚠️
sdk/include/opentelemetry/sdk/logs/processor.h 33.34% 2 Missing ⚠️
sdk/src/logs/multi_log_record_processor.cc 93.75% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4309      +/-   ##
==========================================
- Coverage   81.29%   81.17%   -0.12%     
==========================================
  Files         447      451       +4     
  Lines       19028    19173     +145     
==========================================
+ Hits        15467    15561      +94     
- Misses       3561     3612      +51     
Files with missing lines Coverage Δ
..._event_bridge_log_record_processor_configuration.h 100.00% <100.00%> (ø)
...lude/opentelemetry/sdk/configuration/sdk_builder.h 100.00% <ø> (ø)
sdk/src/configuration/configuration_parser.cc 78.78% <100.00%> (+0.48%) ⬆️
sdk/src/logs/logger.cc 94.40% <100.00%> (+0.28%) ⬆️
sdk/src/logs/logger_context.cc 94.60% <100.00%> (+0.66%) ⬆️
sdk/src/logs/multi_log_record_processor.cc 96.00% <93.75%> (-0.42%) ⬇️
...ation/log_record_processor_configuration_visitor.h 50.00% <0.00%> (-50.00%) ⬇️
...ry/sdk/logs/event_to_span_event_bridge_processor.h 50.00% <50.00%> (ø)
sdk/include/opentelemetry/sdk/logs/processor.h 81.82% <33.34%> (-18.18%) ⬇️
...gs/event_to_span_event_bridge_processor_factory.cc 0.00% <0.00%> (ø)
... and 2 more

... and 2 files with indirect coverage changes

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

om7057 added 3 commits July 26, 2026 11:27
The new yaml_logs_test.cc test case used dynamic_cast, but one CI config
builds with -fno-rtti; switched to reinterpret_cast to match the rest of
the file. Also reverted an unrelated indentation change in sdk_builder.cc
that a local clang-format 14 run introduced on lines I didn't touch;
CI runs clang-format 18, which formats these continuation lines
differently.
clang-tidy flagged three things that pushed the abiv2-preview preset
over its warning budget: the attributes_ member stored a reference
instead of a pointer, OnEmit released the incoming unique_ptr without
an explicit std::move, and ForEachKeyValue could let an allocation
failure escape a noexcept override. Switched the member to a pointer,
added std::move, and wrapped the iteration in a try/catch guarded by
OPENTELEMETRY_HAVE_EXCEPTIONS, matching the pattern already used in
periodic_exporting_metric_reader.cc.

iwyu wanted <utility> in both files for std::move, plus a few trace
headers directly included in the test file rather than pulled in
transitively.
…ectly

configuration_parser.cc used EventToSpanEventBridgeLogRecordProcessorConfiguration
via a transitive include only, which iwyu flagged in both abiv1-preview and
abiv2-preview jobs.
@om7057

om7057 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@dbarker @marcalff could you please review this PR?

* - the log record has a non-empty event name,
* - the log record carries a valid trace id and span id,
* - the current active span (from the ambient context at the time OnEmit is called) is
* recording, and

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.

How do we know the ambient span at OnEmit() is the span from the log's resolved context?

The Logs SDK specification requires using the resolved context: the explicitly supplied context when present, otherwise the ambient context. These can be different. For example, the log may be emitted with span_a explicitly while span_b is currently ambient. In that case this processor finds span_b, its IDs do not match the log, and the event is silently dropped.

The current LogRecordProcessor::OnEmit() API does not receive the resolved context, so I don't think Tracer::GetCurrentSpan() can implement the required behavior correctly.

@om7057 om7057 Jul 28, 2026

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.

@lalitb Got it. I didn't fully consider the case where a log is explicitly created with one span context while a different span is ambient.

I worked on this by storing resolved context on the Recordable interface:

Added SetResolvedContext() and GetResolvedContext() methods to the Recordable base class
Updated ReadWriteLogRecord to store and retrieve the context
Updated Logger to set the resolved context when creating recordables
Modified EventToSpanEventBridge to use it for proper span matching
This keeps the LogRecordProcessor API stable while giving the bridge access to the log's actual context. The changes are in the latest commits on this branch.

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 don’t think Recordable should store the resolved context. Its purpose is to carry the log record through the processing and export pipeline, and it may be retained by a batch processor. Keeping a Context there can also keep the span alive longer than needed.

The resolved context should be passed to the processor during emission instead. Since OnEmit() is a public SDK extension point, we should keep it for compatibility and add a separate OnEmitWithContext(record, resolved_context) method that forwards to OnEmit(record) by default. Using a separate name avoids C++ overload hiding. The logger and multi-processor can pass the resolved context through, while only the bridge needs to override the new method. The context should only be valid for the duration of the call and should not be retained.

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.

@lalitb Thank you for the guidance. I've implemented the solution exactly as you suggested:

  • Added OnEmitWithContext(record, context) to LogRecordProcessor with a default implementation that forwards to OnEmit() for backward compatibility
  • Updated Logger to call OnEmitWithContext() with the resolved context
  • Updated MultiLogRecordProcessor to forward the context to child processors
  • Implemented EventToSpanEventBridge to override OnEmitWithContext() and use the resolved context for proper span matching

The context is passed by reference and never retained. All changes are pushed and ready for review.

@lalitb lalitb left a comment

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.

As mentioned here - . This violates the Logs SDK specification for explicitly supplied contexts and can silently drop valid events. The current C++ LogRecordProcessor::OnEmit() API does not expose that context, so the API gap
needs to be addressed before this bridge can be implemented correctly.

@om7057

om7057 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

How do we know the ambient span at OnEmit() is the span from the log's resolved context?

The Logs SDK specification requires using the resolved context: the explicitly supplied context when present, otherwise the ambient context. These can be different. For example, the log may be emitted with span_a explicitly while span_b is currently ambient. In that case this processor finds span_b, its IDs do not match the log, and the event is silently dropped.

The current LogRecordProcessor::OnEmit() API does not receive the resolved context, so I don't think Tracer::GetCurrentSpan() can implement the required behavior correctly.

@lalitb Thank you for pointing this out. The current implementation doesn't handle explicitly supplied contexts properly; it will silently drop events from logs that were emitted with a different span context than what's currently active.

Would it be acceptable to:

Add a method to the Recordable interface to store/retrieve the resolved context
Update Logger to set it when creating the recordable
Have the bridge use it to properly match against the log's context
This way we keep existing processor signatures unchanged while giving processors access to the context they need. What do you think?

om7057 added 3 commits July 28, 2026 19:56
Add OnEmitWithContext() method to LogRecordProcessor interface to pass the
resolved context (explicit if provided, else ambient) to processors that need it.
The context is valid only for the duration of the call and must not be retained.

Updates:
- LogRecordProcessor: new OnEmitWithContext() virtual method (default forwards to OnEmit)
- Logger: call OnEmitWithContext with resolved context
- MultiLogRecordProcessor: implement OnEmitWithContext to forward to child processors

This enables processors like EventToSpanEventBridge to access the log's resolved
context for proper span matching, addressing spec compliance for explicit contexts.
…pport

Update EventToSpanEventBridgeProcessor to override OnEmitWithContext and use the
log's resolved context (explicit if provided, else ambient) for proper span matching.

Changes:
- Override OnEmitWithContext to receive the resolved context from the logger
- Extract span context from the variant (SpanContext or Context)
- Compare resolved span context to log's trace/span IDs
- Verify current active span matches resolved context before adding event
- Update processor docstring to clarify context usage

This ensures spec compliance for explicitly-supplied log contexts where the log
and ambient span might differ.
@om7057
om7057 force-pushed the fix/event-to-span-event-bridge-config branch from 4e0808a to ec30ecb Compare July 29, 2026 18:48
@om7057

om7057 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

@lalitb Thank you for the guidance. I've implemented the solution exactly as you suggested:

  • Added OnEmitWithContext(record, context) to LogRecordProcessor with a default implementation that forwards to OnEmit() for backward compatibility
  • Updated Logger to call OnEmitWithContext() with the resolved context
  • Updated MultiLogRecordProcessor to forward the context to child processors
  • Implemented EventToSpanEventBridge to override OnEmitWithContext() and use the resolved context for proper span matching

The context is passed by reference and never retained. All changes are pushed and ready for review.

* @param record the log recordable object
* @param context the resolved context (SpanContext or Context variant)
*/
virtual void OnEmitWithContext(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This changes the public SDK extension point, please document both compatibility changes explicitly. To avoid the source break, give the visitor method a default no-op implementation. For LogRecordProcessor, either introduce the new virtual only in a new ABI version or clearly document that downstream processors must be rebuilt.

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.

I went with the "clearly document" option rather than gating on a new ABI version; let me know if you'd prefer the other.

OnEmitWithContext() and ConsumesResolvedContext() both have default implementations, so existing processors compile and behave unchanged. That, and the fact that the added virtuals change the LogRecordProcessor vtable so subclasses must be rebuilt, is now stated in the header and in the CHANGELOG breaking-changes section.

VisitEventToSpanEventBridge() was the actual source of the break; it was pure virtual. It now defaults to a no-op, with a comment explaining why it differs from its siblings.

}

// Get the span object for the resolved context and add the event
auto current_span = opentelemetry::trace::Tracer::GetCurrentSpan();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The live span from the resolved Context is found and then discarded. In the explicit-context case:

  1. GetSpan(ctx) returns span_a, so the first ID check passes.
  2. GetCurrentSpan() returns ambient span_b.
  3. The second ID check compares span_b with the log IDs for span_a and drops the event.

This recreates the requirement that the resolved span must also be ambient. For the Context alternative, retain the Span returned by GetSpan(ctx) and call AddEvent on that object after the recording and ID checks. Handle the bare SpanContext alternative separately because it does not carry a live Span.

Please add a regression test with explicit span_a, ambient span_b, and log IDs for span_a; assert that only span_a receives the event. The current processor tests call only OnEmit, so this path remains untested.

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.

Thank you for tracing it step by step, the span from GetSpan(ctx) was found and then thrown away for the ambient one, so the second ID check could never pass in the explicit case.

The processor now adds the event to the span from the resolved Context. The bare SpanContext alternative is handled separately as you asked, since it carries no live Span and there's no way to look one up by id; it resolves through ambient with the ID check rejecting a mismatch. Added BridgesOntoExplicitContextSpanWhileDifferentSpanIsAmbient (explicit span_a, ambient span_b, ids for span_a, asserts only span_a gets the event), which fails against the previous code, plus tests for the mismatch and bare-SpanContext cases.

One gap I should flag rather than paper over: that test drives OnEmitWithContext() directly. End-to-end through Logger the explicit context still doesn't arrive, because Logger::EmitLogRecord() only receives the LogRecord, context_or_span is consumed by CreateLogRecord() and there's no route to emit time (and per the earlier discussion it shouldn't live on the Recordable). So today the SDK always hands the bridge the ambient context. Closing that needs the context-aware emit path you alluded to, which looks like an ABI-v2-guarded addition carrying context_or_span from logger.h down to the processor. Want me to add that in this PR, or land the processor-level fix and do the plumbing separately?

ASSERT_EQ(config->logger_provider->processors.size(), 1);
auto *processor = config->logger_provider->processors[0].get();
ASSERT_NE(processor, nullptr);
auto *bridge = reinterpret_cast<

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

reinterpret_cast does not check the dynamic type, so ASSERT_NE(bridge, nullptr) only repeats the preceding non-null check on processor.

For example, if the YAML key is misspelled as event_to_span_event_brige/development, the parser falls through to ParseExtensionLogRecordProcessorConfiguration and returns a non-null ExtensionLogRecordProcessorConfiguration. The cast still produces a non-null pointer, so this test passes even though no bridge configuration was parsed. The neighboring simple/batch tests use the same unsafe cast but at least inspect type-specific parsed fields; this empty configuration has no such signal.

Please make the assertion type-aware without relying on RTTI: pass a test LogRecordProcessorConfigurationVisitor to processor->Accept, record that VisitEventToSpanEventBridge was called, and fail if VisitExtension or any other callback runs. That directly proves this key selected the bridge configuration and catches the fallback case.

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.

Agreed; it just repeated the non-null check above it.

It now passes a recording LogRecordProcessorConfigurationVisitor to processor->Accept() and asserts VisitEventToSpanEventBridge fired while VisitExtension/VisitBatch/VisitSimple did not. No RTTI involved. I also added a companion test using your exact misspelling (event_to_span_event_brige/development) that pins the extension fallback, so the bridge assertion can't silently pass for a non-bridge model.

Comment thread sdk/src/logs/logger.cc Outdated
processor.OnEmit(std::move(recordable));
// Send the log recordable to the processor with the resolved context
const nostd::variant<trace_api::SpanContext, context::Context> resolved_context{
context::RuntimeContext::GetCurrent()};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This adds an unconditional RuntimeContext::GetCurrent() call for every emitted log, even when no bridge is configured and the processor ignores the context. That adds hot-path work for all users.

The three cited lookups are not all on one path: ordinary ABI v2 logging performs two, not three. The remaining issue is the extra dispatch-time lookup, especially for ABI v1, which cannot use the explicit-Context path.

Please keep ordinary dispatch on OnEmit, let the bridge resolve ambient context only when configured, and pass an already-resolved explicit context through a separate context-aware path. Add a test proving that a processor which ignores context causes no extra GetCurrent() call.

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.

Got it, that shouldn't cost anything when nothing reads it. Thanks also for the correction on the lookup count.

Ordinary dispatch is back on OnEmit(). Added LogRecordProcessor::ConsumesResolvedContext() (default false); MultiLogRecordProcessor reports true if any child does, and LoggerContext caches it the same way it already caches RecordableEnforcesLogRecordLimits(). Logger only resolves the context when a processor will read it, so simple/batch pipelines add no lookup. Test installs a counting RuntimeContextStorage and asserts EmitLogRecord() adds zero GetCurrent() calls for a context-ignoring processor.

The second half passes an already-resolved explicit context through a separate path. Currently, the resolved context is always ambient.


std::unique_ptr<Recordable> EventToSpanEventBridgeProcessor::MakeRecordable() noexcept
{
return std::unique_ptr<Recordable>(new ReadWriteLogRecord());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adding this processor makes MultiLogRecordProcessor create a bridge ReadWriteLogRecord for every emitted log. The record is populated along with every other child recordable; body and attributes become owned copies. For a log with no event name, OnEmit then immediately discards that work.

Please avoid eagerly creating the bridge child recordable for non-event logs. An Enabled override alone is not enough: another processor may enable the log, after which MakeRecordable still creates recordables for every child. This needs per-child filtering or lazy recordable creation in the multi-processor path.

Please add a test with a normal processor plus the bridge and verify that an empty event name does not call the bridge's MakeRecordable, while a named event still does.

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.

I agree the waste is real, but I couldn't find a fix that actually removes the copy, so I'd rather ask than change shared infrastructure on a guess.

The event name isn't known when MakeRecordable() runs: EmitLogRecord calls CreateLogRecord() first (logger.h:216), and the EventId argument is applied afterwards via LogRecordSetterTrait. Argument order is caller-controlled, so SetEventId can also land after body/attributes. So nothing at MakeRecordable() time can key on it, and the test as specified is only satisfiable by deferring creation.

The catch with deferring: populating the child later means retaining the fields, and the buffer has to be filled unconditionally (it's still unknown whether an event name is coming), so it moves the attribute/body copy rather than removing it. The one variant that does remove it is replaying from a sibling recordable at SetEventId time, since with a simple/batch processor present the data is already there, but that needs a non-RTTI way to discover a sibling is a ReadableLogRecord (there's a nortti CI job), so a new Recordable hook, and it degrades when no readable sibling exists.

Since this touches MultiRecordable/MultiLogRecordProcessor for every user to optimize one optional processor, which direction do you prefer: the sibling-replay hook, something else, or a tracked follow-up?

current_span->AddEvent(event_name, event_timestamp, attributes);
}

void EventToSpanEventBridgeProcessor::OnEmitWithContext(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These two entry points (OnEmit and OnEmitWithContext) duplicate the same validation and emission pipeline, and they have already drifted. Probably extract the common part to a separate function.

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.

Both entry points now share BridgeRecordToSpan(), which holds every precondition and the emission in one place; OnEmit() passes the ambient span and OnEmitWithContext() passes the span from the resolved context.

om7057 added 3 commits July 30, 2026 07:35
…nt-bridge-config

# Conflicts:
#	CHANGELOG.md
…n, harden tests

Bridge onto the span from the resolved context (ThomsonTan review):
OnEmitWithContext() looked up the live span from the resolved Context and then
discarded it in favour of the ambient span, so an event whose ids referred to an
explicitly supplied span was dropped whenever a different span was ambient. The
span from the resolved Context is now the span the event is added to. A bare
SpanContext carries no live Span, so that alternative is handled separately and
still resolves through the ambient span, with the id check rejecting a mismatch.

Remove the duplicated emit pipeline: OnEmit() and OnEmitWithContext() had drifted
apart. Both now share BridgeRecordToSpan(), which holds all preconditions and the
emission in one place.

Keep the emit hot path free of context resolution: LogRecordProcessor gains
ConsumesResolvedContext() (default false), MultiLogRecordProcessor reports true
when any child does, and LoggerContext caches the answer the same way it already
caches RecordableEnforcesLogRecordLimits(). Logger only resolves the ambient
context and dispatches through OnEmitWithContext() when some processor will
actually read it, so simple/batch pipelines keep dispatching through OnEmit()
with no extra RuntimeContext::GetCurrent() call.

Document compatibility: OnEmitWithContext() and ConsumesResolvedContext() are
source compatible via default implementations but change the LogRecordProcessor
vtable, so subclasses must be recompiled. Noted in the header and in the
CHANGELOG breaking-changes section. VisitEventToSpanEventBridge() was pure
virtual, which broke existing visitors, and now defaults to a no-op.

Make the YAML assertion type-aware: the reinterpret_cast only repeated the
preceding non-null check and passed for a misspelled key that fell through to the
extension configuration. The test now dispatches through Accept() with a
recording visitor, and a companion test pins the misspelled-key fallback.

Tests: explicit span_a with ambient span_b asserting only span_a receives the
event (fails against the previous code), explicit-context mismatch, bare
SpanContext, and Logger-level tests that a context-ignoring processor dispatches
through OnEmit() and adds no GetCurrent() call, using a counting
RuntimeContextStorage.
The base declaration left the second parameter unnamed while the comment
documented @param context, so Doxygen warned that the documented argument was
not in the argument list -- once for LogRecordProcessor and again for the
EventToSpanEventBridgeProcessor override that inherits the comment.

Name the parameter and mark it OPENTELEMETRY_MAYBE_UNUSED, since the default
implementation forwards to OnEmit() without reading it. Keeps the parameter
documented rather than dropping the @param line.

Verified with doxygen against docs/public/Doxyfile.lint: no warnings remain for
any file this PR touches. The warnings still reported for api logs logger.h,
noop.h and sdk resource.h are pre-existing on main, are not emitted by the
Doxygen version CI uses, and are untouched here.
@om7057

om7057 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @ThomsonTan.

Fully addressed: the compatibility documentation and the visitor source break, the type-aware YAML assertion (plus a misspelled-key guard), and the duplicated emit pipeline.

Partially addressed, with one honest caveat: the discarded-span bug is fixed in the processor and covered by a regression test that fails against the old code, and ordinary dispatch is back on OnEmit() with no added GetCurrent() for context-ignoring pipelines. But the explicit context still never reaches the processor end-to-end, because Logger::EmitLogRecord() doesn't receive it, so in practice the bridge is still handed the ambient context. I've left details and a question on the relevant threads.

Still open: the eager child recordable. I couldn't find a fix that actually avoids the copy and would like your steer before touching MultiRecordable; details on that thread.

Verified locally; please check once more that the changes have been made.

om7057 added 3 commits July 31, 2026 02:19
- event_to_span_event_bridge_processor.cc: drop <memory>, no longer needed
  directly now that the shared BridgeRecordToSpan() helper pulls unique_ptr
  transitively through recordable.h.
- event_to_span_event_bridge_processor_test.cc: swap trace/context.h for
  nostd/string_view.h + trace/span_metadata.h; the new regression tests use
  trace_api::kSpanKey directly and never call GetSpan()/SetSpan().
- logger_sdk_test.cc: add stddef.h (size_t) and nostd/unique_ptr.h
  (CountingRuntimeContextStorage::Attach's return type), both used directly
  by the new dispatch tests.
- yaml_logs_test.cc: swap the two concrete
  {event_to_span_event_bridge,extension}_log_record_processor_configuration.h
  includes for the base log_record_processor_configuration.h; the parsed
  processor pointer is typed as the base class and only Accept() is called on
  it, the concrete types are only named as visitor-callback parameters, which
  the visitor header already forward-declares.

Verified with 'iwyu' locally is not available in this environment, so
confirmed by inspection against the CI diagnostics and by rebuilding: full
build is clean and ctest is 931/931 passing. doxygen docs/public/Doxyfile.lint
still reports zero warnings for any file this PR touches.
clang-tidy (both abiv1-preview and abiv2-preview pushed the warning count
over the job's threshold by 2):

- event_to_span_event_bridge_processor.cc:237 bugprone-exception-escape:
  OnEmitWithContext() is noexcept but used holds_alternative<Context>() +
  get<Context>(context), and nostd::get() throws bad_variant_access on a
  mismatched alternative. Even though the holds_alternative check made that
  path unreachable, clang-tidy can't prove it statically. Switched to
  nostd::get_if<Context>(&context), which returns nullptr instead of
  throwing -- the same pattern Logger::ExtractSpanContext already uses for
  this exact variant in logger.cc.

- logger_sdk_test.cc:1014 cppcoreguidelines-rvalue-reference-param-not-moved:
  DispatchRecordingProcessor::OnEmit() took its unique_ptr<Recordable>&&
  by dropping it unnamed. Named the parameter and moved it into a
  static_cast<void>(...), mirroring the sibling OnEmitWithContext() override
  a few lines below that already does this.

iwyu (abiv2-preview only; abiv1/abiv1-preview already required these
directly and passed):

- logger_sdk_test.cc: marked the <stddef.h> and opentelemetry/context/
…nt-bridge-config

# Conflicts:
#	CHANGELOG.md
@om7057

om7057 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@lalitb, can you confirm if all the changes requested are addressed in the later commits, and if so could you please approve the PR?

om7057 and others added 6 commits August 1, 2026 22:28
…nt-bridge-config

# Conflicts:
#	CHANGELOG.md
CI / CMake gcc 14 (maintainer mode, sync) failed with a glibc heap-corruption
abort (double free or corruption (fasttop)) inside the functional/otlp mTLS
integration test binary during the mtls-ok test case. Both full ctest runs
in that job completed 100% passing (1332/1332) beforehand; the crash is in
a separate OTLP gRPC exporter mTLS test harness untouched by this PR's diff.
Not a Required check. Retriggering to see if it clears as a transient
flake.
Two unrelated flakes seen across recent runs:
- CI / CMake gcc 14 (maintainer mode, sync): glibc heap-corruption abort in
  the functional/otlp mTLS integration test (mtls-ok case), after both
  ctest suites in that job passed 100%. Not touched by this PR's diff.
- CI / CMake FetchContent usage with opentelemetry-cpp: BasicCurlHttpTests.
  ElegantQuitQuick failed a hardcoded 20ms shutdown-latency assertion
  (actual 247ms) in ext/test/http/curl_http_test.cc, whose own comment
  already documents this margin as CI-host-load-sensitive. Not touched by
  this PR's diff.

Retriggering to see if both clear as transient CI-host flakes.
CI / CMake gcc 14 (maintainer mode, async) failed after both ctest suites
passed 100% (1334/1334). The failure is in functional/otlp/run_test.sh's
docker build step, timing out while pulling
otel/opentelemetry-collector:0.123.0 from Docker Hub:
  dial tcp 54.164.79.128:443: i/o timeout
Network/registry timeout on the runner, unrelated to this PR's diff (no
functional/otlp or Dockerfile changes here). Retriggering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CONFIGURATION] File configuration - EventToSpanEventBridge

3 participants