Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ Increment the:
* [CONFIGURATION] Apply default sampler when none is specified
[#4170](https://github.com/open-telemetry/opentelemetry-cpp/pull/4170)

* [API] Add `trace::GetSpanContext()` to read the active span's `SpanContext`
from a `Context`; reuse it in the logs SDK to remove a duplicated helper.
[#4191](https://github.com/open-telemetry/opentelemetry-cpp/pull/4191)

* [SDK] Apply the `ExemplarFilter` (AlwaysOn/AlwaysOff/TraceBased) via
`ExemplarReservoir::GetSimpleFilteredExemplarReservoir()`.
[#4191](https://github.com/open-telemetry/opentelemetry-cpp/pull/4191)

## [1.27.0] 2026-05-13

* [RELEASE] Bump main branch to 1.27.0-dev
Expand Down
27 changes: 27 additions & 0 deletions api/include/opentelemetry/trace/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,33 @@ inline nostd::shared_ptr<Span> GetSpan(const context::Context &context) noexcept
return nostd::shared_ptr<Span>(new DefaultSpan(SpanContext::GetInvalid()));
}

// Get the SpanContext of the active span from explicit context. Falls back to a SpanContext
// stored directly in the context, and returns an invalid SpanContext if neither is present.
inline SpanContext GetSpanContext(const context::Context &context) noexcept
{
if (!context.HasKey(kSpanKey))
{
return SpanContext::GetInvalid();
}

const context::ContextValue context_value = context.GetValue(kSpanKey);

// Get the span metadata from the active span in the context.
if (const nostd::shared_ptr<Span> *maybe_span =
nostd::get_if<nostd::shared_ptr<Span>>(&context_value))
{
return (*maybe_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<SpanContext> *maybe_span_context =
nostd::get_if<nostd::shared_ptr<SpanContext>>(&context_value))
{
return **maybe_span_context;
}
return SpanContext::GetInvalid();
}

// Check if the context is from a root span
inline bool IsRootSpan(const context::Context &context) noexcept
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW

# include <stdint.h>
# include <memory>
# include <utility>
# include <vector>

# include "opentelemetry/common/timestamp.h"
# include "opentelemetry/context/context.h"
# include "opentelemetry/nostd/shared_ptr.h"
# include "opentelemetry/sdk/metrics/data/exemplar_data.h"
# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/sdk/metrics/exemplar/reservoir.h"
# include "opentelemetry/trace/context.h"
# include "opentelemetry/trace/span_context.h"
# include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace metrics
{

/**
* A reservoir that pre-filters measurements according to an ExemplarFilterType before
* delegating eligible ones to a wrapped reservoir.
*/
class FilteredExemplarReservoir final : public ExemplarReservoir
{
public:
FilteredExemplarReservoir(ExemplarFilterType filter_type,
nostd::shared_ptr<ExemplarReservoir> reservoir)
: should_sample_(SelectFilter(filter_type)), reservoir_(std::move(reservoir))
{}

void OfferMeasurement(int64_t value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context,
const opentelemetry::common::SystemTimestamp &timestamp) noexcept override
{
if (should_sample_(context))
{
reservoir_->OfferMeasurement(value, attributes, context, timestamp);
}
}

void OfferMeasurement(double value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context,
const opentelemetry::common::SystemTimestamp &timestamp) noexcept override
{
if (should_sample_(context))
{
reservoir_->OfferMeasurement(value, attributes, context, timestamp);
}
}

std::vector<std::shared_ptr<ExemplarData>> CollectAndReset(
const MetricAttributes &pointAttributes) noexcept override
{
return reservoir_->CollectAndReset(pointAttributes);
}

private:
using ShouldSampleFn = bool (*)(const opentelemetry::context::Context &context);

static bool AlwaysOn(const opentelemetry::context::Context & /* context */) noexcept
{
return true;
}

static bool AlwaysOff(const opentelemetry::context::Context & /* context */) noexcept
{
return false;
}

static bool TraceBased(const opentelemetry::context::Context &context) noexcept
{
const opentelemetry::trace::SpanContext span_context =
opentelemetry::trace::GetSpanContext(context);
return span_context.IsValid() && span_context.IsSampled();
}

static ShouldSampleFn SelectFilter(ExemplarFilterType filter_type) noexcept
{
Comment on lines +72 to +90
switch (filter_type)
{
case ExemplarFilterType::kAlwaysOn:
return &AlwaysOn;
case ExemplarFilterType::kAlwaysOff:
return &AlwaysOff;
case ExemplarFilterType::kTraceBased:
return &TraceBased;
}
return &TraceBased; // unreachable; all enumerators handled above
}

ShouldSampleFn should_sample_;
nostd::shared_ptr<ExemplarReservoir> reservoir_;
};

} // namespace metrics
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE

#endif // ENABLE_METRICS_EXEMPLAR_PREVIEW
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class ExemplarReservoir

static nostd::shared_ptr<ExemplarReservoir> GetSimpleFilteredExemplarReservoir(
ExemplarFilterType filter_type,
std::shared_ptr<ExemplarReservoir> reservoir);
nostd::shared_ptr<ExemplarReservoir> reservoir);

static nostd::shared_ptr<ExemplarReservoir> GetSimpleFixedSizeExemplarReservoir(
size_t size,
Expand Down
26 changes: 16 additions & 10 deletions sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# include "opentelemetry/common/macros.h"
# include "opentelemetry/sdk/metrics/aggregation/aggregation_config.h"
# include "opentelemetry/sdk/metrics/exemplar/aligned_histogram_bucket_exemplar_reservoir.h"
# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/sdk/metrics/exemplar/reservoir.h"
# include "opentelemetry/sdk/metrics/exemplar/simple_fixed_size_exemplar_reservoir.h"
# include "opentelemetry/version.h"

Expand Down Expand Up @@ -49,7 +51,8 @@ static inline size_t GetSimpleFixedReservoirDefaultSize(const AggregationType ag
static inline nostd::shared_ptr<ExemplarReservoir> GetExemplarReservoir(
const AggregationType agg_type,
const AggregationConfig *agg_config,
const InstrumentDescriptor &instrument_descriptor)
const InstrumentDescriptor &instrument_descriptor,
ExemplarFilterType filter_type)
{
if (agg_type == AggregationType::kHistogram)
{
Expand All @@ -62,18 +65,21 @@ static inline nostd::shared_ptr<ExemplarReservoir> GetExemplarReservoir(
//
if (histogram_agg_config != nullptr && histogram_agg_config->boundaries_.size() > 1)
{
return nostd::shared_ptr<ExemplarReservoir>(new AlignedHistogramBucketExemplarReservoir(
histogram_agg_config->boundaries_.size(),
AlignedHistogramBucketExemplarReservoir::GetHistogramCellSelector(
histogram_agg_config->boundaries_),
GetMapAndResetCellMethod(instrument_descriptor)));
return ExemplarReservoir::GetSimpleFilteredExemplarReservoir(
filter_type,
nostd::shared_ptr<ExemplarReservoir>(new AlignedHistogramBucketExemplarReservoir(
histogram_agg_config->boundaries_.size(),
AlignedHistogramBucketExemplarReservoir::GetHistogramCellSelector(
histogram_agg_config->boundaries_),
GetMapAndResetCellMethod(instrument_descriptor))));
}
}

return nostd::shared_ptr<ExemplarReservoir>(new SimpleFixedSizeExemplarReservoir(
GetSimpleFixedReservoirDefaultSize(agg_type, agg_config),
SimpleFixedSizeExemplarReservoir::GetSimpleFixedSizeCellSelector(),
GetMapAndResetCellMethod(instrument_descriptor)));
return ExemplarReservoir::GetSimpleFilteredExemplarReservoir(
filter_type, nostd::shared_ptr<ExemplarReservoir>(new SimpleFixedSizeExemplarReservoir(
GetSimpleFixedReservoirDefaultSize(agg_type, agg_config),
SimpleFixedSizeExemplarReservoir::GetSimpleFixedSizeCellSelector(),
GetMapAndResetCellMethod(instrument_descriptor))));
}
} // namespace metrics
} // namespace sdk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h"

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/sdk/metrics/exemplar/reservoir.h"
#endif

Expand All @@ -37,7 +36,6 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora
AsyncMetricStorage(const InstrumentDescriptor &instrument_descriptor,
const AggregationType aggregation_type,
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
ExemplarFilterType exempler_filter_type,
nostd::shared_ptr<ExemplarReservoir> &&exemplar_reservoir,
#endif
const AggregationConfig *aggregation_config)
Expand All @@ -49,7 +47,6 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora
delta_hash_map_(
std::make_unique<AttributesHashMap>(aggregation_config_->cardinality_limit_)),
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
exemplar_filter_type_(exempler_filter_type),
exemplar_reservoir_(std::move(exemplar_reservoir)),
#endif
temporal_metric_storage_(instrument_descriptor, aggregation_type, aggregation_config)
Expand All @@ -66,11 +63,8 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora
for (auto &measurement : measurements)
{
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
if (exemplar_filter_type_ == ExemplarFilterType::kAlwaysOn)
{
exemplar_reservoir_->OfferMeasurement(measurement.second, {}, {},
std::chrono::system_clock::now());
}
exemplar_reservoir_->OfferMeasurement(measurement.second, {}, {},
std::chrono::system_clock::now());
#endif

auto aggr = DefaultAggregation::CreateAggregation(aggregation_type_, instrument_descriptor_);
Expand Down Expand Up @@ -146,7 +140,6 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora
std::unique_ptr<AttributesHashMap> delta_hash_map_;
opentelemetry::common::SpinLockMutex hashmap_lock_;
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
ExemplarFilterType exemplar_filter_type_;
nostd::shared_ptr<ExemplarReservoir> exemplar_reservoir_;
#endif
TemporalMetricStorage temporal_metric_storage_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#endif

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/sdk/metrics/exemplar/reservoir.h"
#endif

Expand All @@ -48,25 +47,11 @@ namespace metrics
class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
{

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW

static inline bool EnableExamplarFilter(ExemplarFilterType filter_type,
const opentelemetry::context::Context &context)
{
return filter_type == ExemplarFilterType::kAlwaysOn ||
(filter_type == ExemplarFilterType::kTraceBased &&
opentelemetry::trace::GetSpan(context)->GetContext().IsValid() &&
opentelemetry::trace::GetSpan(context)->GetContext().IsSampled());
}

#endif // ENABLE_METRICS_EXEMPLAR_PREVIEW

public:
SyncMetricStorage(const InstrumentDescriptor &instrument_descriptor,
const AggregationType aggregation_type,
std::shared_ptr<const AttributesProcessor> attributes_processor,
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
ExemplarFilterType exempler_filter_type,
nostd::shared_ptr<ExemplarReservoir> &&exemplar_reservoir,
#endif
const AggregationConfig *aggregation_config)
Expand All @@ -76,7 +61,6 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
std::make_unique<AttributesHashMap>(aggregation_config_->cardinality_limit_)),
attributes_processor_(std::move(attributes_processor)),
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
exemplar_filter_type_(exempler_filter_type),
exemplar_reservoir_(std::move(exemplar_reservoir)),
#endif
temporal_metric_storage_(instrument_descriptor, aggregation_type, aggregation_config)
Expand All @@ -97,10 +81,7 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
return;
}
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
if (EnableExamplarFilter(exemplar_filter_type_, context))
{
exemplar_reservoir_->OfferMeasurement(value, {}, context, std::chrono::system_clock::now());
}
exemplar_reservoir_->OfferMeasurement(value, {}, context, std::chrono::system_clock::now());
#endif
static MetricAttributes attr = MetricAttributes{};
std::lock_guard<opentelemetry::common::SpinLockMutex> guard(attribute_hashmap_lock_);
Expand All @@ -123,11 +104,8 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
return;
}
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
if (EnableExamplarFilter(exemplar_filter_type_, context))
{
exemplar_reservoir_->OfferMeasurement(value, attributes, context,
std::chrono::system_clock::now());
}
exemplar_reservoir_->OfferMeasurement(value, attributes, context,
std::chrono::system_clock::now());
#endif

MetricAttributes attr{attributes, attributes_processor_.get()};
Expand Down Expand Up @@ -155,10 +133,7 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
return;
}
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
if (EnableExamplarFilter(exemplar_filter_type_, context))
{
exemplar_reservoir_->OfferMeasurement(value, {}, context, std::chrono::system_clock::now());
}
exemplar_reservoir_->OfferMeasurement(value, {}, context, std::chrono::system_clock::now());
#endif
static MetricAttributes attr = MetricAttributes{};
std::lock_guard<opentelemetry::common::SpinLockMutex> guard(attribute_hashmap_lock_);
Expand All @@ -181,11 +156,8 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
return;
}
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
if (EnableExamplarFilter(exemplar_filter_type_, context))
{
exemplar_reservoir_->OfferMeasurement(value, attributes, context,
std::chrono::system_clock::now());
}
exemplar_reservoir_->OfferMeasurement(value, attributes, context,
std::chrono::system_clock::now());
#endif
MetricAttributes attr{attributes, attributes_processor_.get()};
std::lock_guard<opentelemetry::common::SpinLockMutex> guard(attribute_hashmap_lock_);
Expand Down Expand Up @@ -298,7 +270,6 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage
std::function<std::unique_ptr<Aggregation>()> create_default_aggregation_;
std::shared_ptr<const AttributesProcessor> attributes_processor_;
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
ExemplarFilterType exemplar_filter_type_;
nostd::shared_ptr<ExemplarReservoir> exemplar_reservoir_;
#endif
TemporalMetricStorage temporal_metric_storage_;
Expand Down
Loading
Loading