Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c7360f5
fix: apply filtering in emit log record
proost May 12, 2026
46c13c8
refactor: filter once
proost May 12, 2026
849c52f
Merge branch 'fix-apply-filtering-emitlogrecord' of https://github.co…
proost May 12, 2026
47d1e37
refactor: enabled filtering on the API level
proost May 13, 2026
a0958a4
feat: use context for EmitLogRecord
proost May 14, 2026
355ac95
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost May 14, 2026
98b3e9c
doc: update changelog
proost May 14, 2026
05b865b
style: follow doc lint
proost May 14, 2026
8063cb6
doc: follow doc lint
proost May 14, 2026
efa0202
Merge branch 'fix-apply-filtering-emitlogrecord' of https://github.co…
proost May 14, 2026
17de264
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
proost May 14, 2026
b9871a4
style: follow doc lint
proost May 14, 2026
5e67680
perf: cached enabled required
proost May 15, 2026
80051a4
Merge branch 'fix-apply-filtering-emitlogrecord' of github.com:proost…
proost May 15, 2026
ff7e302
Merge branch 'main' into fix-apply-filtering-emitlogrecord
ThomsonTan May 15, 2026
dc306d5
feat: has enabled filter
proost May 16, 2026
1a8111f
Merge branch 'fix-apply-filtering-emitlogrecord' of github.com:proost…
proost May 16, 2026
89c8209
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost May 17, 2026
d4578ad
feat: use temp context
proost May 17, 2026
d62c8e4
refactor: unify function
proost May 17, 2026
67713e3
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost May 19, 2026
e717aab
refactor: change default to false
proost May 19, 2026
8a18dc7
refactor: use span context and context
proost May 20, 2026
c1997fe
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost May 20, 2026
d719d99
style: follow lint
proost May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/clang-tidy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: 392
env:
CC: /usr/bin/clang-22
CXX: /usr/bin/clang++-22
Expand Down
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,39 @@ 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,
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)`. 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
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.

* [API/SDK] Replace `Context`-only signatures on
`LogRecordProcessor::Enabled`,
`LogRecordProcessor::EnabledImplementation`,
`Logger::EnabledImplementation` (v2), and `Logger::CreateLogRecord` (v2)
with `nostd::variant<trace::SpanContext, context::Context>`. The
`EmitLogRecord(args...)` template now builds the variant in place from
trace parts in args, avoiding the per-emit `DefaultSpan` heap allocation
that the previous synthesis required. Same precedent as
`trace::StartSpanOptions::parent`. SDK ABI break.

## [1.27.0] 2026-05-13

* [RELEASE] Bump main branch to 1.27.0-dev
Expand Down
175 changes: 162 additions & 13 deletions api/include/opentelemetry/logs/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
#include "opentelemetry/nostd/unique_ptr.h"
#include "opentelemetry/version.h"

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
# 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
{
Expand Down Expand Up @@ -50,6 +56,23 @@ class Logger
*/
virtual nostd::unique_ptr<LogRecord> 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. Avoids allocating a Context purely to
* propagate trace identity.
* @return nostd::unique_ptr<LogRecord>
*/
virtual nostd::unique_ptr<LogRecord> CreateLogRecord(
const nostd::variant<trace::SpanContext, opentelemetry::context::Context> &
/*context_or_span*/) noexcept
{
return CreateLogRecord();
}
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2

/**
* Emit a Log Record object
*
Expand All @@ -58,7 +81,15 @@ class Logger
virtual void EmitLogRecord(nostd::unique_ptr<LogRecord> &&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.
Expand Down Expand Up @@ -120,11 +151,101 @@ class Logger
* KeyValueIterable -> attributes
* Key value iterable container -> attributes
* span<pair<string_view, AttributeValue>> -> 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 <class... ArgumentType>
void EmitLogRecord(ArgumentType &&...args)
{
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
nostd::variant<trace::SpanContext, opentelemetry::context::Context> context_or_span =
trace::SpanContext::GetInvalid();
bool has_context_or_span_arg = false;

if (const opentelemetry::context::Context *ctx = detail::FindContextInArgs(args...))
{
context_or_span = *ctx;
has_context_or_span_arg = true;
}
else if (const trace::SpanContext *sc = detail::FindSpanContextInArgs(args...))
{
context_or_span = *sc;
has_context_or_span_arg = true;
}
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);
has_context_or_span_arg = true;
}
}
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2

const Severity arg_severity = detail::FindSeverityInArgs(args...);
if (arg_severity != Severity::kInvalid)
{
if (!Enabled(arg_severity))
{
return;
}

if (ExtendedEnabledRequired())
{
const EventId *event_id_ptr = detail::FindEventIdInArgs(args...);
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
bool extended_enabled;
if (has_context_or_span_arg)
{
if (event_id_ptr)
{
extended_enabled = EnabledImplementation(context_or_span, arg_severity, *event_id_ptr);
}
else
{
extended_enabled = EnabledImplementation(context_or_span, arg_severity);
}
}
else
{
if (event_id_ptr)
{
extended_enabled = EnabledImplementation(arg_severity, *event_id_ptr);
}
else
{
extended_enabled = EnabledImplementation(arg_severity, static_cast<int64_t>(0));
}
}
#else
const bool extended_enabled =
event_id_ptr ? EnabledImplementation(arg_severity, *event_id_ptr)
: EnabledImplementation(arg_severity, static_cast<int64_t>(0));
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2
if (!extended_enabled)
{
return;
}
}
}

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
nostd::unique_ptr<LogRecord> log_record =
has_context_or_span_arg ? CreateLogRecord(context_or_span) : CreateLogRecord();
#else
nostd::unique_ptr<LogRecord> log_record = CreateLogRecord();
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2

EmitLogRecord(std::move(log_record), std::forward<ArgumentType>(args)...);
}
Expand Down Expand Up @@ -278,25 +399,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<trace::SpanContext, opentelemetry::context::Context> &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<trace::SpanContext, opentelemetry::context::Context> &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

Expand All @@ -323,6 +446,11 @@ class Logger
return static_cast<uint8_t>(severity) >= OPENTELEMETRY_ATOMIC_READ_8(&minimum_severity_);
}

inline bool ExtendedEnabledRequired() const noexcept
{
return OPENTELEMETRY_ATOMIC_READ_8(&extended_enabled_required_) != 0;
}

/**
* Log an event
*
Expand Down Expand Up @@ -505,15 +633,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<trace::SpanContext, opentelemetry::context::Context> &
/*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<trace::SpanContext, opentelemetry::context::Context> &
/*context_or_span*/,
Severity /*severity*/,
const EventId & /*event_id*/) const noexcept
{
return false;
}
Expand All @@ -524,6 +656,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<uint8_t>(required ? 1 : 0));
}

private:
template <class... ValueType>
void IgnoreTraitResult(ValueType &&...)
Expand All @@ -535,6 +673,17 @@ class Logger
// compatible for OpenTelemetry C++ API.
//
mutable uint8_t minimum_severity_{kMaxSeverity};

//
// 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};
};
} // namespace logs
OPENTELEMETRY_END_NAMESPACE
Loading
Loading