Skip to content

[SDK] Handle instrumentation object construction failures safely - #4469

Open
22elix3r wants to merge 6 commits into
open-telemetry:mainfrom
22elix3r:fix/noexcept-provider-construction-4361
Open

[SDK] Handle instrumentation object construction failures safely#4469
22elix3r wants to merge 6 commits into
open-telemetry:mainfrom
22elix3r:fix/noexcept-provider-construction-4361

Conversation

@22elix3r

Copy link
Copy Markdown

Fixes #4361

Changes

Provider construction happens during SDK initialization. Runtime GetTracer / GetLogger / GetMeter calls must not throw. This change follows the post-SIG direction from @dbarker:

  • Remove noexcept from the SDK constructors for TracerProvider, LoggerProvider, MeterProvider, Tracer, Logger, and Meter. Allocation or initialization failures during provider construction can now propagate so the caller (often the configuration library) can handle them.
  • Keep GetTracer, GetLogger, and GetMeter noexcept. If constructing a previously unseen tracer/logger/meter fails, the provider catches the exception (when exceptions are enabled), logs via OTEL_INTERNAL_LOG_ERROR without letting logging escape, and returns a valid noop API object.
  • The noop fallback is allocated during provider construction (new NoopTracer / NoopLogger / NoopMeter stored as a nostd::shared_ptr). Runtime failure handling does not allocate a fresh noop object.
  • Failed construction is not inserted into the provider cache. A later Get* call can retry and return a normal SDK object once the failure is gone. Existing cached objects continue to be returned unchanged.
  • The complete fallible path is protected: instrumentation-scope construction, configurator evaluation, object allocation, shared_ptr control-block allocation, cache insertion, and metrics AddMeter.
  • Public API virtual method exception contracts are unchanged. ABI v1 and ABI v2 Get* signatures are unchanged. Try/catch is compiled only when OPENTELEMETRY_HAVE_EXCEPTIONS is set.

Tests added

  • Constructor noexcept static_asserts for each signal.
  • Deterministic fault injection through a ScopeConfigurator matcher that throws for a named scope.
  • After failure, Get* returns a valid noop-compatible object, using it produces no telemetry, the cache is not poisoned, cached objects are unchanged, and a later call recovers when the injected failure is removed.
  • Those tests are skipped when exceptions are disabled.

Validation

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug \
  -DOTELCPP_WITH_EXAMPLES=OFF -DOTELCPP_BUILD_TESTING=ON \
  -DOTELCPP_WITH_OTLP_GRPC=OFF -DOTELCPP_WITH_OTLP_HTTP=OFF \
  -DOTELCPP_WITH_OTLP_FILE=OFF -DCMAKE_CXX_STANDARD=17

cmake --build build --target tracer_provider_test logger_provider_sdk_test meter_provider_sdk_test
ctest --test-dir build --output-on-failure \
  -R 'trace.(TracerProvider|TracerConfig)|logs.(LoggerProviderSDK|LoggerConfig)|metrics.(MeterProvider|MeterConfig)'
# 63/63 passed, including Get*ReturnsNoopOnConstructionFailure

ctest --test-dir build --output-on-failure -R 'logs.LoggerSDK'
# 12/12 passed

./build/sdk/test/metrics/meter_test
# 20/20 passed

# Exception-disabled compile of changed SDK sources:
g++ -std=c++17 -fno-exceptions -c -DOPENTELEMETRY_HAVE_EXCEPTIONS=0 ...
# tracer_provider.cc, tracer.cc, logger_provider.cc, logger.cc,
# meter_provider.cc, meter.cc: success

# ABI v2 compile of the three provider .cc files:
g++ -std=c++17 -c -DOPENTELEMETRY_ABI_VERSION_NO=2 ...
# tracer_provider.cc, logger_provider.cc, meter_provider.cc: success

git diff --check
# clean

tools/format.sh was not run: clang-format is not installed in this environment.

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed (SDK constructor exception specs only; API Get* contracts unchanged)

Remove noexcept from SDK TracerProvider, LoggerProvider, MeterProvider,
Tracer, Logger, and Meter constructors so initialization-time allocation
failures can propagate. Keep GetTracer/GetLogger/GetMeter noexcept and
return a pre-allocated noop object if constructing a new instrumentation
object fails, without caching the failed attempt.

Fixes open-telemetry#4361

Signed-off-by: elix3r <157088510+22elix3r@users.noreply.github.com>
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.17391% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.59%. Comparing base (e30ef1d) to head (38e1c26).

Files with missing lines Patch % Lines
sdk/src/logs/logger_provider.cc 70.00% 9 Missing ⚠️
sdk/src/metrics/meter_provider.cc 78.58% 6 Missing ⚠️
sdk/src/trace/tracer_provider.cc 79.32% 6 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4469      +/-   ##
==========================================
- Coverage   82.63%   82.59%   -0.04%     
==========================================
  Files         512      512              
  Lines       20139    20194      +55     
