Skip to content

[API/SDK] feat: exemplar filters#4191

Open
proost wants to merge 9 commits into
open-telemetry:mainfrom
proost:feat-exemplar-filters
Open

[API/SDK] feat: exemplar filters#4191
proost wants to merge 9 commits into
open-telemetry:mainfrom
proost:feat-exemplar-filters

Conversation

@proost

@proost proost commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Fixes

Changes

Complete exemplar filter.

This changes accompany breaking change. When I read versioning policy, flagged feature is unstable, so API breaking change is accepted, right?

I tried to abstract filte logic into FilteredExemplarReservoir to avoid filtering logic scattered to metric storage.

Please provide a brief description of the changes here.

For significant contributions please make sure you have completed the following items:

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

@proost proost requested a review from a team as a code owner June 30, 2026 07:15
@proost proost changed the title feat: exemplar filters [API/SDK] feat: exemplar filters Jun 30, 2026
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.00%. Comparing base (4e20f69) to head (65d7375).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
api/include/opentelemetry/trace/context.h 63.64% 4 Missing ⚠️
...sdk/metrics/exemplar/filtered_exemplar_reservoir.h 88.89% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4191      +/-   ##
==========================================
+ Coverage   77.92%   78.00%   +0.08%     
==========================================
  Files         439      440       +1     
  Lines       18612    18628      +16     
==========================================
+ Hits        14502    14529      +27     
+ Misses       4110     4099      -11     
Files with missing lines Coverage Δ
...ude/opentelemetry/sdk/metrics/exemplar/reservoir.h 100.00% <ø> (ø)
...entelemetry/sdk/metrics/exemplar/reservoir_utils.h 100.00% <100.00%> (ø)
...telemetry/sdk/metrics/state/async_metric_storage.h 95.24% <100.00%> (+4.33%) ⬆️
...ntelemetry/sdk/metrics/state/sync_metric_storage.h 90.00% <100.00%> (+7.50%) ⬆️
sdk/src/logs/logger.cc 94.12% <100.00%> (+3.61%) ⬆️
sdk/src/metrics/exemplar/reservoir.cc 75.00% <100.00%> (+8.34%) ⬆️
sdk/src/metrics/meter.cc 81.36% <ø> (ø)
...sdk/metrics/exemplar/filtered_exemplar_reservoir.h 88.89% <88.89%> (ø)
api/include/opentelemetry/trace/context.h 82.61% <63.64%> (-17.39%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes exemplar filtering by moving filter decisions into a FilteredExemplarReservoir wrapper (instead of scattering filter checks across metric storage code), and updates the logs SDK to reuse a new trace::GetSpanContext() helper for extracting the active span context from a Context.

Changes:

  • Add FilteredExemplarReservoir and wire exemplar filter behavior through ExemplarReservoir factories (GetSimpleFilteredExemplarReservoir, GetExemplarReservoir).
  • Remove exemplar-filter branching from sync/async metric storages and update meter/storage call sites accordingly.
  • Add trace::GetSpanContext() API helper, use it in logs SDK, and update/remove related tests/build targets.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h Removes in-storage exemplar filtering; always forwards to reservoir (filter now handled by reservoir wrapper).
sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h Removes in-storage exemplar filtering; always forwards to reservoir (filter now handled by reservoir wrapper).
sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h Adds the filtering reservoir wrapper that enforces ExemplarFilterType.
sdk/src/metrics/exemplar/reservoir.cc Implements GetSimpleFilteredExemplarReservoir() factory creating the wrapper reservoir.
sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h Updates filtered-reservoir factory signature to use nostd::shared_ptr.
sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_utils.h Wraps concrete reservoirs (fixed-size / aligned histogram) with filtered reservoir based on configured filter type.
sdk/src/metrics/meter.cc Passes exemplar filter type into GetExemplarReservoir(...) so storages receive already-filtering reservoirs.
api/include/opentelemetry/trace/context.h Adds trace::GetSpanContext(context) helper for extracting active SpanContext from a Context.
sdk/src/logs/logger.cc Replaces duplicated span-context extraction helper with trace::GetSpanContext().
sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc Adds unit coverage for AlwaysOn/AlwaysOff/TraceBased behavior in the filtered reservoir wrapper.
sdk/test/metrics/exemplar/CMakeLists.txt Registers the new filtered-reservoir test target.
sdk/test/metrics/exemplar/BUILD Adds Bazel cc_test for filtered_exemplar_reservoir_test.
sdk/test/metrics/exemplar/always_sample_filter_test.cc Removes old ExemplarFilter interface-based test (API removed/changed).
sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc Removes old ExemplarFilter interface-based test (API removed/changed).
sdk/test/metrics/sync_metric_storage_counter_test.cc Updates SyncMetricStorage construction for changed exemplar args (filter removed from ctor).
sdk/test/metrics/sync_metric_storage_up_down_counter_test.cc Updates SyncMetricStorage construction for changed exemplar args (filter removed from ctor).
sdk/test/metrics/sync_metric_storage_histogram_test.cc Updates SyncMetricStorage construction for changed exemplar args (filter removed from ctor).
sdk/test/metrics/sync_metric_storage_gauge_test.cc Updates SyncMetricStorage construction for changed exemplar args (filter removed from ctor).
sdk/test/metrics/async_metric_storage_test.cc Updates AsyncMetricStorage construction for changed exemplar args (filter removed from ctor).
sdk/test/metrics/cardinality_limit_test.cc Updates SyncMetricStorage construction for changed exemplar args (filter removed from ctor).
sdk/test/metrics/bound_sync_instruments_test.cc Updates SyncMetricStorage construction for changed exemplar args (filter removed from ctor).
CHANGELOG.md Documents new trace::GetSpanContext() and exemplar filter application via filtered reservoirs.

Comment on lines +72 to +90
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
{

@dbarker dbarker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I haven't reviewed fully but wanted to share this initial feedback on breaking out the GetSpanContext method to a separate PR.


// 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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice addition for performance and reducing dynamic memory allocation. The GetSpan method above is commonly used just to access the SpanContext from the span inside a full Context with this pattern:

auto span_context = opentelemetry:::trace::GetSpan(context)->GetContext(); 

When the context does not have a span then one is dynamically allocated just to create an invalid span context.

Consider breaking this change out to a separate PR and replacing that pattern where appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants