From c7360f537d7f1cf1de6cae453eff315b21d51fa5 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Tue, 12 May 2026 20:33:18 +0900 Subject: [PATCH 01/12] fix: apply filtering in emit log record --- .github/workflows/clang-tidy.yaml | 4 +- .github/workflows/iwyu.yml | 6 +- CHANGELOG.md | 8 +++ api/include/opentelemetry/logs/logger.h | 15 +++++ .../opentelemetry/logs/logger_type_traits.h | 20 ++++++ api/test/logs/logger_test.cc | 20 ++++-- sdk/src/logs/logger.cc | 7 +- sdk/test/logs/logger_sdk_test.cc | 66 +++++++++++++++++-- 8 files changed, 131 insertions(+), 15 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index d53fc08577..f7dde52cd6 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 418 + warning_limit: 416 - cmake_options: all-options-abiv2-preview - warning_limit: 424 + warning_limit: 422 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index 3bfff341f2..1e9d565ea4 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -18,11 +18,11 @@ jobs: matrix: include: - cmake_options: all-options-abiv1 - warning_limit: 161 + warning_limit: 160 - cmake_options: all-options-abiv1-preview - warning_limit: 190 + warning_limit: 189 - cmake_options: all-options-abiv2-preview - warning_limit: 187 + warning_limit: 186 steps: - name: Harden the runner (Audit all outbound calls) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d918c931e..f53206c102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,14 @@ Increment the: ## [Unreleased] +* [API] `Logger::EmitLogRecord(...)` templates now short-circuit when the + supplied Severity is below the logger's minimum. Closes the second half of + #2667 so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers honor + the `Enabled()` flag transparently. +* [SDK] `Logger::EmitLogRecord(unique_ptr)` now applies the + LoggerConfig trace-based filter using the current runtime context. + [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) + * [API] Fix `Logger::Enabled()` [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index 10e3f7a528..c8237091a7 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -83,6 +83,12 @@ class Logger return; } + const Severity arg_severity = detail::FindSeverityInArgs(args...); + if (arg_severity != Severity::kInvalid && !Enabled(arg_severity)) + { + return; + } + // // Keep the parameter pack unpacking order from left to right because left // ones are usually more important like severity and event_id than the @@ -124,6 +130,15 @@ class Logger template void EmitLogRecord(ArgumentType &&...args) { + // Short-circuit BEFORE record creation when Severity is supplied in args + // and below the logger's minimum severity. Saves the recordable alloc on + // filtered logs. + const Severity arg_severity = detail::FindSeverityInArgs(args...); + if (arg_severity != Severity::kInvalid && !Enabled(arg_severity)) + { + return; + } + nostd::unique_ptr log_record = CreateLogRecord(); EmitLogRecord(std::move(log_record), std::forward(args)...); diff --git a/api/include/opentelemetry/logs/logger_type_traits.h b/api/include/opentelemetry/logs/logger_type_traits.h index 19e0e3219b..3e11e63ed5 100644 --- a/api/include/opentelemetry/logs/logger_type_traits.h +++ b/api/include/opentelemetry/logs/logger_type_traits.h @@ -198,6 +198,26 @@ struct LogRecordHasType LogRecordHasType>::type {}; +inline Severity FindSeverityInArgs() noexcept +{ + return Severity::kInvalid; +} + +template +inline Severity FindSeverityInArgs(Severity severity, Rest &&.../*rest*/) noexcept +{ + return severity; +} + +template ::type, Severity>::value, + int>::type = 0> +inline Severity FindSeverityInArgs(First && /*first*/, Rest &&...rest) noexcept +{ + return FindSeverityInArgs(std::forward(rest)...); +} + } // namespace detail } // namespace logs diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 251820477b..6bc0d2e238 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -10,7 +10,6 @@ #include #include "opentelemetry/common/attribute_value.h" -#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/common/key_value_iterable_view.h" #include "opentelemetry/common/timestamp.h" #include "opentelemetry/logs/event_id.h" @@ -26,9 +25,7 @@ #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/trace_flags.h" -#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/nostd/utility.h" #if OPENTELEMETRY_ABI_VERSION_NO >= 2 # include "opentelemetry/context/context.h" @@ -278,6 +275,9 @@ class TestLogger : public Logger } }; +namespace +{ + class EnablementAwareTestLogRecord : public opentelemetry::logs::LogRecord { public: @@ -420,6 +420,8 @@ class EnablementAwareTestLogger : public Logger bool event_id_enabled_; }; +} // namespace + // Define a basic LoggerProvider class that returns an instance of the logger class defined above class TestProvider : public LoggerProvider { @@ -461,3 +463,13 @@ TEST(Logger, EnabledWithExplicitContextUsesContextAwareImplementation) EXPECT_TRUE(logger.last_enabled_context_test_key_value_); } #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + +TEST(Logger, EmitLogRecordTemplateShortCircuitsBelowMinimumSeverity) +{ + EnablementAwareTestLogger logger(Severity::kWarn); + + logger.Info(nostd::string_view{"filtered"}); + + EXPECT_EQ(logger.create_log_record_calls_, 0u); + EXPECT_EQ(logger.emit_log_record_calls_, 0u); +} diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 3706ee1351..38a7ca95af 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -156,6 +156,11 @@ void Logger::EmitLogRecord( return; } + if (!IsAllowedByTraceBasedFiltering(context::RuntimeContext::GetCurrent(), logger_config_)) + { + return; + } + std::unique_ptr recordable = std::unique_ptr(static_cast(log_record.release())); recordable->SetResource(context_->GetResource()); @@ -163,8 +168,6 @@ void Logger::EmitLogRecord( auto &processor = context_->GetProcessor(); - // TODO: Sampler (should include check for minSeverity) - // Send the log recordable to the processor processor.OnEmit(std::move(recordable)); } diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index ba2fe07ba4..4ed8ac62d6 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -21,6 +21,7 @@ #include "opentelemetry/logs/noop.h" #include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" @@ -44,13 +45,13 @@ #include "opentelemetry/trace/trace_id.h" #include "opentelemetry/trace/tracer.h" +#include "opentelemetry/trace/default_span.h" + #if OPENTELEMETRY_ABI_VERSION_NO >= 2 # include # include "opentelemetry/context/context.h" -# include "opentelemetry/nostd/span.h" # include "opentelemetry/trace/context.h" -# include "opentelemetry/trace/default_span.h" #endif using namespace opentelemetry::sdk::logs; @@ -61,7 +62,6 @@ namespace context = opentelemetry::context; namespace logs_api = opentelemetry::logs; namespace nostd = opentelemetry::nostd; -#if OPENTELEMETRY_ABI_VERSION_NO >= 2 namespace { nostd::shared_ptr MakeTestSpan(bool sampled) @@ -81,6 +81,7 @@ nostd::shared_ptr MakeTestSpan(bool sampled) new opentelemetry::trace::DefaultSpan(span_context)); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 context::Context MakeContextWithUnsampledSpanAndInvalidTraceId() { const uint8_t span_id_bytes[opentelemetry::trace::SpanId::kSize] = {0xde, 0xad, 0xbe, 0xef, @@ -95,8 +96,8 @@ context::Context MakeContextWithUnsampledSpanAndInvalidTraceId() nostd::shared_ptr( new opentelemetry::trace::DefaultSpan(span_context))); } -} // namespace #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 +} // namespace TEST(LoggerSDK, LogToNullProcessor) { @@ -273,6 +274,9 @@ class MockProcessor final : public LogRecordProcessor }; #if OPENTELEMETRY_ABI_VERSION_NO >= 2 +namespace +{ + struct EnabledProcessorCallState { logs_api::Severity severity = logs_api::Severity::kInvalid; @@ -335,6 +339,8 @@ class EnablementAwareProcessor final : public LogRecordProcessor bool enabled_; std::shared_ptr call_state_; }; + +} // namespace #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 TEST(LoggerSDK, LogToAProcessor) @@ -480,6 +486,58 @@ TEST(LoggerSDK, LoggerWithEnabledConfig) ASSERT_FALSE(shared_recordable->GetSpanId().IsValid()); } +TEST(LoggerSDK, EmitLogRecordDropsUnsampledTraceBased) +{ + ScopeConfigurator trace_based = + ScopeConfigurator::Builder( + LoggerConfig::Create(true, logs_api::Severity::kInvalid, true)) + .Build(); + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto log_processor = std::unique_ptr(new MockProcessor(shared_recordable)); + + const auto resource = opentelemetry::sdk::resource::Resource::Create({}); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto api_lp = std::shared_ptr( + new LoggerProvider(std::move(log_processor), resource, + std::make_unique>(trace_based))); + auto logger = api_lp->GetLogger("trace-based-logger", "opentelemetry_library", "", schema_url); + + auto unsampled_span = MakeTestSpan(false); + opentelemetry::trace::Scope scope{unsampled_span}; + + logger->EmitLogRecord(logs_api::Severity::kInfo, "dropped"); + + // Record never reached MockProcessor::OnEmit, so shared_recordable stays default. + EXPECT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kInvalid); + EXPECT_EQ(shared_recordable->GetBody(), ""); + EXPECT_EQ(shared_recordable->GetObservedTimestamp(), std::chrono::system_clock::from_time_t(0)); +} + +TEST(LoggerSDK, EmitLogRecordEmitsSampledTraceBased) +{ + ScopeConfigurator trace_based = + ScopeConfigurator::Builder( + LoggerConfig::Create(true, logs_api::Severity::kInvalid, true)) + .Build(); + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto log_processor = std::unique_ptr(new MockProcessor(shared_recordable)); + + const auto resource = opentelemetry::sdk::resource::Resource::Create({}); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto api_lp = std::shared_ptr( + new LoggerProvider(std::move(log_processor), resource, + std::make_unique>(trace_based))); + auto logger = api_lp->GetLogger("trace-based-logger", "opentelemetry_library", "", schema_url); + + auto sampled_span = MakeTestSpan(true); + opentelemetry::trace::Scope scope{sampled_span}; + + logger->EmitLogRecord(logs_api::Severity::kInfo, "allowed"); + + EXPECT_EQ(shared_recordable->GetSeverity(), logs_api::Severity::kInfo); + EXPECT_EQ(shared_recordable->GetBody(), "allowed"); +} + #if OPENTELEMETRY_ABI_VERSION_NO >= 2 TEST(LoggerSDK, LoggerWithMinimumSeverityConfig) { From 46c13c87b7cb25ba8054dce05fc3ca456a804f31 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Tue, 12 May 2026 20:57:10 +0900 Subject: [PATCH 02/12] refactor: filter once --- api/include/opentelemetry/logs/logger.h | 9 --------- api/test/logs/logger_test.cc | 2 +- sdk/test/logs/logger_sdk_test.cc | 1 - 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index c8237091a7..b45636defc 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -130,15 +130,6 @@ class Logger template void EmitLogRecord(ArgumentType &&...args) { - // Short-circuit BEFORE record creation when Severity is supplied in args - // and below the logger's minimum severity. Saves the recordable alloc on - // filtered logs. - const Severity arg_severity = detail::FindSeverityInArgs(args...); - if (arg_severity != Severity::kInvalid && !Enabled(arg_severity)) - { - return; - } - nostd::unique_ptr log_record = CreateLogRecord(); EmitLogRecord(std::move(log_record), std::forward(args)...); diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 6bc0d2e238..d6613d2a0b 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -470,6 +470,6 @@ TEST(Logger, EmitLogRecordTemplateShortCircuitsBelowMinimumSeverity) logger.Info(nostd::string_view{"filtered"}); - EXPECT_EQ(logger.create_log_record_calls_, 0u); + EXPECT_EQ(logger.create_log_record_calls_, 1u); EXPECT_EQ(logger.emit_log_record_calls_, 0u); } diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index 4ed8ac62d6..b972c8f140 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -507,7 +507,6 @@ TEST(LoggerSDK, EmitLogRecordDropsUnsampledTraceBased) logger->EmitLogRecord(logs_api::Severity::kInfo, "dropped"); - // Record never reached MockProcessor::OnEmit, so shared_recordable stays default. EXPECT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kInvalid); EXPECT_EQ(shared_recordable->GetBody(), ""); EXPECT_EQ(shared_recordable->GetObservedTimestamp(), std::chrono::system_clock::from_time_t(0)); From 47d1e37f37cb378c2e0ed1040189fbbb42ed21a0 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Wed, 13 May 2026 19:30:12 +0900 Subject: [PATCH 03/12] refactor: enabled filtering on the API level --- api/include/opentelemetry/logs/logger.h | 29 ++++++++--- .../opentelemetry/logs/logger_type_traits.h | 20 ++++++++ api/test/logs/logger_test.cc | 40 +++++++++++++++ sdk/src/logs/logger.cc | 5 -- sdk/test/logs/log_record_test.cc | 14 +++++ sdk/test/logs/logger_sdk_test.cc | 51 ------------------- 6 files changed, 96 insertions(+), 63 deletions(-) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index b45636defc..bb286292b0 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -58,7 +58,15 @@ class Logger virtual void EmitLogRecord(nostd::unique_ptr &&log_record) noexcept = 0; /** - * Emit a Log Record object with arguments + * Emit a Log Record object with arguments. + * + * @note This overload does NOT apply the @c Enabled filter chain. Callers who + * constructed @p log_record themselves are responsible for calling + * @c Enabled(severity, ...) before invoking this overload if they want + * the LoggerConfig filtering rules (minimum severity, trace-based, + * processor.Enabled) to be honored. The no-record overload + * @c EmitLogRecord(args...) below does call the filter chain + * automatically when @c Severity is present in @p args. * * @param log_record Log record * @param args Arguments which can be used to set data of log record by type. @@ -83,12 +91,6 @@ class Logger return; } - const Severity arg_severity = detail::FindSeverityInArgs(args...); - if (arg_severity != Severity::kInvalid && !Enabled(arg_severity)) - { - return; - } - // // Keep the parameter pack unpacking order from left to right because left // ones are usually more important like severity and event_id than the @@ -130,6 +132,19 @@ class Logger template void EmitLogRecord(ArgumentType &&...args) { + const Severity arg_severity = detail::FindSeverityInArgs(args...); + if (arg_severity != Severity::kInvalid) + { + const EventId *event_id_ptr = detail::FindEventIdInArgs(args...); + const bool is_enabled = event_id_ptr + ? Enabled(arg_severity, *event_id_ptr) + : Enabled(arg_severity, static_cast(0)); + if (!is_enabled) + { + return; + } + } + nostd::unique_ptr log_record = CreateLogRecord(); EmitLogRecord(std::move(log_record), std::forward(args)...); diff --git a/api/include/opentelemetry/logs/logger_type_traits.h b/api/include/opentelemetry/logs/logger_type_traits.h index 3e11e63ed5..25a76db2d0 100644 --- a/api/include/opentelemetry/logs/logger_type_traits.h +++ b/api/include/opentelemetry/logs/logger_type_traits.h @@ -218,6 +218,26 @@ inline Severity FindSeverityInArgs(First && /*first*/, Rest &&...rest) noexcept return FindSeverityInArgs(std::forward(rest)...); } +inline const EventId *FindEventIdInArgs() noexcept +{ + return nullptr; +} + +template +inline const EventId *FindEventIdInArgs(const EventId &event_id, Rest &&... /*rest*/) noexcept +{ + return &event_id; +} + +template ::type, EventId>::value, + int>::type = 0> +inline const EventId *FindEventIdInArgs(First && /*first*/, Rest &&...rest) noexcept +{ + return FindEventIdInArgs(std::forward(rest)...); +} + } // namespace detail } // namespace logs diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index d6613d2a0b..4cb9326819 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -470,6 +470,46 @@ TEST(Logger, EmitLogRecordTemplateShortCircuitsBelowMinimumSeverity) logger.Info(nostd::string_view{"filtered"}); + EXPECT_EQ(logger.create_log_record_calls_, 0u); + EXPECT_EQ(logger.emit_log_record_calls_, 0u); + EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); +} + +TEST(Logger, EmitLogRecordTemplateInvokesEnabledImplementationAndEmitsWhenAllowed) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); + + logger.Info(nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_with_event_id_calls_, 1u); EXPECT_EQ(logger.create_log_record_calls_, 1u); + EXPECT_EQ(logger.emit_log_record_calls_, 1u); + EXPECT_EQ(logger.last_enabled_severity_, Severity::kInfo); +} + +TEST(Logger, EmitLogRecordTemplateShortCircuitsWhenEnabledImplementationReturnsFalse) +{ + EnablementAwareTestLogger logger(Severity::kTrace, false); + + logger.Info(nostd::string_view{"filtered"}); + + EXPECT_EQ(logger.enabled_with_event_id_calls_, 1u); + EXPECT_EQ(logger.create_log_record_calls_, 0u); EXPECT_EQ(logger.emit_log_record_calls_, 0u); } + +TEST(Logger, EmitLogRecordWithRecordBypassesFiltering) +{ + // EmitLogRecord(unique_ptr&&, args...) intentionally does NOT + // route through Enabled() — caller built the record themselves and is + // responsible for any filtering. Lock the contract documented on the + // overload. + EnablementAwareTestLogger logger(Severity::kTrace, false); + + auto record = logger.CreateLogRecord(); + logger.EmitLogRecord(std::move(record), Severity::kInfo, nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); + EXPECT_EQ(logger.create_log_record_calls_, 1u); + EXPECT_EQ(logger.emit_log_record_calls_, 1u); +} diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 38a7ca95af..6e3a6c9936 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -156,11 +156,6 @@ void Logger::EmitLogRecord( return; } - if (!IsAllowedByTraceBasedFiltering(context::RuntimeContext::GetCurrent(), logger_config_)) - { - return; - } - std::unique_ptr recordable = std::unique_ptr(static_cast(log_record.release())); recordable->SetResource(context_->GetResource()); diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc index 57a211b290..bd62fabe92 100644 --- a/sdk/test/logs/log_record_test.cc +++ b/sdk/test/logs/log_record_test.cc @@ -117,6 +117,20 @@ class TestBodyLogger : public opentelemetry::logs::Logger return last_body_; } +protected: + bool EnabledImplementation(opentelemetry::logs::Severity /*severity*/, + int64_t /*event_id*/) const noexcept override + { + return true; + } + + bool EnabledImplementation( + opentelemetry::logs::Severity /*severity*/, + const opentelemetry::logs::EventId & /*event_id*/) const noexcept override + { + return true; + } + private: opentelemetry::sdk::common::OwnedAttributeValue last_body_; }; diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index b972c8f140..5bd17bbf85 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -486,57 +486,6 @@ TEST(LoggerSDK, LoggerWithEnabledConfig) ASSERT_FALSE(shared_recordable->GetSpanId().IsValid()); } -TEST(LoggerSDK, EmitLogRecordDropsUnsampledTraceBased) -{ - ScopeConfigurator trace_based = - ScopeConfigurator::Builder( - LoggerConfig::Create(true, logs_api::Severity::kInvalid, true)) - .Build(); - auto shared_recordable = std::shared_ptr(new MockLogRecordable()); - auto log_processor = std::unique_ptr(new MockProcessor(shared_recordable)); - - const auto resource = opentelemetry::sdk::resource::Resource::Create({}); - const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; - auto api_lp = std::shared_ptr( - new LoggerProvider(std::move(log_processor), resource, - std::make_unique>(trace_based))); - auto logger = api_lp->GetLogger("trace-based-logger", "opentelemetry_library", "", schema_url); - - auto unsampled_span = MakeTestSpan(false); - opentelemetry::trace::Scope scope{unsampled_span}; - - logger->EmitLogRecord(logs_api::Severity::kInfo, "dropped"); - - EXPECT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kInvalid); - EXPECT_EQ(shared_recordable->GetBody(), ""); - EXPECT_EQ(shared_recordable->GetObservedTimestamp(), std::chrono::system_clock::from_time_t(0)); -} - -TEST(LoggerSDK, EmitLogRecordEmitsSampledTraceBased) -{ - ScopeConfigurator trace_based = - ScopeConfigurator::Builder( - LoggerConfig::Create(true, logs_api::Severity::kInvalid, true)) - .Build(); - auto shared_recordable = std::shared_ptr(new MockLogRecordable()); - auto log_processor = std::unique_ptr(new MockProcessor(shared_recordable)); - - const auto resource = opentelemetry::sdk::resource::Resource::Create({}); - const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; - auto api_lp = std::shared_ptr( - new LoggerProvider(std::move(log_processor), resource, - std::make_unique>(trace_based))); - auto logger = api_lp->GetLogger("trace-based-logger", "opentelemetry_library", "", schema_url); - - auto sampled_span = MakeTestSpan(true); - opentelemetry::trace::Scope scope{sampled_span}; - - logger->EmitLogRecord(logs_api::Severity::kInfo, "allowed"); - - EXPECT_EQ(shared_recordable->GetSeverity(), logs_api::Severity::kInfo); - EXPECT_EQ(shared_recordable->GetBody(), "allowed"); -} - #if OPENTELEMETRY_ABI_VERSION_NO >= 2 TEST(LoggerSDK, LoggerWithMinimumSeverityConfig) { From a0958a46a7fbc308b60b4eba554b8811bd64adfa Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Thu, 14 May 2026 08:13:39 +0000 Subject: [PATCH 04/12] feat: use context for EmitLogRecord --- api/include/opentelemetry/logs/logger.h | 38 ++++++++++++-- .../opentelemetry/logs/logger_type_traits.h | 47 ++++++++++++++++- api/test/logs/logger_test.cc | 44 ++++++++++++++++ sdk/include/opentelemetry/sdk/logs/logger.h | 5 ++ sdk/src/logs/logger.cc | 50 +++++++++++++++++++ sdk/test/logs/log_record_test.cc | 2 +- sdk/test/logs/logger_sdk_test.cc | 7 ++- 7 files changed, 184 insertions(+), 9 deletions(-) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index bb286292b0..4d427a655b 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -50,6 +50,20 @@ class Logger */ virtual nostd::unique_ptr CreateLogRecord() noexcept = 0; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + /** + * Create a Log Record object using given context. + * + * @param context Context which carries execution-scoped values across execution unit. + * @return nostd::unique_ptr + */ + virtual nostd::unique_ptr CreateLogRecord( + const opentelemetry::context::Context & /*context*/) noexcept + { + return CreateLogRecord(); + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + /** * Emit a Log Record object * @@ -128,24 +142,42 @@ class Logger * KeyValueIterable -> attributes * Key value iterable container -> attributes * span> -> attributes(return type of MakeAttributes) + * Context (v2 only) -> filter + trace stamp (recommended: pass last) + * */ template void EmitLogRecord(ArgumentType &&...args) { +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + const opentelemetry::context::Context *context_ptr = detail::FindContextInArgs(args...); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + const Severity arg_severity = detail::FindSeverityInArgs(args...); if (arg_severity != Severity::kInvalid) { const EventId *event_id_ptr = detail::FindEventIdInArgs(args...); - const bool is_enabled = event_id_ptr - ? Enabled(arg_severity, *event_id_ptr) - : Enabled(arg_severity, static_cast(0)); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + const bool is_enabled = + context_ptr ? (event_id_ptr ? Enabled(*context_ptr, arg_severity, *event_id_ptr) + : Enabled(*context_ptr, arg_severity)) + : (event_id_ptr ? Enabled(arg_severity, *event_id_ptr) + : Enabled(arg_severity, static_cast(0))); +#else + const bool is_enabled = event_id_ptr ? Enabled(arg_severity, *event_id_ptr) + : Enabled(arg_severity, static_cast(0)); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 if (!is_enabled) { return; } } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::unique_ptr log_record = + context_ptr ? CreateLogRecord(*context_ptr) : CreateLogRecord(); +#else nostd::unique_ptr log_record = CreateLogRecord(); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 EmitLogRecord(std::move(log_record), std::forward(args)...); } diff --git a/api/include/opentelemetry/logs/logger_type_traits.h b/api/include/opentelemetry/logs/logger_type_traits.h index 25a76db2d0..a419c1485c 100644 --- a/api/include/opentelemetry/logs/logger_type_traits.h +++ b/api/include/opentelemetry/logs/logger_type_traits.h @@ -21,6 +21,10 @@ #include "opentelemetry/trace/trace_id.h" #include "opentelemetry/version.h" +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +# include "opentelemetry/context/context.h" +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + OPENTELEMETRY_BEGIN_NAMESPACE namespace logs { @@ -143,6 +147,21 @@ struct LogRecordSetterTrait } }; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +// Context in the argument list is consumed by EmitLogRecord(args...) for +// context-aware filtering and trace stamping (see FindContextInArgs); it is +// not a record field, so the setter is a no-op. +template <> +struct LogRecordSetterTrait +{ + template + inline static LogRecord *Set(LogRecord *log_record, ArgumentType && /*arg*/) noexcept + { + return log_record; + } +}; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + template struct LogRecordSetterTrait { @@ -224,7 +243,7 @@ inline const EventId *FindEventIdInArgs() noexcept } template -inline const EventId *FindEventIdInArgs(const EventId &event_id, Rest &&... /*rest*/) noexcept +inline const EventId *FindEventIdInArgs(const EventId &event_id, Rest &&.../*rest*/) noexcept { return &event_id; } @@ -238,6 +257,32 @@ inline const EventId *FindEventIdInArgs(First && /*first*/, Rest &&...rest) noex return FindEventIdInArgs(std::forward(rest)...); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +inline const opentelemetry::context::Context *FindContextInArgs() noexcept +{ + return nullptr; +} + +template +inline const opentelemetry::context::Context *FindContextInArgs( + const opentelemetry::context::Context &context, + Rest &&.../*rest*/) noexcept +{ + return &context; +} + +template ::type, + opentelemetry::context::Context>::value, + int>::type = 0> +inline const opentelemetry::context::Context *FindContextInArgs(First && /*first*/, + Rest &&...rest) noexcept +{ + return FindContextInArgs(std::forward(rest)...); +} +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + } // namespace detail } // namespace logs diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 4cb9326819..05e831b961 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -334,6 +334,15 @@ class EnablementAwareTestLogger : public Logger return nostd::unique_ptr(new EnablementAwareTestLogRecord()); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::unique_ptr CreateLogRecord( + const context::Context & /*context*/) noexcept override + { + ++create_log_record_context_calls_; + return CreateLogRecord(); + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + using Logger::EmitLogRecord; void EmitLogRecord( @@ -359,6 +368,9 @@ class EnablementAwareTestLogger : public Logger mutable bool last_enabled_context_has_test_key_{false}; mutable bool last_enabled_context_test_key_value_{false}; nostd::unique_ptr last_emitted_record_; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + size_t create_log_record_context_calls_{0}; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 protected: #if OPENTELEMETRY_ABI_VERSION_NO >= 2 @@ -513,3 +525,35 @@ TEST(Logger, EmitLogRecordWithRecordBypassesFiltering) EXPECT_EQ(logger.create_log_record_calls_, 1u); EXPECT_EQ(logger.emit_log_record_calls_, 1u); } + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +TEST(Logger, EmitLogRecordWithContextInArgsRoutesThroughContextAwareEnabledAndEmits) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); + + context::Context test_context{"test-key", true}; + + logger.EmitLogRecord(Severity::kInfo, EventId{0x42, "info"}, nostd::string_view{"emitted"}, + test_context); + + EXPECT_EQ(logger.enabled_with_event_id_calls_, 1u); + EXPECT_EQ(logger.create_log_record_context_calls_, 1u); + EXPECT_EQ(logger.emit_log_record_calls_, 1u); + EXPECT_TRUE(logger.last_enabled_context_has_test_key_); + EXPECT_TRUE(logger.last_enabled_context_test_key_value_); +} + +TEST(Logger, EmitLogRecordWithContextInArgsShortCircuitsWhenEnabledImplementationReturnsFalse) +{ + EnablementAwareTestLogger logger(Severity::kTrace, false); + + context::Context test_context{"test-key", true}; + + logger.EmitLogRecord(Severity::kInfo, EventId{0x42, "info"}, nostd::string_view{"filtered"}, + test_context); + + EXPECT_EQ(logger.enabled_with_event_id_calls_, 1u); + EXPECT_EQ(logger.create_log_record_context_calls_, 0u); + EXPECT_EQ(logger.emit_log_record_calls_, 0u); +} +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index a77d80caa8..098b044df0 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -45,6 +45,11 @@ class Logger final : public opentelemetry::logs::Logger nostd::unique_ptr CreateLogRecord() noexcept override; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::unique_ptr CreateLogRecord( + const opentelemetry::context::Context &context) noexcept override; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + using opentelemetry::logs::Logger::EmitLogRecord; void EmitLogRecord( diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 6e3a6c9936..981d563ded 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -143,6 +143,56 @@ opentelemetry::nostd::unique_ptr Logger::CreateL return opentelemetry::nostd::unique_ptr(recordable.release()); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +opentelemetry::nostd::unique_ptr Logger::CreateLogRecord( + const opentelemetry::context::Context &context) noexcept +{ + if (!logger_config_.IsEnabled()) + { + return kNoopLogger.CreateLogRecord(); + } + + auto recordable = context_->GetProcessor().MakeRecordable(); + + recordable->SetObservedTimestamp(std::chrono::system_clock::now()); + + // Get the span metadata from the supplied context + if (context.HasKey(trace_api::kSpanKey)) + { + const opentelemetry::context::ContextValue context_value = + context.GetValue(trace_api::kSpanKey); + + const trace_api::SpanContext span_context = [&context_value]() { + // Get the span metadata from the active span in the supplied context + if (const nostd::shared_ptr *maybe_span = + nostd::get_if>(&context_value)) + { + const nostd::shared_ptr &span = *maybe_span; + return span->GetContext(); + } + // Get the span metadata directly from a SpanContext in the supplied context. + // TODO: This path is unused and may be removed in the future. + else if (const nostd::shared_ptr *maybe_span_context = + nostd::get_if>(&context_value)) + { + const nostd::shared_ptr &span_context = *maybe_span_context; + return *span_context; + } + return trace_api::SpanContext::GetInvalid(); + }(); + + if (span_context.IsValid()) + { + recordable->SetTraceId(span_context.trace_id()); + recordable->SetTraceFlags(span_context.trace_flags()); + recordable->SetSpanId(span_context.span_id()); + } + } + + return opentelemetry::nostd::unique_ptr(recordable.release()); +} +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + void Logger::EmitLogRecord( opentelemetry::nostd::unique_ptr &&log_record) noexcept { diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc index bd62fabe92..4f9d7addc2 100644 --- a/sdk/test/logs/log_record_test.cc +++ b/sdk/test/logs/log_record_test.cc @@ -11,7 +11,6 @@ #include #include -#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/common/timestamp.h" #include "opentelemetry/logs/log_record.h" #include "opentelemetry/logs/logger.h" @@ -21,6 +20,7 @@ #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/nostd/utility.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/common/attribute_utils.h" #include "opentelemetry/sdk/logs/read_write_log_record.h" diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index 5bd17bbf85..460d7a86f2 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -21,7 +21,6 @@ #include "opentelemetry/logs/noop.h" #include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/shared_ptr.h" -#include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" @@ -45,13 +44,13 @@ #include "opentelemetry/trace/trace_id.h" #include "opentelemetry/trace/tracer.h" -#include "opentelemetry/trace/default_span.h" - #if OPENTELEMETRY_ABI_VERSION_NO >= 2 # include # include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/span.h" # include "opentelemetry/trace/context.h" +# include "opentelemetry/trace/default_span.h" #endif using namespace opentelemetry::sdk::logs; @@ -64,6 +63,7 @@ namespace nostd = opentelemetry::nostd; namespace { +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 nostd::shared_ptr MakeTestSpan(bool sampled) { const uint8_t trace_id_bytes[opentelemetry::trace::TraceId::kSize] = { @@ -81,7 +81,6 @@ nostd::shared_ptr MakeTestSpan(bool sampled) new opentelemetry::trace::DefaultSpan(span_context)); } -#if OPENTELEMETRY_ABI_VERSION_NO >= 2 context::Context MakeContextWithUnsampledSpanAndInvalidTraceId() { const uint8_t span_id_bytes[opentelemetry::trace::SpanId::kSize] = {0xde, 0xad, 0xbe, 0xef, From 98b3e9c67e60067c239e022251a1ed3405c1a339 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Thu, 14 May 2026 17:20:06 +0900 Subject: [PATCH 05/12] doc: update changelog --- CHANGELOG.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06a1e4b1e9..f59db478d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,14 @@ Increment the: ## [Unreleased] -* [API] `Logger::EmitLogRecord(...)` templates now short-circuit when the - supplied Severity is below the logger's minimum. Closes the second half of - #2667 so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers honor - the `Enabled()` flag transparently. -* [SDK] `Logger::EmitLogRecord(unique_ptr)` now applies the - LoggerConfig trace-based filter using the current runtime context. +* [API] `Logger::EmitLogRecord(...)` templates now apply the `Enabled` filter + chain when a `Severity` is in args, so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers + honor the `Enabled()` flag transparently. Closes the second half of #2667. +* [API/SDK] (ABI v2) Add `Logger::CreateLogRecord(const Context &)` virtual + for explicit-context record creation. `Logger::EmitLogRecord(args...)` + also detects a `Context` in args and routes filtering through + `Enabled(context, severity, ...)` plus trace stamping through + `CreateLogRecord(context)`. [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) ## [1.27.0] 2026-05-13 From 05b865b2e8f1d9e492a663eba440ff02a6f36a1b Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Thu, 14 May 2026 17:22:19 +0900 Subject: [PATCH 06/12] style: follow doc lint --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f59db478d5..fc8b57da08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ Increment the: ## [Unreleased] * [API] `Logger::EmitLogRecord(...)` templates now apply the `Enabled` filter - chain when a `Severity` is in args, so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers + chain when a `Severity` is in args, so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers honor the `Enabled()` flag transparently. Closes the second half of #2667. * [API/SDK] (ABI v2) Add `Logger::CreateLogRecord(const Context &)` virtual for explicit-context record creation. `Logger::EmitLogRecord(args...)` From 8063cb6e10eae9e84d16b51ecb059b63ef847c4f Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Thu, 14 May 2026 17:25:13 +0900 Subject: [PATCH 07/12] doc: follow doc lint --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8b57da08..a2338d72f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,9 @@ Increment the: ## [Unreleased] * [API] `Logger::EmitLogRecord(...)` templates now apply the `Enabled` filter - chain when a `Severity` is in args, so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers - honor the `Enabled()` flag transparently. Closes the second half of #2667. + chain when a `Severity` is in args, + so the `Trace`/`Debug`/`Info`/`Warn`/`Error`/`Fatal` helpers honor the `Enabled()` + flag transparently. Closes the second half of #2667. * [API/SDK] (ABI v2) Add `Logger::CreateLogRecord(const Context &)` virtual for explicit-context record creation. `Logger::EmitLogRecord(args...)` also detects a `Context` in args and routes filtering through From b9871a4cad3cc0da5f5d8b3e63fb3901199705e6 Mon Sep 17 00:00:00 2001 From: proost Date: Fri, 15 May 2026 00:11:17 +0900 Subject: [PATCH 08/12] style: follow doc lint --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ff370d917..668fd28242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ Increment the: also detects a `Context` in args and routes filtering through `Enabled(context, severity, ...)` plus trace stamping through `CreateLogRecord(context)`. - [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) + [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) ## [1.27.0] 2026-05-13 From 5e676808e1d841b9f5f085f75806d8b36c2222f9 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Fri, 15 May 2026 16:00:31 +0900 Subject: [PATCH 09/12] perf: cached enabled required --- .github/workflows/clang-tidy.yaml | 2 +- api/include/opentelemetry/logs/logger.h | 50 +++++++++++++++++++------ sdk/src/logs/logger.cc | 2 + 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index f7dde52cd6..1ab4fe2543 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -19,7 +19,7 @@ jobs: - cmake_options: all-options-abiv1-preview warning_limit: 416 - cmake_options: all-options-abiv2-preview - warning_limit: 422 + warning_limit: 420 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index 4d427a655b..a1c265896b 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -155,20 +155,30 @@ class Logger const Severity arg_severity = detail::FindSeverityInArgs(args...); if (arg_severity != Severity::kInvalid) { - const EventId *event_id_ptr = detail::FindEventIdInArgs(args...); + if (!Enabled(arg_severity)) + { + return; + } + + if (ExtendedEnabledRequired()) + { + const EventId *event_id_ptr = detail::FindEventIdInArgs(args...); #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - const bool is_enabled = - context_ptr ? (event_id_ptr ? Enabled(*context_ptr, arg_severity, *event_id_ptr) - : Enabled(*context_ptr, arg_severity)) - : (event_id_ptr ? Enabled(arg_severity, *event_id_ptr) - : Enabled(arg_severity, static_cast(0))); + const bool extended_enabled = + context_ptr + ? (event_id_ptr ? EnabledImplementation(*context_ptr, arg_severity, *event_id_ptr) + : EnabledImplementation(*context_ptr, arg_severity)) + : (event_id_ptr ? EnabledImplementation(arg_severity, *event_id_ptr) + : EnabledImplementation(arg_severity, static_cast(0))); #else - const bool is_enabled = event_id_ptr ? Enabled(arg_severity, *event_id_ptr) - : Enabled(arg_severity, static_cast(0)); + const bool extended_enabled = + event_id_ptr ? EnabledImplementation(arg_severity, *event_id_ptr) + : EnabledImplementation(arg_severity, static_cast(0)); #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 - if (!is_enabled) - { - return; + if (!extended_enabled) + { + return; + } } } @@ -376,6 +386,11 @@ class Logger return static_cast(severity) >= OPENTELEMETRY_ATOMIC_READ_8(&minimum_severity_); } + inline bool ExtendedEnabledRequired() const noexcept + { + return OPENTELEMETRY_ATOMIC_READ_8(&extended_enabled_required_) != 0; + } + /** * Log an event * @@ -577,6 +592,12 @@ class Logger OPENTELEMETRY_ATOMIC_WRITE_8(&minimum_severity_, severity_or_max); } + void SetExtendedEnabledRequired(bool required) noexcept + { + OPENTELEMETRY_ATOMIC_WRITE_8(&extended_enabled_required_, + static_cast(required ? 1 : 0)); + } + private: template void IgnoreTraitResult(ValueType &&...) @@ -588,6 +609,13 @@ class Logger // compatible for OpenTelemetry C++ API. // mutable uint8_t minimum_severity_{kMaxSeverity}; + + // + // extended_enabled_required_ defaults to 1 (full chain required) so subclasses that override + // EnabledImplementation keep their existing semantics until they explicitly opt out via + // SetExtendedEnabledRequired(false). Same atomicity rationale as minimum_severity_. + // + mutable uint8_t extended_enabled_required_{1}; }; } // namespace logs OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 981d563ded..366b26e925 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -84,6 +84,8 @@ Logger::Logger( SetMinimumSeverity(logger_config_.IsEnabled() ? static_cast(logger_config_.GetMinimumSeverity()) : opentelemetry::logs::kMaxSeverity); + + SetExtendedEnabledRequired(logger_config_.IsTraceBased()); } const opentelemetry::nostd::string_view Logger::GetName() noexcept From dc306d58f01085c8a6dfdb2560ebb0d03d61a2ee Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Sun, 17 May 2026 00:26:28 +0900 Subject: [PATCH 10/12] feat: has enabled filter --- CHANGELOG.md | 8 ++++++++ api/test/logs/logger_test.cc | 14 ++++++++++++++ .../sdk/logs/batch_log_record_processor.h | 2 ++ .../sdk/logs/multi_log_record_processor.h | 2 ++ sdk/include/opentelemetry/sdk/logs/processor.h | 8 ++++++++ .../sdk/logs/simple_log_record_processor.h | 2 ++ sdk/src/logs/batch_log_record_processor.cc | 1 - sdk/src/logs/logger.cc | 3 ++- sdk/src/logs/multi_log_record_processor.cc | 14 ++++++++++++-- 9 files changed, 50 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 668fd28242..ef4472354f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,14 @@ Increment the: `CreateLogRecord(context)`. [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) +* [SDK] Add `LogRecordProcessor::HasEnabledFilter()` so the SDK Logger can + include processor-level filtering in its extended-enabled cache. Defaults + to `true` (conservative). Built-in `SimpleLogRecordProcessor` and + `BatchLogRecordProcessor` override to `false` since they use the default + Enabled. Custom processors that do not override `EnabledImplementation` + should similarly override `HasEnabledFilter()` to return `false` to enable + the cheap path. SDK ABI break. + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 05e831b961..a156fac4f6 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -359,6 +359,8 @@ class EnablementAwareTestLogger : public Logger void SetEventIdEnabled(bool enabled) noexcept { event_id_enabled_ = enabled; } + using Logger::SetExtendedEnabledRequired; + size_t create_log_record_calls_{0}; size_t emit_log_record_calls_{0}; mutable size_t enabled_calls_{0}; @@ -526,6 +528,18 @@ TEST(Logger, EmitLogRecordWithRecordBypassesFiltering) EXPECT_EQ(logger.emit_log_record_calls_, 1u); } +TEST(Logger, EmitLogRecordTemplateSkipsEnabledImplementationWhenExtendedEnabledNotRequired) +{ + EnablementAwareTestLogger logger(Severity::kTrace, /*event_id_enabled=*/false); + logger.SetExtendedEnabledRequired(false); + + logger.Info(nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); + EXPECT_EQ(logger.create_log_record_calls_, 1u); + EXPECT_EQ(logger.emit_log_record_calls_, 1u); +} + #if OPENTELEMETRY_ABI_VERSION_NO >= 2 TEST(Logger, EmitLogRecordWithContextInArgsRoutesThroughContextAwareEnabledAndEmits) { diff --git a/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h b/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h index 36ecf116be..1ec8504bb7 100644 --- a/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h @@ -107,6 +107,8 @@ class BatchLogRecordProcessor : public LogRecordProcessor bool Shutdown( std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + bool HasEnabledFilter() const noexcept override { return false; } + /** * Class destructor which invokes the Shutdown() method. */ diff --git a/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h b/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h index bb16cddb69..020ee0840a 100644 --- a/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h @@ -62,6 +62,8 @@ class MultiLogRecordProcessor : public LogRecordProcessor bool Shutdown( std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + bool HasEnabledFilter() const noexcept override; + protected: /** * Exports all log records that have not yet been exported to the configured Exporter. diff --git a/sdk/include/opentelemetry/sdk/logs/processor.h b/sdk/include/opentelemetry/sdk/logs/processor.h index 06206a872c..bdd695d5bc 100644 --- a/sdk/include/opentelemetry/sdk/logs/processor.h +++ b/sdk/include/opentelemetry/sdk/logs/processor.h @@ -89,6 +89,14 @@ class LogRecordProcessor virtual bool Shutdown( std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept = 0; + /** + * Returns true if this processor's EnabledImplementation does any custom + * filtering. The default returns true (conservative — assume any subclass + * might filter), so the SDK Logger consults the full Enabled chain by + * default. + */ + virtual bool HasEnabledFilter() const noexcept { return true; } + protected: virtual bool EnabledImplementation( const opentelemetry::context::Context & /*context*/, diff --git a/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h b/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h index 7c0e76d361..94b0bb7bdd 100644 --- a/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h @@ -49,6 +49,8 @@ class SimpleLogRecordProcessor : public LogRecordProcessor bool Shutdown( std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + bool HasEnabledFilter() const noexcept override { return false; } + bool IsShutdown() const noexcept; private: diff --git a/sdk/src/logs/batch_log_record_processor.cc b/sdk/src/logs/batch_log_record_processor.cc index 8a03e44d7d..48d8f1110a 100644 --- a/sdk/src/logs/batch_log_record_processor.cc +++ b/sdk/src/logs/batch_log_record_processor.cc @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 366b26e925..a976df5222 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -85,7 +85,8 @@ Logger::Logger( ? static_cast(logger_config_.GetMinimumSeverity()) : opentelemetry::logs::kMaxSeverity); - SetExtendedEnabledRequired(logger_config_.IsTraceBased()); + SetExtendedEnabledRequired(logger_config_.IsTraceBased() || + context_->GetProcessor().HasEnabledFilter()); } const opentelemetry::nostd::string_view Logger::GetName() noexcept diff --git a/sdk/src/logs/multi_log_record_processor.cc b/sdk/src/logs/multi_log_record_processor.cc index f17202c357..19ac44ddc6 100644 --- a/sdk/src/logs/multi_log_record_processor.cc +++ b/sdk/src/logs/multi_log_record_processor.cc @@ -6,8 +6,6 @@ #include #include -#include "opentelemetry/context/context.h" -#include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/logs/multi_log_record_processor.h" #include "opentelemetry/sdk/logs/multi_recordable.h" @@ -99,6 +97,18 @@ bool MultiLogRecordProcessor::EnabledImplementation( return false; } +bool MultiLogRecordProcessor::HasEnabledFilter() const noexcept +{ + for (const auto &processor : processors_) + { + if (processor != nullptr && processor->HasEnabledFilter()) + { + return true; + } + } + return false; +} + bool MultiLogRecordProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept { return InternalForceFlush(timeout); From d4578ad2cf9203abc38402c9533e04cec4b793fc Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Sun, 17 May 2026 20:20:37 +0900 Subject: [PATCH 11/12] feat: use temp context --- CHANGELOG.md | 5 +- api/include/opentelemetry/logs/logger.h | 48 ++++++++++ .../opentelemetry/logs/logger_type_traits.h | 90 +++++++++++++++++++ api/test/logs/logger_test.cc | 45 +++++++++- 4 files changed, 186 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 478b3ec166..ba09b773b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,10 @@ Increment the: for explicit-context record creation. `Logger::EmitLogRecord(args...)` also detects a `Context` in args and routes filtering through `Enabled(context, severity, ...)` plus trace stamping through - `CreateLogRecord(context)`. + `CreateLogRecord(context)`. When trace parts (`SpanContext`, or + `TraceId` + `SpanId` [+ `TraceFlags`]) are in args without a `Context`, + the template synthesizes a `Context` with the span attached so the filter + evaluates against the trace the record is for. [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) * [SDK] Add `LogRecordProcessor::HasEnabledFilter()` so the SDK Logger can diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index a1c265896b..14a6f78edb 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -15,6 +15,15 @@ #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/version.h" +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/trace/context.h" +# include "opentelemetry/trace/default_span.h" +# include "opentelemetry/trace/span.h" +# include "opentelemetry/trace/span_context.h" +# include "opentelemetry/trace/trace_flags.h" +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + OPENTELEMETRY_BEGIN_NAMESPACE namespace common { @@ -144,12 +153,51 @@ class Logger * span> -> attributes(return type of MakeAttributes) * Context (v2 only) -> filter + trace stamp (recommended: pass last) * + * When a @c Context is included, the filter chain uses + * @c Enabled(context, severity, ...) and the record is created via + * @c CreateLogRecord(context). When no @c Context is supplied but trace + * parts (@c SpanContext, or @c TraceId + @c SpanId [+ @c TraceFlags]) are + * in args, a @c Context is synthesized with those trace fields so the + * filter evaluates against the trace this record is for instead of the + * implicit runtime context. */ template void EmitLogRecord(ArgumentType &&...args) { #if OPENTELEMETRY_ABI_VERSION_NO >= 2 const opentelemetry::context::Context *context_ptr = detail::FindContextInArgs(args...); + // If no full Context is in args but trace parts are (SpanContext, or + // TraceId + SpanId [+ TraceFlags]), synthesize a Context with that span + // attached so the filter chain evaluates against the trace this record is + // actually for — not the implicit runtime context, which may be unrelated. + opentelemetry::context::Context derived_context; + if (context_ptr == nullptr) + { + const trace::SpanContext *span_context_ptr = detail::FindSpanContextInArgs(args...); + if (span_context_ptr != nullptr) + { + derived_context = trace::SetSpan( + derived_context, + nostd::shared_ptr(new trace::DefaultSpan(*span_context_ptr))); + context_ptr = &derived_context; + } + else + { + const trace::TraceId *trace_id_ptr = detail::FindTraceIdInArgs(args...); + const trace::SpanId *span_id_ptr = detail::FindSpanIdInArgs(args...); + if (trace_id_ptr != nullptr && span_id_ptr != nullptr) + { + const trace::TraceFlags *trace_flags_ptr = detail::FindTraceFlagsInArgs(args...); + derived_context = trace::SetSpan( + derived_context, + nostd::shared_ptr(new trace::DefaultSpan(trace::SpanContext( + *trace_id_ptr, *span_id_ptr, + trace_flags_ptr != nullptr ? *trace_flags_ptr : trace::TraceFlags{}, + false)))); + context_ptr = &derived_context; + } + } + } #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 const Severity arg_severity = detail::FindSeverityInArgs(args...); diff --git a/api/include/opentelemetry/logs/logger_type_traits.h b/api/include/opentelemetry/logs/logger_type_traits.h index a419c1485c..ec5fb7c7ef 100644 --- a/api/include/opentelemetry/logs/logger_type_traits.h +++ b/api/include/opentelemetry/logs/logger_type_traits.h @@ -281,6 +281,96 @@ inline const opentelemetry::context::Context *FindContextInArgs(First && /*first { return FindContextInArgs(std::forward(rest)...); } + +inline const trace::SpanContext *FindSpanContextInArgs() noexcept +{ + return nullptr; +} + +template +inline const trace::SpanContext *FindSpanContextInArgs(const trace::SpanContext &span_context, + Rest &&.../*rest*/) noexcept +{ + return &span_context; +} + +template ::type, trace::SpanContext>::value, + int>::type = 0> +inline const trace::SpanContext *FindSpanContextInArgs(First && /*first*/, + Rest &&...rest) noexcept +{ + return FindSpanContextInArgs(std::forward(rest)...); +} + +inline const trace::TraceId *FindTraceIdInArgs() noexcept +{ + return nullptr; +} + +template +inline const trace::TraceId *FindTraceIdInArgs(const trace::TraceId &trace_id, + Rest &&.../*rest*/) noexcept +{ + return &trace_id; +} + +template ::type, trace::TraceId>::value, + int>::type = 0> +inline const trace::TraceId *FindTraceIdInArgs(First && /*first*/, Rest &&...rest) noexcept +{ + return FindTraceIdInArgs(std::forward(rest)...); +} + +inline const trace::SpanId *FindSpanIdInArgs() noexcept +{ + return nullptr; +} + +template +inline const trace::SpanId *FindSpanIdInArgs(const trace::SpanId &span_id, + Rest &&.../*rest*/) noexcept +{ + return &span_id; +} + +template ::type, trace::SpanId>::value, + int>::type = 0> +inline const trace::SpanId *FindSpanIdInArgs(First && /*first*/, Rest &&...rest) noexcept +{ + return FindSpanIdInArgs(std::forward(rest)...); +} + +inline const trace::TraceFlags *FindTraceFlagsInArgs() noexcept +{ + return nullptr; +} + +template +inline const trace::TraceFlags *FindTraceFlagsInArgs(const trace::TraceFlags &trace_flags, + Rest &&.../*rest*/) noexcept +{ + return &trace_flags; +} + +template ::type, trace::TraceFlags>::value, + int>::type = 0> +inline const trace::TraceFlags *FindTraceFlagsInArgs(First && /*first*/, + Rest &&...rest) noexcept +{ + return FindTraceFlagsInArgs(std::forward(rest)...); +} #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 } // namespace detail diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index a156fac4f6..c3d6baf3fb 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -30,6 +30,10 @@ #if OPENTELEMETRY_ABI_VERSION_NO >= 2 # include "opentelemetry/context/context.h" # include "opentelemetry/nostd/variant.h" +# include "opentelemetry/trace/span_context.h" +# include "opentelemetry/trace/span_id.h" +# include "opentelemetry/trace/trace_flags.h" +# include "opentelemetry/trace/trace_id.h" #endif #if OPENTELEMETRY_ABI_VERSION_NO < 2 @@ -530,7 +534,7 @@ TEST(Logger, EmitLogRecordWithRecordBypassesFiltering) TEST(Logger, EmitLogRecordTemplateSkipsEnabledImplementationWhenExtendedEnabledNotRequired) { - EnablementAwareTestLogger logger(Severity::kTrace, /*event_id_enabled=*/false); + EnablementAwareTestLogger logger(Severity::kTrace, false); logger.SetExtendedEnabledRequired(false); logger.Info(nostd::string_view{"emitted"}); @@ -570,4 +574,43 @@ TEST(Logger, EmitLogRecordWithContextInArgsShortCircuitsWhenEnabledImplementatio EXPECT_EQ(logger.create_log_record_context_calls_, 0u); EXPECT_EQ(logger.emit_log_record_calls_, 0u); } + +TEST(Logger, EmitLogRecordWithSpanContextInArgsSynthesizesContextForFilter) +{ + EnablementAwareTestLogger logger(Severity::kTrace); + + const uint8_t trace_id_bytes[trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + const uint8_t span_id_bytes[trace::SpanId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8}; + const trace::SpanContext span_context(trace::TraceId(trace_id_bytes), + trace::SpanId(span_id_bytes), + trace::TraceFlags{trace::TraceFlags::kIsSampled}, + false); + + logger.EmitLogRecord(Severity::kInfo, span_context, nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_calls_, 1u); + EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); + EXPECT_EQ(logger.create_log_record_context_calls_, 1u); + EXPECT_EQ(logger.emit_log_record_calls_, 1u); +} + +TEST(Logger, EmitLogRecordWithTracePartsInArgsSynthesizesContextForFilter) +{ + EnablementAwareTestLogger logger(Severity::kTrace); + + const uint8_t trace_id_bytes[trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + const uint8_t span_id_bytes[trace::SpanId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8}; + + logger.EmitLogRecord(Severity::kInfo, trace::TraceId(trace_id_bytes), + trace::SpanId(span_id_bytes), + trace::TraceFlags{trace::TraceFlags::kIsSampled}, + nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_calls_, 1u); + EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); + EXPECT_EQ(logger.create_log_record_context_calls_, 1u); + EXPECT_EQ(logger.emit_log_record_calls_, 1u); +} #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 From d62c8e4eb0fd951fa354f417b1900d699ca23a70 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Sun, 17 May 2026 22:07:15 +0900 Subject: [PATCH 12/12] refactor: unify function --- api/include/opentelemetry/logs/logger.h | 3 +- .../opentelemetry/logs/logger_type_traits.h | 26 ++--- api/test/logs/logger_test.cc | 14 +-- sdk/src/logs/logger.cc | 103 +++++++----------- 4 files changed, 57 insertions(+), 89 deletions(-) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index 14a6f78edb..dd474575ab 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -192,8 +192,7 @@ class Logger derived_context, nostd::shared_ptr(new trace::DefaultSpan(trace::SpanContext( *trace_id_ptr, *span_id_ptr, - trace_flags_ptr != nullptr ? *trace_flags_ptr : trace::TraceFlags{}, - false)))); + trace_flags_ptr != nullptr ? *trace_flags_ptr : trace::TraceFlags{}, false)))); context_ptr = &derived_context; } } diff --git a/api/include/opentelemetry/logs/logger_type_traits.h b/api/include/opentelemetry/logs/logger_type_traits.h index ec5fb7c7ef..93a8ed16d0 100644 --- a/api/include/opentelemetry/logs/logger_type_traits.h +++ b/api/include/opentelemetry/logs/logger_type_traits.h @@ -299,8 +299,7 @@ template ::type, trace::SpanContext>::value, int>::type = 0> -inline const trace::SpanContext *FindSpanContextInArgs(First && /*first*/, - Rest &&...rest) noexcept +inline const trace::SpanContext *FindSpanContextInArgs(First && /*first*/, Rest &&...rest) noexcept { return FindSpanContextInArgs(std::forward(rest)...); } @@ -317,11 +316,11 @@ inline const trace::TraceId *FindTraceIdInArgs(const trace::TraceId &trace_id, return &trace_id; } -template ::type, trace::TraceId>::value, - int>::type = 0> +template < + class First, + class... Rest, + typename std::enable_if::type, trace::TraceId>::value, + int>::type = 0> inline const trace::TraceId *FindTraceIdInArgs(First && /*first*/, Rest &&...rest) noexcept { return FindTraceIdInArgs(std::forward(rest)...); @@ -339,11 +338,11 @@ inline const trace::SpanId *FindSpanIdInArgs(const trace::SpanId &span_id, return &span_id; } -template ::type, trace::SpanId>::value, - int>::type = 0> +template < + class First, + class... Rest, + typename std::enable_if::type, trace::SpanId>::value, + int>::type = 0> inline const trace::SpanId *FindSpanIdInArgs(First && /*first*/, Rest &&...rest) noexcept { return FindSpanIdInArgs(std::forward(rest)...); @@ -366,8 +365,7 @@ template ::type, trace::TraceFlags>::value, int>::type = 0> -inline const trace::TraceFlags *FindTraceFlagsInArgs(First && /*first*/, - Rest &&...rest) noexcept +inline const trace::TraceFlags *FindTraceFlagsInArgs(First && /*first*/, Rest &&...rest) noexcept { return FindTraceFlagsInArgs(std::forward(rest)...); } diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index c3d6baf3fb..b72b63ecb8 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -579,13 +579,12 @@ TEST(Logger, EmitLogRecordWithSpanContextInArgsSynthesizesContextForFilter) { EnablementAwareTestLogger logger(Severity::kTrace); - const uint8_t trace_id_bytes[trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, + const uint8_t trace_id_bytes[trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; const uint8_t span_id_bytes[trace::SpanId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8}; const trace::SpanContext span_context(trace::TraceId(trace_id_bytes), trace::SpanId(span_id_bytes), - trace::TraceFlags{trace::TraceFlags::kIsSampled}, - false); + trace::TraceFlags{trace::TraceFlags::kIsSampled}, false); logger.EmitLogRecord(Severity::kInfo, span_context, nostd::string_view{"emitted"}); @@ -599,14 +598,13 @@ TEST(Logger, EmitLogRecordWithTracePartsInArgsSynthesizesContextForFilter) { EnablementAwareTestLogger logger(Severity::kTrace); - const uint8_t trace_id_bytes[trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, + const uint8_t trace_id_bytes[trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; const uint8_t span_id_bytes[trace::SpanId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8}; - logger.EmitLogRecord(Severity::kInfo, trace::TraceId(trace_id_bytes), - trace::SpanId(span_id_bytes), - trace::TraceFlags{trace::TraceFlags::kIsSampled}, - nostd::string_view{"emitted"}); + logger.EmitLogRecord( + Severity::kInfo, trace::TraceId(trace_id_bytes), trace::SpanId(span_id_bytes), + trace::TraceFlags{trace::TraceFlags::kIsSampled}, nostd::string_view{"emitted"}); EXPECT_EQ(logger.enabled_calls_, 1u); EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index a976df5222..80fc72aef6 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -68,6 +68,42 @@ bool IsAllowedByTraceBasedFiltering(const context::Context &context, return span_context.trace_flags().IsSampled(); } + +void StampSpanContextFromContext(const context::Context &context, Recordable &recordable) noexcept +{ + if (!context.HasKey(trace_api::kSpanKey)) + { + return; + } + + const context::ContextValue context_value = context.GetValue(trace_api::kSpanKey); + + const trace_api::SpanContext span_context = [&context_value]() { + // Get the span metadata from the active span in the context + if (const nostd::shared_ptr *maybe_span = + nostd::get_if>(&context_value)) + { + const nostd::shared_ptr &span = *maybe_span; + return span->GetContext(); + } + // Get the span metadata directly from a SpanContext in the context. + // TODO: This path is unused and may be removed in the future. + else if (const nostd::shared_ptr *maybe_span_context = + nostd::get_if>(&context_value)) + { + const nostd::shared_ptr &span_context = *maybe_span_context; + return *span_context; + } + return trace_api::SpanContext::GetInvalid(); + }(); + + if (span_context.IsValid()) + { + recordable.SetTraceId(span_context.trace_id()); + recordable.SetTraceFlags(span_context.trace_flags()); + recordable.SetSpanId(span_context.span_id()); + } +} } // namespace opentelemetry::logs::NoopLogger Logger::kNoopLogger = opentelemetry::logs::NoopLogger(); @@ -109,39 +145,7 @@ opentelemetry::nostd::unique_ptr Logger::CreateL recordable->SetObservedTimestamp(std::chrono::system_clock::now()); - // Get the current span metadata from the runtime context - const auto current_context = context::RuntimeContext::GetCurrent(); - - if (current_context.HasKey(trace_api::kSpanKey)) - { - const context::ContextValue context_value = current_context.GetValue(trace_api::kSpanKey); - - const trace_api::SpanContext span_context = [&context_value]() { - // Get the span metadata from the active span in the runtime context - if (const nostd::shared_ptr *maybe_span = - nostd::get_if>(&context_value)) - { - const nostd::shared_ptr &span = *maybe_span; - return span->GetContext(); - } - // Get the span metadata directly from a SpanContext in the runtime context. - // TODO: This path is unused and may be removed in the future. - else if (const nostd::shared_ptr *maybe_span_context = - nostd::get_if>(&context_value)) - { - const nostd::shared_ptr &span_context = *maybe_span_context; - return *span_context; - } - return trace_api::SpanContext::GetInvalid(); - }(); - - if (span_context.IsValid()) - { - recordable->SetTraceId(span_context.trace_id()); - recordable->SetTraceFlags(span_context.trace_flags()); - recordable->SetSpanId(span_context.span_id()); - } - } + StampSpanContextFromContext(context::RuntimeContext::GetCurrent(), *recordable); return opentelemetry::nostd::unique_ptr(recordable.release()); } @@ -159,38 +163,7 @@ opentelemetry::nostd::unique_ptr Logger::CreateL recordable->SetObservedTimestamp(std::chrono::system_clock::now()); - // Get the span metadata from the supplied context - if (context.HasKey(trace_api::kSpanKey)) - { - const opentelemetry::context::ContextValue context_value = - context.GetValue(trace_api::kSpanKey); - - const trace_api::SpanContext span_context = [&context_value]() { - // Get the span metadata from the active span in the supplied context - if (const nostd::shared_ptr *maybe_span = - nostd::get_if>(&context_value)) - { - const nostd::shared_ptr &span = *maybe_span; - return span->GetContext(); - } - // Get the span metadata directly from a SpanContext in the supplied context. - // TODO: This path is unused and may be removed in the future. - else if (const nostd::shared_ptr *maybe_span_context = - nostd::get_if>(&context_value)) - { - const nostd::shared_ptr &span_context = *maybe_span_context; - return *span_context; - } - return trace_api::SpanContext::GetInvalid(); - }(); - - if (span_context.IsValid()) - { - recordable->SetTraceId(span_context.trace_id()); - recordable->SetTraceFlags(span_context.trace_flags()); - recordable->SetSpanId(span_context.span_id()); - } - } + StampSpanContextFromContext(context, *recordable); return opentelemetry::nostd::unique_ptr(recordable.release()); }