==========================================
+ Hits        16640    16677      +37     
- Misses       3499     3517      +18     
Files with missing lines Coverage Δ
sdk/include/opentelemetry/sdk/metrics/meter.h 57.15% <ø> (ø)
sdk/include/opentelemetry/sdk/trace/tracer.h 100.00% <ø> (ø)
sdk/src/logs/logger.cc 94.12% <100.00%> (ø)
sdk/src/metrics/meter.cc 81.36% <100.00%> (ø)
sdk/src/trace/tracer.cc 80.22% <100.00%> (ø)
sdk/src/metrics/meter_provider.cc 85.51% <78.58%> (-6.80%) ⬇️
sdk/src/trace/tracer_provider.cc 83.96% <79.32%> (-4.75%) ⬇️
sdk/src/logs/logger_provider.cc 84.00% <70.00%> (-7.07%) ⬇️

... and 1 file with indirect coverage changes

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

@22elix3r
22elix3r marked this pull request as ready for review August 22, 2026 15:18
@22elix3r
22elix3r requested a review from a team as a code owner August 22, 2026 15:18
Copilot AI lite review requested due to automatic review settings August 22, 2026 15:18
@22elix3r

Copy link
Copy Markdown
Author

@dbarker Latest commits are up (format/IWYU follow-up + merge from main). CI is waiting on workflow approval again.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the OpenTelemetry C++ SDK to safely handle failures during instrumentation object construction while preserving noexcept guarantees for runtime GetTracer/GetLogger/GetMeter calls by returning a pre-allocated noop fallback when construction fails (with exception handling compiled in only when exceptions are enabled).

Changes:

  • Remove noexcept from SDK provider and instrumentation constructors so initialization failures can propagate to the caller.
  • Keep provider Get* methods noexcept, adding guarded try/catch to log construction failures and return a pre-allocated noop object without poisoning caches.
  • Add unit tests for constructor exception specs and deterministic fault injection/recovery behavior (skipped when exceptions are disabled).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
sdk/test/trace/tracer_provider_test.cc Adds static_asserts for constructor exception specs and tests noop fallback + recovery for GetTracer.
sdk/test/metrics/meter_provider_sdk_test.cc Adds static_asserts and tests noop fallback + recovery for GetMeter.
sdk/test/logs/logger_provider_sdk_test.cc Adds static_asserts and tests noop fallback + recovery for GetLogger; introduces a new test processor.
sdk/src/trace/tracer.cc Removes noexcept from Tracer constructor implementation.
sdk/src/trace/tracer_provider.cc Adds pre-allocated noop tracer fallback and exception-guarded construction in GetTracer.
sdk/src/metrics/meter.cc Removes noexcept from Meter constructor implementation.
sdk/src/metrics/meter_provider.cc Adds pre-allocated noop meter fallback and exception-guarded construction in GetMeter.
sdk/src/logs/logger.cc Removes noexcept from Logger constructor implementation.
sdk/src/logs/logger_provider.cc Adds pre-allocated noop logger fallback and exception-guarded construction in GetLogger.
sdk/include/opentelemetry/sdk/trace/tracer.h Removes noexcept from Tracer constructor declaration.
sdk/include/opentelemetry/sdk/trace/tracer_provider.h Removes noexcept from TracerProvider constructors; adds stored noop tracer member.
sdk/include/opentelemetry/sdk/metrics/meter.h Removes noexcept from Meter constructor declaration.
sdk/include/opentelemetry/sdk/metrics/meter_provider.h Removes noexcept from MeterProvider constructors; adds stored noop meter member.
sdk/include/opentelemetry/sdk/logs/logger.h Removes noexcept from Logger constructor declaration.
sdk/include/opentelemetry/sdk/logs/logger_provider.h Removes noexcept from LoggerProvider constructors; adds stored noop logger member.
CHANGELOG.md Documents the SDK behavior change for constructor exception specs and noop fallback behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +285 to +289
void OnEmit(std::unique_ptr<Recordable> &&record) noexcept override
{
++emit_count_;
auto record_ptr = std::move(record);
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — ignored the parameter instead of moving it into an unused local.

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.

@22elix3r Thanks for the updates! Just sharing some context that Copilot is missing and why your original approach is preferred.

We've been using the pattern of moving args passed as rvalue references into temporary objects (sometimes named "unused") to ensure the method maintains ownership of the arg's lifetime. If we apply the (void)record approach then it is possible the record is not destroyed within the OnEmit method scope. This would be unexpected from the caller.

The auto record_ptr = std::move(record); approach is more explicit and matches the intended pattern of the production log record processors.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Restored auto record_ptr = std::move(record) so OnEmit still takes ownership of the record. Copilot's unused-variable note was a miss here. the unique_ptr destructor is the use.

Signed-off-by: elix3r <157088510+22elix3r@users.noreply.github.com>
@22elix3r
22elix3r requested a lite review from Copilot August 22, 2026 15:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Restore CountingProcessor ownership of the OnEmit record via std::move.
Add the includes IWYU asked for, and mark empty logging catches so
clang-tidy does not count them against the unique-warning limit.

Signed-off-by: elix3r <157088510+22elix3r@users.noreply.github.com>
…er-construction-4361

Signed-off-by: elix3r <157088510+22elix3r@users.noreply.github.com>
@22elix3r

Copy link
Copy Markdown
Author

@dbarker IWYU and clang-tidy should be clean now. CountingProcessor uses std::move again, and the branch is merged onto latest main. Ready for another look when you have a minute.

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.

Code flagged noexcept should not raise exceptions

3 participants