diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index b43f15a0ae..c00abe7ae5 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: 389 + warning_limit: 387 - cmake_options: all-options-abiv2-preview - warning_limit: 395 + warning_limit: 391 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 21bbc377fa..dfc5a0bb09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,30 @@ Increment the: * [SDK] MeterProvider: do not warn in destructor after explicit Shutdown [#4085](https://github.com/open-telemetry/opentelemetry-cpp/pull/4085) +* [API] `Logger::EmitLogRecord(...)` templates now apply the `Enabled` filter + chain when a `Severity` is in args. + [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) + +* [API/SDK] (ABI v2) Add `Logger::CreateLogRecord(const Context &)` virtual + for explicit-context record creation. `Logger::EmitLogRecord(args...)` + also detects a `Context`, `SpanContext` + or `TraceId` + `SpanId` [+ `TraceFlags`] in args and routes filtering. + [#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`. Built-in `SimpleLogRecordProcessor` and + `BatchLogRecordProcessor` override to `false` since they use the default + Enabled. + [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) + +* [API/SDK] Replace `Context`-only signatures on + `LogRecordProcessor::Enabled`, + `LogRecordProcessor::EnabledImplementation`, + `Logger::EnabledImplementation` (v2), and `Logger::CreateLogRecord` (v2) + with `nostd::variant`. + [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index 10e3f7a528..591453812f 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -15,6 +15,13 @@ #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/version.h" +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +# include "opentelemetry/context/runtime_context.h" +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/trace/span_context.h" +# include "opentelemetry/trace/trace_flags.h" +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + OPENTELEMETRY_BEGIN_NAMESPACE namespace common { @@ -50,6 +57,22 @@ class Logger */ virtual nostd::unique_ptr CreateLogRecord() noexcept = 0; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + /** + * Create a Log Record object using either a Context or a SpanContext. + * + * @param context_or_span Variant carrying either a full Context or just a + * SpanContext. + * @return nostd::unique_ptr + */ + virtual nostd::unique_ptr CreateLogRecord( + const nostd::variant & + /*context_or_span*/) noexcept + { + return CreateLogRecord(); + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + /** * Emit a Log Record object * @@ -58,7 +81,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. @@ -120,11 +151,84 @@ class Logger * KeyValueIterable -> attributes * Key value iterable container -> attributes * span> -> attributes(return type of MakeAttributes) + * Context (v2 only) -> filter + trace stamp (recommended: pass last) + * + * When a @c Context or trace parts (@c SpanContext, or @c TraceId + + * @c SpanId [+ @c TraceFlags]) are in args, the filter chain uses + * @c Enabled(context_or_span, severity, ...) and the record is created + * via @c CreateLogRecord(context_or_span), 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 + nostd::variant context_or_span = + trace::SpanContext::GetInvalid(); + + if (const opentelemetry::context::Context *ctx = detail::FindContextInArgs(args...)) + { + context_or_span = *ctx; + } + else if (const trace::SpanContext *sc = detail::FindSpanContextInArgs(args...)) + { + context_or_span = *sc; + } + 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...); + context_or_span = trace::SpanContext( + *trace_id_ptr, *span_id_ptr, + trace_flags_ptr != nullptr ? *trace_flags_ptr : trace::TraceFlags{}, false); + } + else + { + context_or_span = opentelemetry::context::RuntimeContext::GetCurrent(); + } + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + + const Severity arg_severity = detail::FindSeverityInArgs(args...); + if (arg_severity != Severity::kInvalid) + { + if (!Enabled(arg_severity)) + { + return; + } + + const EventId *event_id_ptr = detail::FindEventIdInArgs(args...); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + if (ExtendedEnabledRequired()) + { + const bool extended_enabled = + event_id_ptr ? EnabledImplementation(context_or_span, arg_severity, *event_id_ptr) + : EnabledImplementation(context_or_span, arg_severity); + if (!extended_enabled) + { + return; + } + } +#else + const bool extended_enabled = + event_id_ptr ? EnabledImplementation(arg_severity, *event_id_ptr) + : EnabledImplementation(arg_severity, static_cast(0)); + if (!extended_enabled) + { + return; + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + } + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::unique_ptr log_record = CreateLogRecord(context_or_span); +#else nostd::unique_ptr log_record = CreateLogRecord(); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 EmitLogRecord(std::move(log_record), std::forward(args)...); } @@ -278,25 +382,27 @@ class Logger // #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - inline bool Enabled(const opentelemetry::context::Context &context, - Severity severity = Severity::kInvalid) const noexcept + inline bool Enabled( + const nostd::variant &context_or_span, + Severity severity = Severity::kInvalid) const noexcept { if OPENTELEMETRY_LIKELY_CONDITION (!Enabled(severity)) { return false; } - return EnabledImplementation(context, severity); + return EnabledImplementation(context_or_span, severity); } - inline bool Enabled(const opentelemetry::context::Context &context, - Severity severity, - const EventId &event_id) const noexcept + inline bool Enabled( + const nostd::variant &context_or_span, + Severity severity, + const EventId &event_id) const noexcept { if OPENTELEMETRY_LIKELY_CONDITION (!Enabled(severity)) { return false; } - return EnabledImplementation(context, severity, event_id); + return EnabledImplementation(context_or_span, severity, event_id); } #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 @@ -323,6 +429,13 @@ class Logger return static_cast(severity) >= OPENTELEMETRY_ATOMIC_READ_8(&minimum_severity_); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + inline bool ExtendedEnabledRequired() const noexcept + { + return OPENTELEMETRY_ATOMIC_READ_8(&extended_enabled_required_) != 0; + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + /** * Log an event * @@ -505,15 +618,19 @@ class Logger } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - virtual bool EnabledImplementation(const opentelemetry::context::Context & /*context*/, - Severity /*severity*/) const noexcept + virtual bool EnabledImplementation( + const nostd::variant & + /*context_or_span*/, + Severity /*severity*/) const noexcept { return false; } - virtual bool EnabledImplementation(const opentelemetry::context::Context & /*context*/, - Severity /*severity*/, - const EventId & /*event_id*/) const noexcept + virtual bool EnabledImplementation( + const nostd::variant & + /*context_or_span*/, + Severity /*severity*/, + const EventId & /*event_id*/) const noexcept { return false; } @@ -524,6 +641,14 @@ class Logger OPENTELEMETRY_ATOMIC_WRITE_8(&minimum_severity_, severity_or_max); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + void SetExtendedEnabledRequired(bool required) noexcept + { + OPENTELEMETRY_ATOMIC_WRITE_8(&extended_enabled_required_, + static_cast(required ? 1 : 0)); + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + private: template void IgnoreTraitResult(ValueType &&...) @@ -535,6 +660,19 @@ class Logger // compatible for OpenTelemetry C++ API. // mutable uint8_t minimum_severity_{kMaxSeverity}; + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + // + // Controls whether the EmitLogRecord(args...) template calls the + // EnabledImplementation virtual in addition to the cheap atomic + // minimum_severity_ check. When 0, the template trusts the atomic check + // as the complete enabled decision and skips the virtual dispatch. When + // 1, the template also consults EnabledImplementation, which lets a + // Logger subclass apply richer filtering (trace-based, processor-level + // Enabled, custom predicates). + // + mutable uint8_t extended_enabled_required_{0}; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 }; } // namespace logs OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/logs/logger_type_traits.h b/api/include/opentelemetry/logs/logger_type_traits.h index 19e0e3219b..93a8ed16d0 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 { @@ -198,6 +217,160 @@ 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)...); +} + +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)...); +} + +#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)...); +} + +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 < + 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)...); +} + +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 < + 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)...); +} + +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 } // namespace logs diff --git a/api/include/opentelemetry/logs/noop.h b/api/include/opentelemetry/logs/noop.h index 275b58e604..8608bb514d 100644 --- a/api/include/opentelemetry/logs/noop.h +++ b/api/include/opentelemetry/logs/noop.h @@ -34,6 +34,8 @@ class NoopLogger final : public Logger public: const nostd::string_view GetName() noexcept override { return "noop logger"; } + using Logger::CreateLogRecord; + nostd::unique_ptr CreateLogRecord() noexcept override { /* diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 1c3baa473b..66b2bf5ce9 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 @@ -261,6 +265,8 @@ class TestLogger : public Logger { const nostd::string_view GetName() noexcept override { return "test logger"; } + using Logger::CreateLogRecord; + nostd::unique_ptr CreateLogRecord() noexcept override { return nullptr; @@ -275,6 +281,9 @@ class TestLogger : public Logger } }; +namespace +{ + class EnablementAwareTestLogRecord : public opentelemetry::logs::LogRecord { public: @@ -317,8 +326,8 @@ class EnablementAwareTestLogger : public Logger { public: explicit EnablementAwareTestLogger(Severity minimum_severity, - bool event_id_enabled = false) noexcept - : event_id_enabled_(event_id_enabled) + bool enabled_impl_result = false) noexcept + : enabled_impl_result_(enabled_impl_result) { SetMinimumSeverity(static_cast(minimum_severity)); } @@ -331,6 +340,16 @@ class EnablementAwareTestLogger : public Logger return nostd::unique_ptr(new EnablementAwareTestLogRecord()); } +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::unique_ptr CreateLogRecord( + const nostd::variant & /*context_or_span*/) noexcept + override + { + ++create_log_record_context_calls_; + return CreateLogRecord(); + } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + using Logger::EmitLogRecord; void EmitLogRecord( @@ -345,7 +364,11 @@ class EnablementAwareTestLogger : public Logger } } - void SetEventIdEnabled(bool enabled) noexcept { event_id_enabled_ = enabled; } + void SetEnabledImplResult(bool enabled) noexcept { enabled_impl_result_ = enabled; } + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + using Logger::SetExtendedEnabledRequired; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 size_t create_log_record_calls_{0}; size_t emit_log_record_calls_{0}; @@ -356,47 +379,61 @@ 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}; + mutable bool last_variant_holds_span_context_{false}; + mutable bool last_variant_holds_context_{false}; + mutable trace::SpanContext last_span_context_{trace::SpanContext::GetInvalid()}; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 protected: #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - bool EnabledImplementation(const context::Context &context, - Severity severity) const noexcept override + void CaptureContextOrSpan( + const nostd::variant &context_or_span) const noexcept { - ++enabled_calls_; - last_enabled_severity_ = severity; - auto value = context.GetValue("test-key"); - if (const bool *maybe_value = nostd::get_if(&value)) + last_variant_holds_span_context_ = false; + last_variant_holds_context_ = false; + last_span_context_ = trace::SpanContext::GetInvalid(); + last_enabled_context_has_test_key_ = false; + last_enabled_context_test_key_value_ = false; + + if (const trace::SpanContext *sc = nostd::get_if(&context_or_span)) { - last_enabled_context_has_test_key_ = true; - last_enabled_context_test_key_value_ = *maybe_value; + last_variant_holds_span_context_ = true; + last_span_context_ = *sc; } - else + else if (const context::Context *ctx = nostd::get_if(&context_or_span)) { - last_enabled_context_has_test_key_ = false; - last_enabled_context_test_key_value_ = false; + last_variant_holds_context_ = true; + auto value = ctx->GetValue("test-key"); + if (const bool *maybe_value = nostd::get_if(&value)) + { + last_enabled_context_has_test_key_ = true; + last_enabled_context_test_key_value_ = *maybe_value; + } } - return true; } - bool EnabledImplementation(const context::Context &context, - Severity severity, - const EventId &event_id) const noexcept override + bool EnabledImplementation( + const nostd::variant &context_or_span, + Severity severity) const noexcept override + { + ++enabled_calls_; + last_enabled_severity_ = severity; + CaptureContextOrSpan(context_or_span); + return enabled_impl_result_; + } + + bool EnabledImplementation( + const nostd::variant &context_or_span, + Severity severity, + const EventId &event_id) const noexcept override { ++enabled_with_event_id_calls_; last_enabled_severity_ = severity; last_enabled_event_id_ = event_id.id_; - auto value = context.GetValue("test-key"); - if (const bool *maybe_value = nostd::get_if(&value)) - { - last_enabled_context_has_test_key_ = true; - last_enabled_context_test_key_value_ = *maybe_value; - } - else - { - last_enabled_context_has_test_key_ = false; - last_enabled_context_test_key_value_ = false; - } - return event_id_enabled_; + CaptureContextOrSpan(context_or_span); + return enabled_impl_result_; } #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 @@ -405,7 +442,7 @@ class EnablementAwareTestLogger : public Logger ++enabled_with_event_id_calls_; last_enabled_severity_ = severity; last_enabled_event_id_ = event_id.id_; - return event_id_enabled_; + return enabled_impl_result_; } bool EnabledImplementation(Severity severity, int64_t event_id) const noexcept override @@ -414,9 +451,11 @@ class EnablementAwareTestLogger : public Logger } private: - bool event_id_enabled_; + bool enabled_impl_result_; }; +} // namespace + // Define a basic LoggerProvider class that returns an instance of the logger class defined above class TestProvider : public LoggerProvider { @@ -447,7 +486,7 @@ TEST(Logger, PushLoggerImplementation) #if OPENTELEMETRY_ABI_VERSION_NO >= 2 TEST(Logger, EnabledWithExplicitContextUsesContextAwareImplementation) { - EnablementAwareTestLogger logger(Severity::kTrace); + EnablementAwareTestLogger logger(Severity::kTrace, true); context::Context test_context{"test-key", true}; @@ -458,3 +497,184 @@ 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); + EXPECT_EQ(logger.enabled_with_event_id_calls_, 0u); +} + +TEST(Logger, EmitLogRecordTemplateInvokesEnabledImplementationAndEmitsWhenAllowed) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + logger.SetExtendedEnabledRequired(true); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + + logger.Info(nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_calls_ + 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); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + logger.SetExtendedEnabledRequired(true); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + + logger.Info(nostd::string_view{"filtered"}); + + EXPECT_EQ(logger.enabled_calls_ + 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); +} + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +TEST(Logger, EmitLogRecordTemplateSkipsEnabledImplementationWhenExtendedEnabledNotRequired) +{ + EnablementAwareTestLogger logger(Severity::kTrace, 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); +} + +TEST(Logger, EmitLogRecordWithContextInArgsRoutesContextVariantToEnabledAndEmits) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); + logger.SetExtendedEnabledRequired(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_variant_holds_context_); + EXPECT_FALSE(logger.last_variant_holds_span_context_); + 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); + logger.SetExtendedEnabledRequired(true); + + 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); +} + +TEST(Logger, EmitLogRecordWithSpanContextInArgsRoutesSpanContextVariantToEnabled) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); + logger.SetExtendedEnabledRequired(true); + + 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); + EXPECT_TRUE(logger.last_variant_holds_span_context_); + EXPECT_FALSE(logger.last_variant_holds_context_); + EXPECT_EQ(logger.last_span_context_.trace_id(), span_context.trace_id()); + EXPECT_EQ(logger.last_span_context_.span_id(), span_context.span_id()); + EXPECT_EQ(logger.last_span_context_.trace_flags(), span_context.trace_flags()); +} + +TEST(Logger, EmitLogRecordWithSpanContextInArgsAndEventIdRoutesVariantWithEventId) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); + logger.SetExtendedEnabledRequired(true); + + 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, EventId{0x42, "info"}, span_context, + nostd::string_view{"emitted"}); + + EXPECT_EQ(logger.enabled_calls_, 0u); + 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_variant_holds_span_context_); + EXPECT_FALSE(logger.last_variant_holds_context_); + EXPECT_EQ(logger.last_span_context_.trace_id(), span_context.trace_id()); + EXPECT_EQ(logger.last_span_context_.span_id(), span_context.span_id()); + EXPECT_EQ(logger.last_enabled_event_id_, 0x42); +} + +TEST(Logger, EmitLogRecordWithTracePartsInArgsRoutesSpanContextVariantToEnabled) +{ + EnablementAwareTestLogger logger(Severity::kTrace, true); + logger.SetExtendedEnabledRequired(true); + + 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::TraceId trace_id(trace_id_bytes); + const trace::SpanId span_id(span_id_bytes); + const trace::TraceFlags trace_flags{trace::TraceFlags::kIsSampled}; + + logger.EmitLogRecord(Severity::kInfo, trace_id, span_id, trace_flags, + 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); + EXPECT_TRUE(logger.last_variant_holds_span_context_); + EXPECT_FALSE(logger.last_variant_holds_context_); + EXPECT_EQ(logger.last_span_context_.trace_id(), trace_id); + EXPECT_EQ(logger.last_span_context_.span_id(), span_id); + EXPECT_EQ(logger.last_span_context_.trace_flags(), trace_flags); +} +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h index 14076a8b3e..58ecac86a8 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h @@ -197,6 +197,8 @@ class Logger : public opentelemetry::logs::Logger provHandle(initProvHandle()) {} + using opentelemetry::logs::Logger::CreateLogRecord; + nostd::unique_ptr CreateLogRecord() noexcept { nostd::unique_ptr log_record(new LogRecord()); 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/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index a77d80caa8..8be05720a1 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -17,6 +17,11 @@ #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/version.h" +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/trace/span_context.h" +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -45,6 +50,12 @@ class Logger final : public opentelemetry::logs::Logger nostd::unique_ptr CreateLogRecord() noexcept override; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::unique_ptr CreateLogRecord( + const nostd::variant + &context_or_span) noexcept override; +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + using opentelemetry::logs::Logger::EmitLogRecord; void EmitLogRecord( @@ -69,10 +80,12 @@ class Logger final : public opentelemetry::logs::Logger int64_t event_id) const noexcept override; #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - bool EnabledImplementation(const opentelemetry::context::Context &context, + bool EnabledImplementation(const nostd::variant &context_or_span, opentelemetry::logs::Severity severity) const noexcept override; - bool EnabledImplementation(const opentelemetry::context::Context &context, + bool EnabledImplementation(const nostd::variant &context_or_span, opentelemetry::logs::Severity severity, const opentelemetry::logs::EventId &event_id) const noexcept override; #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 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..8158a2dbf6 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. @@ -82,7 +84,8 @@ class MultiLogRecordProcessor : public LogRecordProcessor std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; bool EnabledImplementation( - const opentelemetry::context::Context &context, + const opentelemetry::nostd::variant &context_or_span, const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope, opentelemetry::logs::Severity severity, opentelemetry::nostd::string_view event_name) const noexcept override; diff --git a/sdk/include/opentelemetry/sdk/logs/processor.h b/sdk/include/opentelemetry/sdk/logs/processor.h index 06206a872c..557db92250 100644 --- a/sdk/include/opentelemetry/sdk/logs/processor.h +++ b/sdk/include/opentelemetry/sdk/logs/processor.h @@ -9,6 +9,8 @@ #include "opentelemetry/context/context.h" #include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/span_context.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -63,12 +65,13 @@ class LogRecordProcessor * The default implementation is permissive and returns true. */ bool Enabled( - const opentelemetry::context::Context &context, + const opentelemetry::nostd::variant &context_or_span, const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope, opentelemetry::logs::Severity severity, opentelemetry::nostd::string_view event_name = {}) const noexcept { - return EnabledImplementation(context, instrumentation_scope, severity, event_name); + return EnabledImplementation(context_or_span, instrumentation_scope, severity, event_name); } /** @@ -89,9 +92,18 @@ 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*/, + const opentelemetry::nostd::variant & /*context_or_span*/, const opentelemetry::sdk::instrumentationscope::InstrumentationScope & /*instrumentation_scope*/, opentelemetry::logs::Severity /*severity*/, 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/logger.cc b/sdk/src/logs/logger.cc index 8b7062db4a..5c643da7b6 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -25,7 +25,6 @@ #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/recordable.h" -#include "opentelemetry/trace/context.h" #include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" @@ -50,15 +49,57 @@ nostd::string_view GetEventName(const opentelemetry::logs::EventId &event_id) no : nostd::string_view{}; } -bool IsAllowedByTraceBasedFiltering(const context::Context &context, - const LoggerConfig &logger_config) noexcept +trace_api::SpanContext ExtractSpanContextFromContext(const context::Context &context) noexcept +{ + if (!context.HasKey(trace_api::kSpanKey)) + { + return trace_api::SpanContext::GetInvalid(); + } + + const context::ContextValue context_value = context.GetValue(trace_api::kSpanKey); + + // 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. + 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(); +} + +trace_api::SpanContext ExtractSpanContext( + const nostd::variant &context_or_span) noexcept +{ + if (const trace_api::SpanContext *sc = nostd::get_if(&context_or_span)) + { + return *sc; + } + if (const context::Context *ctx = nostd::get_if(&context_or_span)) + { + return ExtractSpanContextFromContext(*ctx); + } + return trace_api::SpanContext::GetInvalid(); +} + +bool IsAllowedByTraceBasedFiltering( + const nostd::variant &context_or_span, + const LoggerConfig &logger_config) noexcept { if (!logger_config.IsTraceBased()) { return true; } - const trace_api::SpanContext span_context = trace_api::GetSpan(context)->GetContext(); + const trace_api::SpanContext span_context = ExtractSpanContext(context_or_span); if (!span_context.span_id().IsValid()) { @@ -67,6 +108,19 @@ bool IsAllowedByTraceBasedFiltering(const context::Context &context, return span_context.trace_flags().IsSampled(); } + +void StampSpanContextFromVariant( + const nostd::variant &context_or_span, + Recordable &recordable) noexcept +{ + const trace_api::SpanContext span_context = ExtractSpanContext(context_or_span); + 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(); @@ -83,6 +137,11 @@ Logger::Logger( SetMinimumSeverity(logger_config_.IsEnabled() ? static_cast(logger_config_.GetMinimumSeverity()) : opentelemetry::logs::kMaxSeverity); + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + SetExtendedEnabledRequired(logger_config_.IsTraceBased() || + context_->GetProcessor().HasEnabledFilter()); +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 } const opentelemetry::nostd::string_view Logger::GetName() noexcept @@ -105,42 +164,33 @@ 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()); - } + StampSpanContextFromVariant( + nostd::variant{ + context::RuntimeContext::GetCurrent()}, + *recordable); + + return opentelemetry::nostd::unique_ptr(recordable.release()); +} + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 +opentelemetry::nostd::unique_ptr Logger::CreateLogRecord( + const nostd::variant + &context_or_span) noexcept +{ + if (!logger_config_.IsEnabled()) + { + return kNoopLogger.CreateLogRecord(); } + auto recordable = context_->GetProcessor().MakeRecordable(); + + recordable->SetObservedTimestamp(std::chrono::system_clock::now()); + + StampSpanContextFromVariant(context_or_span, *recordable); + return opentelemetry::nostd::unique_ptr(recordable.release()); } +#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 void Logger::EmitLogRecord( opentelemetry::nostd::unique_ptr &&log_record) noexcept @@ -162,8 +212,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)); } @@ -171,7 +219,8 @@ void Logger::EmitLogRecord( bool Logger::EnabledImplementation(opentelemetry::logs::Severity severity, const opentelemetry::logs::EventId &event_id) const noexcept { - const auto ¤t = context::RuntimeContext::GetCurrent(); + const nostd::variant current{ + context::RuntimeContext::GetCurrent()}; if (!IsAllowedByTraceBasedFiltering(current, logger_config_)) { return false; @@ -184,7 +233,8 @@ bool Logger::EnabledImplementation(opentelemetry::logs::Severity severity, bool Logger::EnabledImplementation(opentelemetry::logs::Severity severity, int64_t /*event_id*/) const noexcept { - const auto ¤t = context::RuntimeContext::GetCurrent(); + const nostd::variant current{ + context::RuntimeContext::GetCurrent()}; if (!IsAllowedByTraceBasedFiltering(current, logger_config_)) { return false; @@ -194,27 +244,29 @@ bool Logger::EnabledImplementation(opentelemetry::logs::Severity severity, } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 -bool Logger::EnabledImplementation(const opentelemetry::context::Context &context, - opentelemetry::logs::Severity severity) const noexcept +bool Logger::EnabledImplementation( + const nostd::variant &context_or_span, + opentelemetry::logs::Severity severity) const noexcept { - if (!IsAllowedByTraceBasedFiltering(context, logger_config_)) + if (!IsAllowedByTraceBasedFiltering(context_or_span, logger_config_)) { return false; } - return context_->GetProcessor().Enabled(context, GetInstrumentationScope(), severity); + return context_->GetProcessor().Enabled(context_or_span, GetInstrumentationScope(), severity); } -bool Logger::EnabledImplementation(const opentelemetry::context::Context &context, - opentelemetry::logs::Severity severity, - const opentelemetry::logs::EventId &event_id) const noexcept +bool Logger::EnabledImplementation( + const nostd::variant &context_or_span, + opentelemetry::logs::Severity severity, + const opentelemetry::logs::EventId &event_id) const noexcept { - if (!IsAllowedByTraceBasedFiltering(context, logger_config_)) + if (!IsAllowedByTraceBasedFiltering(context_or_span, logger_config_)) { return false; } - return context_->GetProcessor().Enabled(context, GetInstrumentationScope(), severity, + return context_->GetProcessor().Enabled(context_or_span, GetInstrumentationScope(), severity, GetEventName(event_id)); } #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 diff --git a/sdk/src/logs/multi_log_record_processor.cc b/sdk/src/logs/multi_log_record_processor.cc index 12bafcefad..17826bcf43 100644 --- a/sdk/src/logs/multi_log_record_processor.cc +++ b/sdk/src/logs/multi_log_record_processor.cc @@ -75,7 +75,8 @@ void MultiLogRecordProcessor::OnEmit(std::unique_ptr &&record) noexc } bool MultiLogRecordProcessor::EnabledImplementation( - const opentelemetry::context::Context &context, + const opentelemetry::nostd::variant &context_or_span, const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope, opentelemetry::logs::Severity severity, opentelemetry::nostd::string_view event_name) const noexcept @@ -88,7 +89,7 @@ bool MultiLogRecordProcessor::EnabledImplementation( for (const auto &processor : processors_) { if (processor != nullptr && - processor->Enabled(context, instrumentation_scope, severity, event_name)) + processor->Enabled(context_or_span, instrumentation_scope, severity, event_name)) { return true; } @@ -97,6 +98,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); diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc index 4fa3641fab..02c0921fe5 100644 --- a/sdk/test/logs/log_record_test.cc +++ b/sdk/test/logs/log_record_test.cc @@ -96,6 +96,8 @@ class TestBodyLogger : public opentelemetry::logs::Logger const nostd::string_view GetName() noexcept override { return "test body logger"; } + using opentelemetry::logs::Logger::CreateLogRecord; + nostd::unique_ptr CreateLogRecord() noexcept override { return nostd::unique_ptr(new ReadWriteLogRecord()); @@ -117,6 +119,22 @@ class TestBodyLogger : public opentelemetry::logs::Logger return last_body_; } +protected: + using opentelemetry::logs::Logger::EnabledImplementation; + + 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 ba2fe07ba4..a869c0ea7c 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -61,9 +61,9 @@ namespace context = opentelemetry::context; namespace logs_api = opentelemetry::logs; namespace nostd = opentelemetry::nostd; -#if OPENTELEMETRY_ABI_VERSION_NO >= 2 namespace { +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 nostd::shared_ptr MakeTestSpan(bool sampled) { const uint8_t trace_id_bytes[opentelemetry::trace::TraceId::kSize] = { @@ -95,8 +95,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 +273,9 @@ class MockProcessor final : public LogRecordProcessor }; #if OPENTELEMETRY_ABI_VERSION_NO >= 2 +namespace +{ + struct EnabledProcessorCallState { logs_api::Severity severity = logs_api::Severity::kInvalid; @@ -306,21 +309,30 @@ class EnablementAwareProcessor final : public LogRecordProcessor bool Shutdown(std::chrono::microseconds /* timeout */) noexcept override { return true; } protected: - bool EnabledImplementation(const context::Context &context, - const InstrumentationScope &instrumentation_scope, - logs_api::Severity severity, - nostd::string_view event_name) const noexcept override + bool EnabledImplementation( + const nostd::variant &context_or_span, + const InstrumentationScope &instrumentation_scope, + logs_api::Severity severity, + nostd::string_view event_name) const noexcept override { call_state_->severity = severity; call_state_->event_name = std::string(event_name); call_state_->scope_name = instrumentation_scope.GetName(); call_state_->call_count++; - auto value = context.GetValue("test-key"); - if (const bool *maybe_value = nostd::get_if(&value)) + if (const context::Context *ctx = nostd::get_if(&context_or_span)) { - call_state_->context_has_test_key = true; - call_state_->context_test_key_value = *maybe_value; + auto value = ctx->GetValue("test-key"); + if (const bool *maybe_value = nostd::get_if(&value)) + { + call_state_->context_has_test_key = true; + call_state_->context_test_key_value = *maybe_value; + } + else + { + call_state_->context_has_test_key = false; + call_state_->context_test_key_value = false; + } } else { @@ -335,6 +347,8 @@ class EnablementAwareProcessor final : public LogRecordProcessor bool enabled_; std::shared_ptr call_state_; }; + +} // namespace #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 TEST(LoggerSDK, LogToAProcessor) @@ -653,6 +667,95 @@ TEST(LoggerSDK, LoggerTraceBasedConfigAllowsSampledExplicitContextWithNamedEvent EXPECT_TRUE(call_state->context_test_key_value); EXPECT_EQ(call_state->call_count, 1U); } + +TEST(LoggerSDK, EmitLogRecordWithExplicitContextStampsTraceFieldsFromContext) +{ + auto api_lp = std::shared_ptr(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = api_lp->GetLogger("logger", "opentelemetry_library", "", schema_url); + + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto lp = static_cast(api_lp.get()); + lp->AddProcessor(std::unique_ptr(new MockProcessor(shared_recordable))); + + nostd::shared_ptr runtime_span; + { + std::vector> sp; + auto tp = opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(sp)); + runtime_span = tp->GetTracer("runtime")->StartSpan("runtime"); + } + opentelemetry::trace::Scope runtime_scope{runtime_span}; + + auto explicit_span = MakeTestSpan(/*sampled=*/true); + context::Context explicit_context; + explicit_context = opentelemetry::trace::SetSpan(explicit_context, explicit_span); + const auto explicit_span_ctx = explicit_span->GetContext(); + + logger->EmitLogRecord(logs_api::Severity::kInfo, nostd::string_view{"msg"}, explicit_context); + + EXPECT_EQ(shared_recordable->GetTraceId(), explicit_span_ctx.trace_id()); + EXPECT_EQ(shared_recordable->GetSpanId(), explicit_span_ctx.span_id()); + EXPECT_EQ(shared_recordable->GetTraceFlags(), explicit_span_ctx.trace_flags()); + EXPECT_NE(shared_recordable->GetTraceId(), runtime_span->GetContext().trace_id()); +} + +TEST(LoggerSDK, EmitLogRecordWithExplicitSpanContextStampsTraceFields) +{ + auto api_lp = std::shared_ptr(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = api_lp->GetLogger("logger", "opentelemetry_library", "", schema_url); + + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto lp = static_cast(api_lp.get()); + lp->AddProcessor(std::unique_ptr(new MockProcessor(shared_recordable))); + + nostd::shared_ptr runtime_span; + { + std::vector> sp; + auto tp = opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(sp)); + runtime_span = tp->GetTracer("runtime")->StartSpan("runtime"); + } + opentelemetry::trace::Scope runtime_scope{runtime_span}; + + const auto explicit_span_ctx = MakeTestSpan(true)->GetContext(); + + logger->EmitLogRecord(logs_api::Severity::kInfo, nostd::string_view{"msg"}, explicit_span_ctx); + + EXPECT_EQ(shared_recordable->GetTraceId(), explicit_span_ctx.trace_id()); + EXPECT_EQ(shared_recordable->GetSpanId(), explicit_span_ctx.span_id()); + EXPECT_EQ(shared_recordable->GetTraceFlags(), explicit_span_ctx.trace_flags()); + EXPECT_NE(shared_recordable->GetTraceId(), runtime_span->GetContext().trace_id()); +} + +TEST(LoggerSDK, EmitLogRecordWithExplicitTracePartsStampsTraceFields) +{ + auto api_lp = std::shared_ptr(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = api_lp->GetLogger("logger", "opentelemetry_library", "", schema_url); + + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto lp = static_cast(api_lp.get()); + lp->AddProcessor(std::unique_ptr(new MockProcessor(shared_recordable))); + + nostd::shared_ptr runtime_span; + { + std::vector> sp; + auto tp = opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(sp)); + runtime_span = tp->GetTracer("runtime")->StartSpan("runtime"); + } + opentelemetry::trace::Scope runtime_scope{runtime_span}; + + const auto explicit_span_ctx = MakeTestSpan(true)->GetContext(); + + logger->EmitLogRecord(logs_api::Severity::kInfo, nostd::string_view{"msg"}, + explicit_span_ctx.trace_id(), explicit_span_ctx.span_id(), + explicit_span_ctx.trace_flags()); + + EXPECT_EQ(shared_recordable->GetTraceId(), explicit_span_ctx.trace_id()); + EXPECT_EQ(shared_recordable->GetSpanId(), explicit_span_ctx.span_id()); + EXPECT_EQ(shared_recordable->GetTraceFlags(), explicit_span_ctx.trace_flags()); + EXPECT_NE(shared_recordable->GetTraceId(), runtime_span->GetContext().trace_id()); +} #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 static std::unique_ptr create_mock_log_recordable( diff --git a/sdk/test/logs/simple_log_record_processor_test.cc b/sdk/test/logs/simple_log_record_processor_test.cc index 2f9f0ca510..1716ba52ef 100644 --- a/sdk/test/logs/simple_log_record_processor_test.cc +++ b/sdk/test/logs/simple_log_record_processor_test.cc @@ -24,6 +24,7 @@ #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/recordable.h" #include "opentelemetry/sdk/logs/simple_log_record_processor.h" +#include "opentelemetry/trace/span_context.h" using namespace opentelemetry::sdk::logs; using namespace opentelemetry::sdk::common; @@ -292,14 +293,22 @@ class EnabledProcessor final : public LogRecordProcessor bool Shutdown(std::chrono::microseconds /* timeout */) noexcept override { return true; } protected: - bool EnabledImplementation(const context::Context &context, - const instrumentation_scope::InstrumentationScope &scope, - logs_api::Severity severity, - nostd::string_view event_name) const noexcept override + bool EnabledImplementation( + const nostd::variant &context_or_span, + const instrumentation_scope::InstrumentationScope &scope, + logs_api::Severity severity, + nostd::string_view event_name) const noexcept override { if (call_state_ != nullptr) { - call_state_->context = context; + if (const context::Context *ctx = nostd::get_if(&context_or_span)) + { + call_state_->context = *ctx; + } + else + { + call_state_->context = context::Context{}; + } call_state_->scope_name = scope.GetName(); call_state_->severity = severity; call_state_->event_name = std::string(event_name);