Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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: 418
warning_limit: 416
- cmake_options: all-options-abiv2-preview
warning_limit: 424
warning_limit: 420
env:
CC: /usr/bin/clang-22
CXX: /usr/bin/clang++-22
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/iwyu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ Increment the:
* [RELEASE] Bump main branch to 1.28.0-dev
[#4081](https://github.com/open-telemetry/opentelemetry-cpp/pull/4081)

* [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)

* [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
Expand Down
83 changes: 82 additions & 1 deletion api/include/opentelemetry/logs/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ class Logger
*/
virtual nostd::unique_ptr<LogRecord> 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<LogRecord>
*/
virtual nostd::unique_ptr<LogRecord> CreateLogRecord(
const opentelemetry::context::Context & /*context*/) noexcept
{
return CreateLogRecord();
}
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2

/**
* Emit a Log Record object
*
Expand All @@ -58,7 +72,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 +142,52 @@ 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)
*
*/
template <class... ArgumentType>
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)
{
if (!Enabled(arg_severity))
{
return;
}

if (ExtendedEnabledRequired())
{
const EventId *event_id_ptr = detail::FindEventIdInArgs(args...);
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
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<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 =
context_ptr ? CreateLogRecord(*context_ptr) : 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 @@ -323,6 +386,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 @@ -524,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<uint8_t>(required ? 1 : 0));
}

private:
template <class... ValueType>
void IgnoreTraitResult(ValueType &&...)
Expand All @@ -535,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
85 changes: 85 additions & 0 deletions api/include/opentelemetry/logs/logger_type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -143,6 +147,21 @@ struct LogRecordSetterTrait<common::KeyValueIterable>
}
};

#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<opentelemetry::context::Context>
{
template <class ArgumentType>
inline static LogRecord *Set(LogRecord *log_record, ArgumentType && /*arg*/) noexcept
{
return log_record;
}
};
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2

template <class ValueType>
struct LogRecordSetterTrait
{
Expand Down Expand Up @@ -198,6 +217,72 @@ struct LogRecordHasType<ValueType, TargetType, ArgumentType...>
LogRecordHasType<ValueType, ArgumentType...>>::type
{};

inline Severity FindSeverityInArgs() noexcept
{
return Severity::kInvalid;
}

template <class... Rest>
inline Severity FindSeverityInArgs(Severity severity, Rest &&.../*rest*/) noexcept
{
return severity;
}

template <class First,
class... Rest,
typename std::enable_if<!std::is_same<typename std::decay<First>::type, Severity>::value,
int>::type = 0>
inline Severity FindSeverityInArgs(First && /*first*/, Rest &&...rest) noexcept
{
return FindSeverityInArgs(std::forward<Rest>(rest)...);
}

inline const EventId *FindEventIdInArgs() noexcept
{
return nullptr;
}

template <class... Rest>
inline const EventId *FindEventIdInArgs(const EventId &event_id, Rest &&.../*rest*/) noexcept
{
return &event_id;
}

template <class First,
class... Rest,
typename std::enable_if<!std::is_same<typename std::decay<First>::type, EventId>::value,
int>::type = 0>
inline const EventId *FindEventIdInArgs(First && /*first*/, Rest &&...rest) noexcept
{
return FindEventIdInArgs(std::forward<Rest>(rest)...);
}

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
inline const opentelemetry::context::Context *FindContextInArgs() noexcept
{
return nullptr;
}

template <class... Rest>
inline const opentelemetry::context::Context *FindContextInArgs(
const opentelemetry::context::Context &context,
Rest &&.../*rest*/) noexcept
{
return &context;
}

template <class First,
class... Rest,
typename std::enable_if<!std::is_same<typename std::decay<First>::type,
opentelemetry::context::Context>::value,
int>::type = 0>
inline const opentelemetry::context::Context *FindContextInArgs(First && /*first*/,
Rest &&...rest) noexcept
{
return FindContextInArgs(std::forward<Rest>(rest)...);
}
#endif // OPENTELEMETRY_ABI_VERSION_NO >= 2

} // namespace detail

} // namespace logs
Expand Down
Loading
Loading