Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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