Skip to content

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

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

[SDK] Handle instrumentation object construction failures safely#4469
22elix3r wants to merge 7 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.65957% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.08%. Comparing base (6af677c) to head (274e114).

Files with missing lines Patch % Lines
sdk/src/logs/logger_provider.cc 70.00% 9 Missing ⚠️
sdk/src/metrics/meter_provider.cc 80.00% 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   83.12%   83.08%   -0.03%     
==========================================
  Files         519      519              
  Lines       20256    20312      +56     
==========================================
+ Hits        16835    16875      +40     
- Misses       3421     3437      +16     
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 87.18% <80.00%> (-6.15%) ⬇️
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 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.

@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 thread sdk/test/logs/logger_provider_sdk_test.cc
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.

@dbarker dbarker 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.

Thanks for the PR! Please see some feedback below.

Comment thread sdk/src/metrics/meter_provider.cc Outdated
<< detail << "; returning noop meter.");
#if OPENTELEMETRY_HAVE_EXCEPTIONS
}
catch (...) // NOLINT(bugprone-empty-catch)

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.

please catch std::exception here (and the other similar locations). The logging may really only through bad alloc from the string stream but catching the standard exception is safe.

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 in ff059b9. The logging helpers in MeterProvider, TracerProvider, and LoggerProvider now catch std::exception instead of (...). Logging can still throw (bad_alloc from the string stream is the realistic case), and catching std::exception is enough to keep the noexcept Get* path from escaping.

LogGetTracerConstructionFailure(ex.what());
return noop_tracer_;
}
catch (...)

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.

Please add a comment here on the rationale for the catch all (that this must catch any user thrown exception in configurators). Please also do the same for logger and meter.

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.

Added a comment on the catch-all in TracerProvider, LoggerProvider, and MeterProvider in ff059b9. Scope configurators are user callbacks, so they can throw anything, including types that are not std::exception. Get* is noexcept, so we have to catch all of that and return the preallocated noop.

TEST(MeterProvider, GetMeterReturnsNoopOnConstructionFailure)
{
auto should_throw = std::make_shared<bool>(true);
auto throwing_configurator =

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.

Perfect use case. The configurator can be any user provided callback that gets executed when constructing a Meter/Tracer/Logger.

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.

Glad that landed in the right place. We went through the configurator on purpose so the test hits the same path a real user callback would.

Logging can throw std::bad_alloc from the string stream. Catch
std::exception in the GetTracer/GetLogger/GetMeter logging helpers
instead of (...). Document why the construction catch-all remains:
user scope configurators can throw any exception type.

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

Copy link
Copy Markdown
Author

@dbarker Addressed the review notes in ff059b9. Logging helpers now catch std::exception, and I added comments on the catch-all around user configurators. Same change in trace, logs, and metrics.

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