Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
21c3ab6
feat: examplar filters
proost Jul 18, 2026
b13986f
test: using mock
proost Jul 18, 2026
8dc295b
doc: update change log
proost Jul 18, 2026
f99c77b
style: follow lint
proost Jul 18, 2026
99d868a
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Jul 22, 2026
a12d4c3
ci: higher test timeout
proost Jul 24, 2026
d793892
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Jul 24, 2026
60b9440
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Jul 25, 2026
fd50a26
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Jul 28, 2026
5733f19
Merge branch 'main' into feat-exemplar-filters
dbarker Jul 29, 2026
ca5c78e
BREAKING: remove timestamp from OfferMeasurement
proost Jul 30, 2026
f4e1873
Merge branch 'feat-exemplar-filters' of github.com:proost/opentelemet…
proost Jul 30, 2026
27b0702
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Jul 30, 2026
af8d052
perf: use ExemplarFilterEnabled
proost Jul 31, 2026
30496c0
fix: remove span context validation
proost Jul 31, 2026
47625e6
style: rollback ctor order
proost Jul 31, 2026
cdac424
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Jul 31, 2026
acd26fd
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
proost Aug 1, 2026
5eddcd2
doc: update breaking change
proost Aug 1, 2026
058a013
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
proost Aug 2, 2026
685210e
Merge branch 'main' into feat-exemplar-filters
dbarker Aug 2, 2026
bcb6a0b
Merge branch 'main' into feat-exemplar-filters
dbarker Aug 3, 2026
4418990
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
proost Aug 4, 2026
d86e581
Merge branch 'feat-exemplar-filters' of https://github.com/proost/ope…
proost Aug 4, 2026
14c9c6b
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Aug 5, 2026
7bf2585
perf: remove allocation to heap
proost Aug 5, 2026
f979f57
Merge branch 'feat-exemplar-filters' of github.com:proost/opentelemet…
proost Aug 5, 2026
42a13bd
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Aug 5, 2026
3645456
doc: update changelog
proost Aug 5, 2026
b314b34
fix: offer with attributes
proost Aug 5, 2026
1d37a96
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
proost Aug 5, 2026
b95fecd
Merge branch 'main' into feat-exemplar-filters
dbarker Aug 8, 2026
de91279
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
proost Aug 10, 2026
774ad44
Merge branch 'feat-exemplar-filters' of https://github.com/proost/ope…
proost Aug 10, 2026
e2dc0f4
Merge branch 'main' into feat-exemplar-filters
marcalff Aug 11, 2026
0e5c5de
Merge branch 'main' of github.com:open-telemetry/opentelemetry-cpp in…
proost Aug 12, 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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ Increment the:
`trace_based` to match the specification.
[#4328](https://github.com/open-telemetry/opentelemetry-cpp/pull/4328)

* [SDK] Complete exemplar filtering: the exemplar filter(`AlwaysOn`/
Comment thread
proost marked this conversation as resolved.
`AlwaysOff`/`TraceBased`)
[#4267](https://github.com/open-telemetry/opentelemetry-cpp/pull/4267)

Breaking changes:

* [CONFIGURATION] SDK default component builder libraries and example
Expand Down Expand Up @@ -231,6 +235,18 @@ Breaking changes:
rebuilt.
[#4328](https://github.com/open-telemetry/opentelemetry-cpp/pull/4328)

* [METRICS SDK] Remove the `SystemTimestamp` parameter from the preview
`ExemplarReservoir::OfferMeasurement()` overloads
[#4267](https://github.com/open-telemetry/opentelemetry-cpp/pull/4267)
* This is an incompatible API and ABI change for custom exemplar reservoirs.
Implementations and callers must remove the timestamp parameter.

* [METRICS SDK] Breaking change to the preview metrics exemplar surface: the
`SyncMetricStorage`/`AsyncMetricStorage` constructors now take an
`ExemplarFilterType`, and `ExemplarData::Create` takes the `SpanContext`
by value.
[#4267](https://github.com/open-telemetry/opentelemetry-cpp/pull/4267)

## [1.28.0] 2026-07-16

* [RELEASE] Bump main branch to 1.28.0-dev
Expand Down
2 changes: 1 addition & 1 deletion ci/do_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ elif [[ "$1" == "bazel.tsan" ]]; then
exit 0
elif [[ "$1" == "bazel.valgrind" ]]; then
bazel $BAZEL_STARTUP_OPTIONS build $BAZEL_OPTIONS_ASYNC //...
bazel $BAZEL_STARTUP_OPTIONS test --test_timeout=600 --run_under="/usr/bin/valgrind --leak-check=full --error-exitcode=1 --errors-for-leak-kinds=definite --suppressions=\"${SRC_DIR}/ci/valgrind-suppressions\"" $BAZEL_TEST_OPTIONS_ASYNC //...
bazel $BAZEL_STARTUP_OPTIONS test --test_timeout=900 --run_under="/usr/bin/valgrind --leak-check=full --error-exitcode=1 --errors-for-leak-kinds=definite --suppressions=\"${SRC_DIR}/ci/valgrind-suppressions\"" $BAZEL_TEST_OPTIONS_ASYNC //...
exit 0
elif [[ "$1" == "benchmark" ]]; then
[ -z "${BENCHMARK_DIR}" ] && export BENCHMARK_DIR=$HOME/benchmark
Expand Down
8 changes: 3 additions & 5 deletions sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#pragma once

#include <memory>

#include "opentelemetry/common/timestamp.h"
#include "opentelemetry/sdk/metrics/data/metric_data.h"
#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h"
Expand All @@ -26,7 +24,7 @@ using MetricAttributes = opentelemetry::sdk::metrics::FilteredOrderedAttributeMa
class ExemplarData
{
public:
static ExemplarData Create(const std::shared_ptr<opentelemetry::trace::SpanContext> &context,
static ExemplarData Create(const opentelemetry::trace::SpanContext &context,
const opentelemetry::common::SystemTimestamp &timestamp,
const PointDataAttributes &point_data_attr)
{
Expand Down Expand Up @@ -68,10 +66,10 @@ class ExemplarData
static PointType CreateDropPointData() { return DropPointData{}; }

private:
ExemplarData(const std::shared_ptr<opentelemetry::trace::SpanContext> &context,
ExemplarData(const opentelemetry::trace::SpanContext &context,
opentelemetry::common::SystemTimestamp timestamp,
const PointDataAttributes &point_data_attr)
: context_(*context.get()), timestamp_(timestamp), point_data_attr_(point_data_attr)
: context_(context), timestamp_(timestamp), point_data_attr_(point_data_attr)
{}

opentelemetry::trace::SpanContext context_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW

# include "opentelemetry/context/context.h"
# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/trace/context.h"
# include "opentelemetry/trace/span_context.h"
# include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace metrics
{

/**
* Decides whether a measurement is eligible to be offered to the exemplar reservoir.
*
* - kAlwaysOff : no measurement is eligible (exemplar sampling disabled).
* - kAlwaysOn : every measurement is eligible.
* - kTraceBased : eligible only when recorded inside a valid, sampled span.
*/
inline bool ExemplarFilterEnabled(ExemplarFilterType filter_type,
const opentelemetry::context::Context &context) noexcept
{
switch (filter_type)
{
case ExemplarFilterType::kAlwaysOn:
return true;
case ExemplarFilterType::kAlwaysOff:
return false;
default: // TraceBased.
const opentelemetry::trace::SpanContext span_context =
opentelemetry::trace::GetSpanContext(context);
return span_context.IsValid() && span_context.IsSampled();
}
}

} // namespace metrics
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE

#endif // ENABLE_METRICS_EXEMPLAR_PREVIEW
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ class FixedSizeExemplarReservoir : public ExemplarReservoir

using ExemplarReservoir::OfferMeasurement;

void OfferMeasurement(
int64_t value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context,
const opentelemetry::common::SystemTimestamp & /* timestamp */) noexcept override
void OfferMeasurement(int64_t value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context) noexcept override
{
if (!reservoir_cell_selector_)
{
Expand All @@ -54,11 +52,9 @@ class FixedSizeExemplarReservoir : public ExemplarReservoir
}
}

void OfferMeasurement(
double value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context,
const opentelemetry::common::SystemTimestamp & /* timestamp */) noexcept override
void OfferMeasurement(double value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context) noexcept override
{
if (!reservoir_cell_selector_)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,16 @@ class NoExemplarReservoir final : public ExemplarReservoir
{

public:
void OfferMeasurement(
int64_t /* value */,
const MetricAttributes & /* attributes */,
const opentelemetry::context::Context & /* context */,
const opentelemetry::common::SystemTimestamp & /* timestamp */) noexcept override
void OfferMeasurement(int64_t /* value */,
const MetricAttributes & /* attributes */,
const opentelemetry::context::Context & /* context */) noexcept override
{
// Stores nothing
}

void OfferMeasurement(
double /* value */,
const MetricAttributes & /* attributes */,
const opentelemetry::context::Context & /* context */,
const opentelemetry::common::SystemTimestamp & /* timestamp */) noexcept override
void OfferMeasurement(double /* value */,
const MetricAttributes & /* attributes */,
const opentelemetry::context::Context & /* context */) noexcept override
{
// Stores nothing.
}
Expand Down
21 changes: 6 additions & 15 deletions sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# include <memory>
# include <vector>

# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h"
# include "opentelemetry/version.h"

Expand Down Expand Up @@ -47,18 +46,14 @@ class ExemplarReservoir
virtual ~ExemplarReservoir() = default;

/** Offers a long measurement to be sampled. */
virtual void OfferMeasurement(
int64_t value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context,
const opentelemetry::common::SystemTimestamp &timestamp) noexcept = 0;
virtual void OfferMeasurement(int64_t value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context) noexcept = 0;

/** Offers a double measurement to be sampled. */
virtual void OfferMeasurement(
double value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context,
const opentelemetry::common::SystemTimestamp &timestamp) noexcept = 0;
virtual void OfferMeasurement(double value,
const MetricAttributes &attributes,
const opentelemetry::context::Context &context) noexcept = 0;

/**
* Builds vector of Exemplars for exporting from the current reservoir.
Expand All @@ -73,10 +68,6 @@ class ExemplarReservoir
virtual std::vector<std::shared_ptr<ExemplarData>> CollectAndReset(
const MetricAttributes &pointAttributes) noexcept = 0;

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

static nostd::shared_ptr<ExemplarReservoir> GetSimpleFixedSizeExemplarReservoir(
size_t size,
const std::shared_ptr<ReservoirCellSelector> &reservoir_cell_selector,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class ReservoirCell
*/
std::shared_ptr<ExemplarData> GetAndResetLong(const MetricAttributes &point_attributes)
{
if (!context_)
if (!populated_)
{
return nullptr;
}
Expand All @@ -88,7 +88,7 @@ class ReservoirCell
*/
std::shared_ptr<ExemplarData> GetAndResetDouble(const MetricAttributes &point_attributes)
{
if (!context_)
if (!populated_)
{
return nullptr;
}
Expand All @@ -109,6 +109,7 @@ class ReservoirCell
{
value_ = 0.0;
record_time_ = opentelemetry::common::SystemTimestamp{};
populated_ = false;
}

private:
Expand All @@ -134,15 +135,13 @@ class ReservoirCell
{
attributes_ = attributes;
record_time_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now());
const auto current_ctx = opentelemetry::trace::GetSpanContext(context);
if (current_ctx.IsValid())
{
context_.reset(new opentelemetry::trace::SpanContext{current_ctx});
}
context_ = opentelemetry::trace::GetSpanContext(context);
populated_ = true;
}

// Cell stores either long or double values, but must not store both
std::shared_ptr<opentelemetry::trace::SpanContext> context_;
bool populated_ = false;
opentelemetry::trace::SpanContext context_ = opentelemetry::trace::SpanContext::GetInvalid();
nostd::variant<int64_t, double> value_;
opentelemetry::common::SystemTimestamp record_time_;
MetricAttributes attributes_;
Expand Down
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 @@ -48,8 +50,14 @@ 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 (filter_type == ExemplarFilterType::kAlwaysOff)
{
return ExemplarReservoir::GetNoExemplarReservoir();
}

if (agg_type == AggregationType::kHistogram)
{
const auto *histogram_agg_config = static_cast<const HistogramAggregationConfig *>(agg_config);
Expand All @@ -61,18 +69,18 @@ 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(
return ExemplarReservoir::GetAlignedHistogramBucketExemplarReservoir(
histogram_agg_config->boundaries_.size(),
AlignedHistogramBucketExemplarReservoir::GetHistogramCellSelector(
histogram_agg_config->boundaries_),
GetMapAndResetCellMethod(instrument_descriptor)));
GetMapAndResetCellMethod(instrument_descriptor));
}
}

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

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
# include "opentelemetry/sdk/metrics/exemplar/filter_predicate.h"
# include "opentelemetry/sdk/metrics/exemplar/filter_type.h"
# include "opentelemetry/sdk/metrics/exemplar/reservoir.h"
#endif
Expand All @@ -37,7 +38,7 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora
AsyncMetricStorage(const InstrumentDescriptor &instrument_descriptor,
const AggregationType aggregation_type,
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
ExemplarFilterType exempler_filter_type,
ExemplarFilterType exemplar_filter_type,
nostd::shared_ptr<ExemplarReservoir> &&exemplar_reservoir,
#endif
const AggregationConfig *aggregation_config)
Expand All @@ -49,7 +50,7 @@ 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_filter_type_(exemplar_filter_type),
exemplar_reservoir_(std::move(exemplar_reservoir)),
#endif
temporal_metric_storage_(instrument_descriptor, aggregation_type, aggregation_config)
Expand All @@ -63,13 +64,16 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora
// exporter/reader can request either for delta or cumulative value.
// So we convert the async counter value to delta before passing it to temporal storage.
std::lock_guard<opentelemetry::common::SpinLockMutex> guard(hashmap_lock_);
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
const bool offer_exemplars =
ExemplarFilterEnabled(exemplar_filter_type_, opentelemetry::context::Context{});
#endif
for (auto &measurement : measurements)
{
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
if (exemplar_filter_type_ == ExemplarFilterType::kAlwaysOn)
if (offer_exemplars)
{
exemplar_reservoir_->OfferMeasurement(measurement.second, {}, {},
std::chrono::system_clock::now());
exemplar_reservoir_->OfferMeasurement(measurement.second, measurement.first, {});
}
Comment thread
proost marked this conversation as resolved.
#endif

Expand Down
Loading
Loading