From 1cea021f3354dba8167e2f45d59b6802d673bcf5 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Tue, 30 Jun 2026 15:59:29 +0900 Subject: [PATCH 01/49] feat: exemplar filters --- api/include/opentelemetry/trace/context.h | 27 +++++ .../exemplar/filtered_exemplar_reservoir.h | 111 ++++++++++++++++++ .../sdk/metrics/exemplar/reservoir.h | 2 +- .../sdk/metrics/exemplar/reservoir_utils.h | 26 ++-- .../sdk/metrics/state/async_metric_storage.h | 11 +- .../sdk/metrics/state/sync_metric_storage.h | 41 +------ sdk/src/logs/logger.cc | 34 +----- sdk/src/metrics/exemplar/reservoir.cc | 11 ++ sdk/src/metrics/meter.cc | 6 +- sdk/test/metrics/async_metric_storage_test.cc | 7 +- .../metrics/bound_sync_instruments_test.cc | 53 ++++----- sdk/test/metrics/cardinality_limit_test.cc | 2 - sdk/test/metrics/exemplar/BUILD | 17 +++ sdk/test/metrics/exemplar/CMakeLists.txt | 7 +- .../exemplar/always_sample_filter_test.cc | 18 --- .../filtered_exemplar_reservoir_test.cc | 108 +++++++++++++++++ .../exemplar/with_trace_sample_filter_test.cc | 17 --- .../sync_metric_storage_counter_test.cc | 11 +- .../metrics/sync_metric_storage_gauge_test.cc | 9 +- .../sync_metric_storage_histogram_test.cc | 7 +- ...ync_metric_storage_up_down_counter_test.cc | 5 +- 21 files changed, 348 insertions(+), 182 deletions(-) create mode 100644 sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h delete mode 100644 sdk/test/metrics/exemplar/always_sample_filter_test.cc create mode 100644 sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc delete mode 100644 sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc diff --git a/api/include/opentelemetry/trace/context.h b/api/include/opentelemetry/trace/context.h index 146f0d65f9..d68f19a7db 100644 --- a/api/include/opentelemetry/trace/context.h +++ b/api/include/opentelemetry/trace/context.h @@ -23,6 +23,33 @@ inline nostd::shared_ptr GetSpan(const context::Context &context) noexcept return nostd::shared_ptr(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 *maybe_span = + nostd::get_if>(&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 *maybe_span_context = + nostd::get_if>(&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 { diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h new file mode 100644 index 0000000000..179cf7d533 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h @@ -0,0 +1,111 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW + +# include +# include +# include +# include + +# 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 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 ×tamp) 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 ×tamp) noexcept override + { + if (should_sample_(context)) + { + reservoir_->OfferMeasurement(value, attributes, context, timestamp); + } + } + + std::vector> 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 + { + 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 reservoir_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE + +#endif // ENABLE_METRICS_EXEMPLAR_PREVIEW diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h index efbd20abc4..19ad5551e6 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h @@ -75,7 +75,7 @@ class ExemplarReservoir static nostd::shared_ptr GetSimpleFilteredExemplarReservoir( ExemplarFilterType filter_type, - std::shared_ptr reservoir); + nostd::shared_ptr reservoir); static nostd::shared_ptr GetSimpleFixedSizeExemplarReservoir( size_t size, diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_utils.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_utils.h index 6ca88411af..e8e2970e89 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_utils.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_utils.h @@ -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" @@ -49,7 +51,8 @@ static inline size_t GetSimpleFixedReservoirDefaultSize(const AggregationType ag static inline nostd::shared_ptr 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) { @@ -62,18 +65,21 @@ static inline nostd::shared_ptr GetExemplarReservoir( // if (histogram_agg_config != nullptr && histogram_agg_config->boundaries_.size() > 1) { - return nostd::shared_ptr(new AlignedHistogramBucketExemplarReservoir( - histogram_agg_config->boundaries_.size(), - AlignedHistogramBucketExemplarReservoir::GetHistogramCellSelector( - histogram_agg_config->boundaries_), - GetMapAndResetCellMethod(instrument_descriptor))); + return ExemplarReservoir::GetSimpleFilteredExemplarReservoir( + filter_type, + nostd::shared_ptr(new AlignedHistogramBucketExemplarReservoir( + histogram_agg_config->boundaries_.size(), + AlignedHistogramBucketExemplarReservoir::GetHistogramCellSelector( + histogram_agg_config->boundaries_), + GetMapAndResetCellMethod(instrument_descriptor)))); } } - return nostd::shared_ptr(new SimpleFixedSizeExemplarReservoir( - GetSimpleFixedReservoirDefaultSize(agg_type, agg_config), - SimpleFixedSizeExemplarReservoir::GetSimpleFixedSizeCellSelector(), - GetMapAndResetCellMethod(instrument_descriptor))); + return ExemplarReservoir::GetSimpleFilteredExemplarReservoir( + filter_type, nostd::shared_ptr(new SimpleFixedSizeExemplarReservoir( + GetSimpleFixedReservoirDefaultSize(agg_type, agg_config), + SimpleFixedSizeExemplarReservoir::GetSimpleFixedSizeCellSelector(), + GetMapAndResetCellMethod(instrument_descriptor)))); } } // namespace metrics } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h index a3f56ef64d..017d802578 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h @@ -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 @@ -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 &&exemplar_reservoir, #endif const AggregationConfig *aggregation_config) @@ -49,7 +47,6 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora delta_hash_map_( std::make_unique(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) @@ -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_); @@ -146,7 +140,6 @@ class AsyncMetricStorage : public MetricStorage, public AsyncWritableMetricStora std::unique_ptr delta_hash_map_; opentelemetry::common::SpinLockMutex hashmap_lock_; #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType exemplar_filter_type_; nostd::shared_ptr exemplar_reservoir_; #endif TemporalMetricStorage temporal_metric_storage_; diff --git a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h index 0c77f74c63..b3112a47f6 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h @@ -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 @@ -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 attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType exempler_filter_type, nostd::shared_ptr &&exemplar_reservoir, #endif const AggregationConfig *aggregation_config) @@ -76,7 +61,6 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage std::make_unique(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) @@ -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 guard(attribute_hashmap_lock_); @@ -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()}; @@ -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 guard(attribute_hashmap_lock_); @@ -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 guard(attribute_hashmap_lock_); @@ -298,7 +270,6 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage std::function()> create_default_aggregation_; std::shared_ptr attributes_processor_; #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType exemplar_filter_type_; nostd::shared_ptr exemplar_reservoir_; #endif TemporalMetricStorage temporal_metric_storage_; diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 5c643da7b6..bdef3d445d 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -8,13 +8,11 @@ #include "opentelemetry/common/timestamp.h" #include "opentelemetry/context/context.h" -#include "opentelemetry/context/context_value.h" #include "opentelemetry/context/runtime_context.h" #include "opentelemetry/logs/event_id.h" #include "opentelemetry/logs/log_record.h" #include "opentelemetry/logs/noop.h" #include "opentelemetry/logs/severity.h" -#include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/nostd/variant.h" @@ -25,10 +23,9 @@ #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/recordable.h" -#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/context.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/span_metadata.h" #include "opentelemetry/trace/trace_flags.h" #include "opentelemetry/version.h" @@ -49,33 +46,6 @@ nostd::string_view GetEventName(const opentelemetry::logs::EventId &event_id) no : nostd::string_view{}; } -trace_api::SpanContext ExtractSpanContextFromContext(const context::Context &context) noexcept -{ - if (!context.HasKey(trace_api::kSpanKey)) - { - return trace_api::SpanContext::GetInvalid(); - } - - const context::ContextValue context_value = context.GetValue(trace_api::kSpanKey); - - // Get the span metadata from the active span in the context - if (const nostd::shared_ptr *maybe_span = - nostd::get_if>(&context_value)) - { - const nostd::shared_ptr &span = *maybe_span; - return 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 *maybe_span_context = - nostd::get_if>(&context_value)) - { - const nostd::shared_ptr &span_context = *maybe_span_context; - return *span_context; - } - return trace_api::SpanContext::GetInvalid(); -} - trace_api::SpanContext ExtractSpanContext( const nostd::variant &context_or_span) noexcept { @@ -85,7 +55,7 @@ trace_api::SpanContext ExtractSpanContext( } if (const context::Context *ctx = nostd::get_if(&context_or_span)) { - return ExtractSpanContextFromContext(*ctx); + return trace_api::GetSpanContext(*ctx); } return trace_api::SpanContext::GetInvalid(); } diff --git a/sdk/src/metrics/exemplar/reservoir.cc b/sdk/src/metrics/exemplar/reservoir.cc index f5fe4064b6..71e46ba298 100644 --- a/sdk/src/metrics/exemplar/reservoir.cc +++ b/sdk/src/metrics/exemplar/reservoir.cc @@ -4,9 +4,12 @@ #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW # include +# include # include "opentelemetry/nostd/shared_ptr.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/filtered_exemplar_reservoir.h" # include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir_cell.h" @@ -20,6 +23,14 @@ namespace sdk namespace metrics { +nostd::shared_ptr ExemplarReservoir::GetSimpleFilteredExemplarReservoir( + ExemplarFilterType filter_type, + nostd::shared_ptr reservoir) +{ + return nostd::shared_ptr{ + new FilteredExemplarReservoir{filter_type, std::move(reservoir)}}; +} + nostd::shared_ptr ExemplarReservoir::GetSimpleFixedSizeExemplarReservoir( size_t size, const std::shared_ptr &reservoir_cell_selector, diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index 4a4c2f6b68..8acab9f3dd 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -545,9 +545,8 @@ std::unique_ptr Meter::RegisterSyncMetricStorage( sync_storage = std::shared_ptr(new SyncMetricStorage( view_instr_desc, view.GetAggregationType(), view.GetAttributesProcessor(), #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - exemplar_filter_type, GetExemplarReservoir(view.GetAggregationType(), view.GetAggregationConfig(), - view_instr_desc), + view_instr_desc, exemplar_filter_type), #endif view.GetAggregationConfig())); storage_registry_.insert({view_instr_desc, sync_storage}); @@ -618,9 +617,8 @@ std::unique_ptr Meter::RegisterAsyncMetricStorage( async_storage = std::shared_ptr(new AsyncMetricStorage( view_instr_desc, view.GetAggregationType(), #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - exemplar_filter_type, GetExemplarReservoir(view.GetAggregationType(), view.GetAggregationConfig(), - view_instr_desc), + view_instr_desc, exemplar_filter_type), #endif view.GetAggregationConfig())); storage_registry_.insert({view_instr_desc, async_storage}); diff --git a/sdk/test/metrics/async_metric_storage_test.cc b/sdk/test/metrics/async_metric_storage_test.cc index 4de1a95c4f..3987770420 100644 --- a/sdk/test/metrics/async_metric_storage_test.cc +++ b/sdk/test/metrics/async_metric_storage_test.cc @@ -28,7 +28,6 @@ #include "opentelemetry/sdk/metrics/view/attributes_processor.h" #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" #endif @@ -66,7 +65,7 @@ TEST_P(WritableMetricStorageTestFixture, TestAggregation) opentelemetry::sdk::metrics::AsyncMetricStorage storage( instr_desc, AggregationType::kSum, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); int64_t get_count1 = 20; @@ -161,7 +160,7 @@ TEST_P(WritableMetricStorageTestUpDownFixture, TestAggregation) opentelemetry::sdk::metrics::AsyncMetricStorage storage( instr_desc, AggregationType::kDefault, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); int64_t get_count1 = 20; @@ -257,7 +256,7 @@ TEST_P(WritableMetricStorageTestObservableGaugeFixture, TestAggregation) opentelemetry::sdk::metrics::AsyncMetricStorage storage( instr_desc, AggregationType::kLastValue, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); int64_t freq_cpu0 = 3; diff --git a/sdk/test/metrics/bound_sync_instruments_test.cc b/sdk/test/metrics/bound_sync_instruments_test.cc index 9c2f233691..a3194e54a1 100644 --- a/sdk/test/metrics/bound_sync_instruments_test.cc +++ b/sdk/test/metrics/bound_sync_instruments_test.cc @@ -40,7 +40,6 @@ # include "opentelemetry/sdk/metrics/view/attributes_processor.h" # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" # endif @@ -75,7 +74,6 @@ class StorageHolder } storage_ = std::make_shared(desc, agg_type, proc_, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), # endif cfg_); @@ -186,12 +184,12 @@ TEST(BoundSyncInstruments, BoundCounterBindInitializerList) InstrumentValueType::kLong}; std::shared_ptr proc(new DefaultAttributesProcessor{}); AggregationConfig cfg; - std::unique_ptr storage(new SyncMetricStorage( - desc, AggregationType::kSum, proc, + std::unique_ptr storage( + new SyncMetricStorage(desc, AggregationType::kSum, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif - &cfg)); + &cfg)); SyncMetricStorage *storage_ptr = storage.get(); LongCounter counter(desc, std::move(storage)); opentelemetry::metrics::Counter &api_counter = counter; @@ -238,12 +236,12 @@ TEST(BoundSyncInstruments, UnboundCounterDropsValueAboveInt64Max) InstrumentValueType::kLong}; std::shared_ptr proc(new DefaultAttributesProcessor{}); AggregationConfig cfg; - std::unique_ptr storage(new SyncMetricStorage( - desc, AggregationType::kSum, proc, + std::unique_ptr storage( + new SyncMetricStorage(desc, AggregationType::kSum, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif - &cfg)); + &cfg)); SyncMetricStorage *storage_ptr = storage.get(); LongCounter counter(desc, std::move(storage)); M attrs = {{"key", "v"}}; @@ -261,12 +259,12 @@ TEST(BoundSyncInstruments, BoundCounterDropsValueAboveInt64Max) InstrumentValueType::kLong}; std::shared_ptr proc(new DefaultAttributesProcessor{}); AggregationConfig cfg; - std::unique_ptr storage(new SyncMetricStorage( - desc, AggregationType::kSum, proc, + std::unique_ptr storage( + new SyncMetricStorage(desc, AggregationType::kSum, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif - &cfg)); + &cfg)); SyncMetricStorage *storage_ptr = storage.get(); LongCounter counter(desc, std::move(storage)); M attrs = {{"key", "v"}}; @@ -315,12 +313,12 @@ TEST(BoundSyncInstruments, UnboundHistogramDropsValueAboveInt64Max) InstrumentValueType::kLong}; std::shared_ptr proc(new DefaultAttributesProcessor{}); HistogramAggregationConfig cfg; - std::unique_ptr storage(new SyncMetricStorage( - desc, AggregationType::kHistogram, proc, + std::unique_ptr storage( + new SyncMetricStorage(desc, AggregationType::kHistogram, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif - &cfg)); + &cfg)); SyncMetricStorage *storage_ptr = storage.get(); LongHistogram histogram(desc, std::move(storage)); M attrs = {{"key", "v"}}; @@ -354,12 +352,12 @@ TEST(BoundSyncInstruments, BoundHistogramDropsValueAboveInt64Max) InstrumentValueType::kLong}; std::shared_ptr proc(new DefaultAttributesProcessor{}); HistogramAggregationConfig cfg; - std::unique_ptr storage(new SyncMetricStorage( - desc, AggregationType::kHistogram, proc, + std::unique_ptr storage( + new SyncMetricStorage(desc, AggregationType::kHistogram, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif - &cfg)); + &cfg)); SyncMetricStorage *storage_ptr = storage.get(); LongHistogram histogram(desc, std::move(storage)); M attrs = {{"key", "v"}}; @@ -394,12 +392,12 @@ TEST(BoundSyncInstruments, BoundHistogramBindInitializerList) InstrumentValueType::kLong}; std::shared_ptr proc(new DefaultAttributesProcessor{}); HistogramAggregationConfig cfg; - std::unique_ptr storage(new SyncMetricStorage( - desc, AggregationType::kHistogram, proc, + std::unique_ptr storage( + new SyncMetricStorage(desc, AggregationType::kHistogram, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif - &cfg)); + &cfg)); SyncMetricStorage *storage_ptr = storage.get(); LongHistogram histogram(desc, std::move(storage)); opentelemetry::metrics::Histogram &api_histogram = histogram; @@ -436,7 +434,6 @@ TEST(BoundSyncInstruments, BoundCounterRespectsDropAggregation) AggregationConfig cfg; SyncMetricStorage storage(desc, AggregationType::kDrop, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), # endif &cfg); @@ -472,7 +469,6 @@ TEST(BoundSyncInstruments, BoundCounterRespectsLastValueAggregation) AggregationConfig cfg; SyncMetricStorage storage(desc, AggregationType::kLastValue, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), # endif &cfg); @@ -511,7 +507,6 @@ TEST(BoundSyncInstruments, BoundHistogramRespectsCustomBuckets) cfg.boundaries_ = {10.0, 20.0}; SyncMetricStorage storage(desc, AggregationType::kHistogram, proc, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), # endif &cfg); diff --git a/sdk/test/metrics/cardinality_limit_test.cc b/sdk/test/metrics/cardinality_limit_test.cc index 01aaf79b0a..4a5e47139b 100644 --- a/sdk/test/metrics/cardinality_limit_test.cc +++ b/sdk/test/metrics/cardinality_limit_test.cc @@ -32,7 +32,6 @@ #include "opentelemetry/sdk/metrics/view/attributes_processor.h" #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" #endif @@ -116,7 +115,6 @@ TEST_P(WritableMetricStorageCardinalityLimitTestFixture, LongCounterSumAggregati new DefaultAttributesProcessor{}}; SyncMetricStorage storage(instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), #endif &aggConfig); diff --git a/sdk/test/metrics/exemplar/BUILD b/sdk/test/metrics/exemplar/BUILD index 9b11a64b9c..10973fd000 100644 --- a/sdk/test/metrics/exemplar/BUILD +++ b/sdk/test/metrics/exemplar/BUILD @@ -53,3 +53,20 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "filtered_exemplar_reservoir_test", + srcs = [ + "filtered_exemplar_reservoir_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/metrics/exemplar/CMakeLists.txt b/sdk/test/metrics/exemplar/CMakeLists.txt index 04a715b2f8..32f755e747 100644 --- a/sdk/test/metrics/exemplar/CMakeLists.txt +++ b/sdk/test/metrics/exemplar/CMakeLists.txt @@ -1,9 +1,10 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 -foreach(testname - no_exemplar_reservoir_test - aligned_histogram_bucket_exemplar_reservoir_test reservoir_cell_test) +foreach( + testname + no_exemplar_reservoir_test aligned_histogram_bucket_exemplar_reservoir_test + reservoir_cell_test filtered_exemplar_reservoir_test) add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/sdk/test/metrics/exemplar/always_sample_filter_test.cc b/sdk/test/metrics/exemplar/always_sample_filter_test.cc deleted file mode 100644 index f78cd412bd..0000000000 --- a/sdk/test/metrics/exemplar/always_sample_filter_test.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include - -#include "opentelemetry/context/context.h" -#include "opentelemetry/sdk/metrics/exemplar/filter.h" - -using namespace opentelemetry::sdk::metrics; - -TEST(AlwaysSampleFilter, SampleMeasurement) -{ - auto filter = opentelemetry::sdk::metrics::ExemplarFilter::GetAlwaysSampleFilter(); - ASSERT_TRUE( - filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); - ASSERT_TRUE(filter->ShouldSampleMeasurement(static_cast(1), MetricAttributes{}, - opentelemetry::context::Context{})); -} diff --git a/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc b/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc new file mode 100644 index 0000000000..b839121539 --- /dev/null +++ b/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc @@ -0,0 +1,108 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW + +# include +# include +# include +# include +# include + +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/nostd/span.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/sdk/metrics/exemplar/reservoir_cell.h" +# include "opentelemetry/sdk/metrics/exemplar/simple_fixed_size_exemplar_reservoir.h" +# include "opentelemetry/trace/context.h" +# include "opentelemetry/trace/default_span.h" +# include "opentelemetry/trace/span.h" +# include "opentelemetry/trace/span_context.h" +# include "opentelemetry/trace/span_id.h" +# include "opentelemetry/trace/trace_flags.h" +# include "opentelemetry/trace/trace_id.h" + +namespace +{ +namespace metrics_sdk = opentelemetry::sdk::metrics; +namespace trace_api = opentelemetry::trace; +namespace context_api = opentelemetry::context; +namespace nostd = opentelemetry::nostd; + +nostd::shared_ptr MakeFiltered( + metrics_sdk::ExemplarFilterType filter_type, + metrics_sdk::MapAndResetCellType map_and_reset = &metrics_sdk::ReservoirCell::GetAndResetDouble) +{ + auto inner = metrics_sdk::ExemplarReservoir::GetSimpleFixedSizeExemplarReservoir( + 1, metrics_sdk::SimpleFixedSizeExemplarReservoir::GetSimpleFixedSizeCellSelector(), + map_and_reset); + return metrics_sdk::ExemplarReservoir::GetSimpleFilteredExemplarReservoir(filter_type, inner); +} + +context_api::Context ContextWithSpan(bool sampled) +{ + const uint8_t trace_id_bytes[trace_api::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + const uint8_t span_id_bytes[trace_api::SpanId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8}; + trace_api::SpanContext span_context( + trace_api::TraceId(trace_id_bytes), trace_api::SpanId(span_id_bytes), + sampled ? trace_api::TraceFlags(trace_api::TraceFlags::kIsSampled) : trace_api::TraceFlags(), + false); + nostd::shared_ptr span(new trace_api::DefaultSpan(span_context)); + context_api::Context context; + return trace_api::SetSpan(context, span); +} + +bool OffersDouble(const nostd::shared_ptr &reservoir, + const context_api::Context &context) +{ + reservoir->OfferMeasurement(1.0, metrics_sdk::MetricAttributes{}, context, + std::chrono::system_clock::now()); + return !reservoir->CollectAndReset(metrics_sdk::MetricAttributes{}).empty(); +} +} // namespace + +TEST(FilteredExemplarReservoir, AlwaysOnOffersMeasurement) +{ + EXPECT_TRUE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kAlwaysOn), + context_api::Context{})); +} + +TEST(FilteredExemplarReservoir, AlwaysOnOffersLongMeasurement) +{ + auto reservoir = MakeFiltered(metrics_sdk::ExemplarFilterType::kAlwaysOn, + &metrics_sdk::ReservoirCell::GetAndResetLong); + reservoir->OfferMeasurement(static_cast(1), metrics_sdk::MetricAttributes{}, + context_api::Context{}, std::chrono::system_clock::now()); + EXPECT_FALSE(reservoir->CollectAndReset(metrics_sdk::MetricAttributes{}).empty()); +} + +TEST(FilteredExemplarReservoir, AlwaysOffDropsMeasurement) +{ + EXPECT_FALSE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kAlwaysOff), + context_api::Context{})); +} + +TEST(FilteredExemplarReservoir, TraceBasedDropsWithoutSpan) +{ + EXPECT_FALSE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kTraceBased), + context_api::Context{})); +} + +TEST(FilteredExemplarReservoir, TraceBasedOffersWithSampledSpan) +{ + EXPECT_TRUE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kTraceBased), + ContextWithSpan(true))); +} + +TEST(FilteredExemplarReservoir, TraceBasedDropsUnsampledSpan) +{ + EXPECT_FALSE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kTraceBased), + ContextWithSpan(false))); +} + +#endif // ENABLE_METRICS_EXEMPLAR_PREVIEW diff --git a/sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc b/sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc deleted file mode 100644 index fcf9d6344e..0000000000 --- a/sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include -#include "opentelemetry/context/context.h" -#include "opentelemetry/sdk/metrics/exemplar/filter.h" - -using namespace opentelemetry::sdk::metrics; - -TEST(WithTraceSampleFilter, SampleMeasurement) -{ - auto filter = opentelemetry::sdk::metrics::ExemplarFilter::GetWithTraceSampleFilter(); - ASSERT_FALSE( - filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); - ASSERT_FALSE(filter->ShouldSampleMeasurement(static_cast(1), MetricAttributes{}, - opentelemetry::context::Context{})); -} diff --git a/sdk/test/metrics/sync_metric_storage_counter_test.cc b/sdk/test/metrics/sync_metric_storage_counter_test.cc index 89e8df46ff..f31599a8af 100644 --- a/sdk/test/metrics/sync_metric_storage_counter_test.cc +++ b/sdk/test/metrics/sync_metric_storage_counter_test.cc @@ -27,7 +27,6 @@ #include "opentelemetry/sdk/metrics/view/attributes_processor.h" #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" #endif @@ -53,7 +52,7 @@ TEST_P(WritableMetricStorageTestFixture, LongCounterSumAggregation) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); @@ -193,7 +192,7 @@ TEST_P(WritableMetricStorageTestFixture, DoubleCounterSumAggregation) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); @@ -329,7 +328,7 @@ TEST(SyncMetricStorageTest, DeltaCounterStartTimestampTracksEmptyCycles) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); @@ -407,7 +406,7 @@ TEST(SyncMetricStorageTest, DeltaCounterFirstIntervalUsesInstrumentCreationTime) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); auto after_creation = std::chrono::system_clock::now(); @@ -460,7 +459,7 @@ TEST(SyncMetricStorageTest, DeltaCounterMultiCollectorFirstIntervalUsesInstrumen opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); auto after_creation = std::chrono::system_clock::now(); diff --git a/sdk/test/metrics/sync_metric_storage_gauge_test.cc b/sdk/test/metrics/sync_metric_storage_gauge_test.cc index 1dc0a4d2cd..2675a9aaf8 100644 --- a/sdk/test/metrics/sync_metric_storage_gauge_test.cc +++ b/sdk/test/metrics/sync_metric_storage_gauge_test.cc @@ -26,7 +26,6 @@ # include "opentelemetry/nostd/variant.h" # include "opentelemetry/sdk/metrics/data/metric_data.h" # include "opentelemetry/sdk/metrics/data/point_data.h" -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" # include "opentelemetry/sdk/metrics/state/metric_collector.h" # include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" @@ -58,7 +57,7 @@ TEST_P(WritableMetricStorageTestFixture, LongGaugeLastValueAggregation) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kLastValue, default_attributes_processor, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif nullptr); @@ -142,7 +141,7 @@ TEST_P(WritableMetricStorageTestFixture, DoubleGaugeLastValueAggregation) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kLastValue, default_attributes_processor, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif nullptr); @@ -236,7 +235,7 @@ TEST_P(WritableMetricStorageDeltaMultiReaderTestFixture, opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kLastValue, default_attributes_processor, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif nullptr); auto after_creation = std::chrono::system_clock::now(); @@ -417,7 +416,7 @@ TEST_P(WritableMetricStorageDeltaMultiReaderTestFixture, opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kLastValue, default_attributes_processor, # ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), # endif nullptr); auto after_creation = std::chrono::system_clock::now(); diff --git a/sdk/test/metrics/sync_metric_storage_histogram_test.cc b/sdk/test/metrics/sync_metric_storage_histogram_test.cc index 9f608bcd16..aabef07f14 100644 --- a/sdk/test/metrics/sync_metric_storage_histogram_test.cc +++ b/sdk/test/metrics/sync_metric_storage_histogram_test.cc @@ -27,7 +27,6 @@ #include "opentelemetry/sdk/metrics/view/attributes_processor.h" #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" #endif @@ -54,7 +53,7 @@ TEST_P(WritableMetricStorageHistogramTestFixture, LongHistogram) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kHistogram, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); @@ -195,7 +194,7 @@ TEST_P(WritableMetricStorageHistogramTestFixture, DoubleHistogram) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kHistogram, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); @@ -343,7 +342,7 @@ TEST_P(WritableMetricStorageHistogramTestFixture, Base2ExponentialDoubleHistogra opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kBase2ExponentialHistogram, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); diff --git a/sdk/test/metrics/sync_metric_storage_up_down_counter_test.cc b/sdk/test/metrics/sync_metric_storage_up_down_counter_test.cc index cf54165341..3b36aaa940 100644 --- a/sdk/test/metrics/sync_metric_storage_up_down_counter_test.cc +++ b/sdk/test/metrics/sync_metric_storage_up_down_counter_test.cc @@ -26,7 +26,6 @@ #include "opentelemetry/sdk/metrics/view/attributes_processor.h" #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" #endif @@ -52,7 +51,7 @@ TEST_P(WritableMetricStorageTestFixture, LongUpDownCounterSumAggregation) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); @@ -202,7 +201,7 @@ TEST_P(WritableMetricStorageTestFixture, DoubleUpDownCounterSumAggregation) opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor, #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW - ExemplarFilterType::kAlwaysOff, ExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), #endif nullptr); From ce49c8034d5faf16ada22580caeb9242847b787e Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Tue, 30 Jun 2026 16:16:12 +0900 Subject: [PATCH 02/49] doc: update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c462112a7..3de3fe70dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 From 34cc7e68067f52f09fac352b0bbb72e29eee56db Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Tue, 30 Jun 2026 17:51:19 +0900 Subject: [PATCH 03/49] test: change to mock object --- .../filtered_exemplar_reservoir_test.cc | 77 +++++++++++-------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc b/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc index b839121539..22d4616370 100644 --- a/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc +++ b/sdk/test/metrics/exemplar/filtered_exemplar_reservoir_test.cc @@ -16,8 +16,6 @@ # 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/sdk/metrics/exemplar/reservoir_cell.h" -# include "opentelemetry/sdk/metrics/exemplar/simple_fixed_size_exemplar_reservoir.h" # include "opentelemetry/trace/context.h" # include "opentelemetry/trace/default_span.h" # include "opentelemetry/trace/span.h" @@ -33,15 +31,33 @@ namespace trace_api = opentelemetry::trace; namespace context_api = opentelemetry::context; namespace nostd = opentelemetry::nostd; -nostd::shared_ptr MakeFiltered( - metrics_sdk::ExemplarFilterType filter_type, - metrics_sdk::MapAndResetCellType map_and_reset = &metrics_sdk::ReservoirCell::GetAndResetDouble) +class CountingReservoir : public metrics_sdk::ExemplarReservoir { - auto inner = metrics_sdk::ExemplarReservoir::GetSimpleFixedSizeExemplarReservoir( - 1, metrics_sdk::SimpleFixedSizeExemplarReservoir::GetSimpleFixedSizeCellSelector(), - map_and_reset); - return metrics_sdk::ExemplarReservoir::GetSimpleFilteredExemplarReservoir(filter_type, inner); -} +public: + void OfferMeasurement(int64_t, + const metrics_sdk::MetricAttributes &, + const context_api::Context &, + const opentelemetry::common::SystemTimestamp &) noexcept override + { + ++offered; + } + + void OfferMeasurement(double, + const metrics_sdk::MetricAttributes &, + const context_api::Context &, + const opentelemetry::common::SystemTimestamp &) noexcept override + { + ++offered; + } + + std::vector> CollectAndReset( + const metrics_sdk::MetricAttributes &) noexcept override + { + return {}; + } + + int offered = 0; +}; context_api::Context ContextWithSpan(bool sampled) { @@ -57,52 +73,53 @@ context_api::Context ContextWithSpan(bool sampled) return trace_api::SetSpan(context, span); } -bool OffersDouble(const nostd::shared_ptr &reservoir, - const context_api::Context &context) +int ForwardedCount(metrics_sdk::ExemplarFilterType filter_type, const context_api::Context &context) { - reservoir->OfferMeasurement(1.0, metrics_sdk::MetricAttributes{}, context, - std::chrono::system_clock::now()); - return !reservoir->CollectAndReset(metrics_sdk::MetricAttributes{}).empty(); + auto *spy = new CountingReservoir(); + auto filtered = metrics_sdk::ExemplarReservoir::GetSimpleFilteredExemplarReservoir( + filter_type, nostd::shared_ptr(spy)); + filtered->OfferMeasurement(1.0, metrics_sdk::MetricAttributes{}, context, + std::chrono::system_clock::now()); + return spy->offered; } } // namespace TEST(FilteredExemplarReservoir, AlwaysOnOffersMeasurement) { - EXPECT_TRUE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kAlwaysOn), - context_api::Context{})); + EXPECT_EQ(ForwardedCount(metrics_sdk::ExemplarFilterType::kAlwaysOn, context_api::Context{}), 1); } TEST(FilteredExemplarReservoir, AlwaysOnOffersLongMeasurement) { - auto reservoir = MakeFiltered(metrics_sdk::ExemplarFilterType::kAlwaysOn, - &metrics_sdk::ReservoirCell::GetAndResetLong); - reservoir->OfferMeasurement(static_cast(1), metrics_sdk::MetricAttributes{}, - context_api::Context{}, std::chrono::system_clock::now()); - EXPECT_FALSE(reservoir->CollectAndReset(metrics_sdk::MetricAttributes{}).empty()); + auto *spy = new CountingReservoir(); + auto filtered = metrics_sdk::ExemplarReservoir::GetSimpleFilteredExemplarReservoir( + metrics_sdk::ExemplarFilterType::kAlwaysOn, + nostd::shared_ptr(spy)); + filtered->OfferMeasurement(static_cast(1), metrics_sdk::MetricAttributes{}, + context_api::Context{}, std::chrono::system_clock::now()); + EXPECT_EQ(spy->offered, 1); } TEST(FilteredExemplarReservoir, AlwaysOffDropsMeasurement) { - EXPECT_FALSE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kAlwaysOff), - context_api::Context{})); + EXPECT_EQ(ForwardedCount(metrics_sdk::ExemplarFilterType::kAlwaysOff, context_api::Context{}), 0); } TEST(FilteredExemplarReservoir, TraceBasedDropsWithoutSpan) { - EXPECT_FALSE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kTraceBased), - context_api::Context{})); + EXPECT_EQ(ForwardedCount(metrics_sdk::ExemplarFilterType::kTraceBased, context_api::Context{}), + 0); } TEST(FilteredExemplarReservoir, TraceBasedOffersWithSampledSpan) { - EXPECT_TRUE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kTraceBased), - ContextWithSpan(true))); + EXPECT_EQ(ForwardedCount(metrics_sdk::ExemplarFilterType::kTraceBased, ContextWithSpan(true)), 1); } TEST(FilteredExemplarReservoir, TraceBasedDropsUnsampledSpan) { - EXPECT_FALSE(OffersDouble(MakeFiltered(metrics_sdk::ExemplarFilterType::kTraceBased), - ContextWithSpan(false))); + EXPECT_EQ(ForwardedCount(metrics_sdk::ExemplarFilterType::kTraceBased, ContextWithSpan(false)), + 0); } #endif // ENABLE_METRICS_EXEMPLAR_PREVIEW From 8ad88a37458af0c0ea6bf0b1106406e2e6c6ed3b Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:55:53 -0400 Subject: [PATCH 04/49] [API] fix the trace state regex to comply with the w3c trace context level 2 (#4194) --- CHANGELOG.md | 9 ++++++++ api/include/opentelemetry/trace/trace_state.h | 22 +++++-------------- api/test/trace/trace_state_test.cc | 22 +++++++++++++++++++ 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3de3fe70dd..584b5a32e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,15 @@ Increment the: ## [Unreleased] +* [API] Fix `TraceState::IsValidKey()` to comply with the W3C Trace Context + Level 2, where keys containing `@` and keys with more than 241 characters + before `@` or more than 14 characters after `@` are now accepted. + Only the total 256-character key length limit is enforced. + **Note**: this is a correctness fix to an inline API header; the observable + behavior of `IsValidKey`, `IsValidKeyRegEx`, and `IsValidKeyNonRegEx` + changes (see `docs/abi-policy.md`). + [#4194](https://github.com/open-telemetry/opentelemetry-cpp/pull/4194) + * [SDK] Add `TracerProvider::UpdateTracerConfigurator()` and example [#4065](https://github.com/open-telemetry/opentelemetry-cpp/issues/4065) diff --git a/api/include/opentelemetry/trace/trace_state.h b/api/include/opentelemetry/trace/trace_state.h index d50354dbc5..5cab875852 100644 --- a/api/include/opentelemetry/trace/trace_state.h +++ b/api/include/opentelemetry/trace/trace_state.h @@ -205,9 +205,9 @@ class OPENTELEMETRY_EXPORT TraceState /** Returns whether key is a valid key. See https://www.w3.org/TR/trace-context/#key * Identifiers MUST begin with a lowercase letter or a digit, and can only contain * lowercase letters (a-z), digits (0-9), underscores (_), dashes (-), asterisks (*), - * and forward slashes (/). - * For multi-tenant vendor scenarios, an at sign (@) can be used to prefix the vendor name. - * + * forward slashes (/), and at signs (@). + * An at sign (@) is treated as a regular character (keychar) with no structural meaning. + * Total key length must not exceed 256 characters. */ static bool IsValidKey(nostd::string_view key) noexcept { @@ -255,15 +255,9 @@ class OPENTELEMETRY_EXPORT TraceState try { # endif - static std::regex reg_key("^[a-z0-9][a-z0-9*_\\-/]{0,255}$"); - static std::regex reg_key_multitenant( - "^[a-z0-9][a-z0-9*_\\-/]{0,240}(@)[a-z0-9][a-z0-9*_\\-/]{0,13}$"); + static std::regex reg_key("^[a-z0-9][a-z0-9*_\\-/@]{0,255}$"); std::string key_s(key.data(), key.size()); - if (std::regex_match(key_s, reg_key) || std::regex_match(key_s, reg_key_multitenant)) - { - return true; - } - return false; + return std::regex_match(key_s, reg_key); # if OPENTELEMETRY_HAVE_EXCEPTIONS } catch (const std::regex_error &) @@ -300,18 +294,12 @@ class OPENTELEMETRY_EXPORT TraceState return false; } - int ats = 0; - for (const char c : key) { if (!IsLowerCaseAlphaOrDigit(c) && c != '_' && c != '-' && c != '@' && c != '*' && c != '/') { return false; } - if ((c == '@') && (++ats > 1)) - { - return false; - } } return true; } diff --git a/api/test/trace/trace_state_test.cc b/api/test/trace/trace_state_test.cc index 294e88905a..284006fd1a 100644 --- a/api/test/trace/trace_state_test.cc +++ b/api/test/trace/trace_state_test.cc @@ -62,6 +62,9 @@ TEST(TraceStateTest, ValidateHeaderParsing) {"k1=v1,k2=v2,invalidmember", ""}, {"1a-2f@foo=bar1,a*/foo-_/bar=bar4", "1a-2f@foo=bar1,a*/foo-_/bar=bar4"}, {"1a-2f@foo=bar1,*/foo-_/bar=bar4", ""}, + {"foo@@bar=1,baz=2", "foo@@bar=1,baz=2"}, + {"foo@bar@baz=1", "foo@bar@baz=1"}, + {"@foo=1", ""}, {",k1=v1", "k1=v1"}, {",", ""}, {",=,", ""}, @@ -71,6 +74,12 @@ TEST(TraceStateTest, ValidateHeaderParsing) { EXPECT_EQ(create_ts_return_header(testcase.input), testcase.expected); } + // Key length boundaries: 256-char key valid, 257-char invalid + EXPECT_NE(create_ts_return_header(std::string(256, 'z') + "=1"), ""); + EXPECT_EQ(create_ts_return_header(std::string(257, 'z') + "=1"), ""); + // Old vendor-section limits (241 before @, 14 after) are gone; only total length matters + EXPECT_NE(create_ts_return_header(std::string(242, 't') + "@v=1"), ""); // 244-char key with @ + EXPECT_NE(create_ts_return_header("t@" + std::string(15, 'v') + "=1"), ""); // 15 chars after @ } TEST(TraceStateTest, ExceedsMaxKeyValuePairs) @@ -165,10 +174,23 @@ TEST(TraceStateTest, GetAllEntries) TEST(TraceStateTest, IsValidKey) { EXPECT_TRUE(TraceState::IsValidKey("valid-key23/*")); + // @ is a regular keychar per W3C Trace Context Level 2 + EXPECT_TRUE(TraceState::IsValidKey("foo@")); + EXPECT_TRUE(TraceState::IsValidKey("foo@@bar")); + EXPECT_TRUE(TraceState::IsValidKey("foo@bar@baz")); + EXPECT_FALSE(TraceState::IsValidKey("@foo")); // must start with lcalpha or DIGIT EXPECT_FALSE(TraceState::IsValidKey("Invalid_key")); EXPECT_FALSE(TraceState::IsValidKey("invalid$Key&")); EXPECT_FALSE(TraceState::IsValidKey("")); EXPECT_FALSE(TraceState::IsValidKey(kLongString)); + // Key length boundary: exactly 256 chars valid, 257 invalid + EXPECT_TRUE(TraceState::IsValidKey(std::string(256, 'z'))); + EXPECT_FALSE(TraceState::IsValidKey(std::string(257, 'z'))); + // Old vendor-section limits (241 before @, 14 after) are gone; only total length matters + EXPECT_TRUE( + TraceState::IsValidKey(std::string(241, 't') + "@" + std::string(14, 'v'))); // 256 chars + EXPECT_TRUE(TraceState::IsValidKey(std::string(242, 't') + "@v")); // 244 chars + EXPECT_TRUE(TraceState::IsValidKey("t@" + std::string(15, 'v'))); // 17 chars } TEST(TraceStateTest, IsValidValue) From 3ff4551283b627a39b6c6de84f2bbd10cc8169eb Mon Sep 17 00:00:00 2001 From: proost Date: Sat, 18 Jul 2026 14:18:16 +0900 Subject: [PATCH 05/49] [CODE HEALTH] Move trace and baggage propagation test classes into anonymous namespace (#4199) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 9 ++------- api/test/baggage/propagation/baggage_propagator_test.cc | 5 +++++ api/test/trace/propagation/b3_propagation_test.cc | 5 +++++ api/test/trace/propagation/http_text_format_test.cc | 5 +++++ api/test/trace/propagation/jaeger_propagation_test.cc | 5 +++++ 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index d95edcab17..fa7407d999 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 328 + warning_limit: 324 - cmake_options: all-options-abiv2-preview - warning_limit: 338 + warning_limit: 334 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 584b5a32e1..cac61ba02e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,13 +125,8 @@ 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) +* [CODE HEALTH] Move trace and baggage propagation test classes into anonymous namespace + [#4199](https://github.com/open-telemetry/opentelemetry-cpp/pull/4199) ## [1.27.0] 2026-05-13 diff --git a/api/test/baggage/propagation/baggage_propagator_test.cc b/api/test/baggage/propagation/baggage_propagator_test.cc index 6335b2984b..fcb3a7f56d 100644 --- a/api/test/baggage/propagation/baggage_propagator_test.cc +++ b/api/test/baggage/propagation/baggage_propagator_test.cc @@ -20,6 +20,9 @@ using namespace opentelemetry; using namespace opentelemetry::baggage::propagation; +namespace +{ + class BaggageCarrierTest : public context::propagation::TextMapCarrier { public: @@ -120,3 +123,5 @@ TEST(BaggagePropagatorTest, InjectEmptyHeader) EXPECT_EQ(carrier.headers_.find(baggage::kBaggageHeader), carrier.headers_.end()); } } + +} // namespace diff --git a/api/test/trace/propagation/b3_propagation_test.cc b/api/test/trace/propagation/b3_propagation_test.cc index f34dad12ad..fefe2effdd 100644 --- a/api/test/trace/propagation/b3_propagation_test.cc +++ b/api/test/trace/propagation/b3_propagation_test.cc @@ -27,6 +27,9 @@ using namespace opentelemetry; +namespace +{ + class TextMapCarrierTest : public context::propagation::TextMapCarrier { public: @@ -219,3 +222,5 @@ TEST(B3PropagationTest, GetCurrentSpanMultiHeader) EXPECT_EQ(carrier.headers_["X-B3-SpanId"], "0102030405060708"); EXPECT_EQ(carrier.headers_["X-B3-Sampled"], "1"); } + +} // namespace diff --git a/api/test/trace/propagation/http_text_format_test.cc b/api/test/trace/propagation/http_text_format_test.cc index 78ae5f6ef4..6c105a82ce 100644 --- a/api/test/trace/propagation/http_text_format_test.cc +++ b/api/test/trace/propagation/http_text_format_test.cc @@ -32,6 +32,9 @@ using namespace opentelemetry; +namespace +{ + class TextMapCarrierTest : public context::propagation::TextMapCarrier { public: @@ -266,3 +269,5 @@ TEST(GlobalPropagator, SetAndGet) EXPECT_EQ(fields[0], trace::propagation::kTraceParent); EXPECT_EQ(fields[1], trace::propagation::kTraceState); } + +} // namespace diff --git a/api/test/trace/propagation/jaeger_propagation_test.cc b/api/test/trace/propagation/jaeger_propagation_test.cc index e7e7feffee..0b101fbe57 100644 --- a/api/test/trace/propagation/jaeger_propagation_test.cc +++ b/api/test/trace/propagation/jaeger_propagation_test.cc @@ -30,6 +30,9 @@ using namespace opentelemetry; +namespace +{ + class TextMapCarrierTest : public context::propagation::TextMapCarrier { public: @@ -204,3 +207,5 @@ TEST(JaegerPropagatorTest, DoNotInjectInvalidContext) format.Inject(carrier, ctx); EXPECT_TRUE(carrier.headers_.count("uber-trace-id") == 0); } + +} // namespace From d478b8cb0ad341535eb5428cc793f97684891c60 Mon Sep 17 00:00:00 2001 From: WenTao Ou Date: Thu, 2 Jul 2026 22:00:09 +0800 Subject: [PATCH 06/49] [CMAKE] Add pkg-config support for OpenTelemetry proto installation (#4192) --- cmake/opentelemetry-proto.cmake | 36 ++++++++++++++++++++++++--------- cmake/pkgconfig.cmake | 2 ++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/cmake/opentelemetry-proto.cmake b/cmake/opentelemetry-proto.cmake index c77a87488d..4c512071e9 100644 --- a/cmake/opentelemetry-proto.cmake +++ b/cmake/opentelemetry-proto.cmake @@ -395,15 +395,6 @@ endif() set_target_properties(opentelemetry_proto PROPERTIES EXPORT_NAME proto) patch_protobuf_targets(opentelemetry_proto) -if(OPENTELEMETRY_INSTALL) - install( - DIRECTORY ${GENERATED_PROTOBUF_PATH}/opentelemetry - DESTINATION include - COMPONENT exporters_otlp_common - FILES_MATCHING - PATTERN "*.h") -endif() - if(TARGET protobuf::libprotobuf) target_link_libraries(opentelemetry_proto PUBLIC protobuf::libprotobuf) else() # cmake 3.8 or lower @@ -424,3 +415,30 @@ if(BUILD_SHARED_LIBS) set_property(TARGET ${proto_target} PROPERTY POSITION_INDEPENDENT_CODE ON) endforeach() endif() + +if(OPENTELEMETRY_INSTALL) + install( + DIRECTORY ${GENERATED_PROTOBUF_PATH}/opentelemetry + DESTINATION include + COMPONENT exporters_otlp_common + FILES_MATCHING + PATTERN "*.h") + + include(${PROJECT_SOURCE_DIR}/cmake/pkgconfig.cmake) + + opentelemetry_add_pkgconfig( + proto + "OpenTelemetry - Protocol" + "Protocols for spans, metrics, and logs." + "protobuf" + ) + + if(WITH_OTLP_GRPC) + opentelemetry_add_pkgconfig( + proto_grpc + "OpenTelemetry - Protocol (gRPC)" + "Protocols for spans, metrics, and logs over gRPC." + "opentelemetry_proto grpc++ protobuf" + ) + endif() +endif() diff --git a/cmake/pkgconfig.cmake b/cmake/pkgconfig.cmake index 7bff4cb48c..63c1a2affd 100644 --- a/cmake/pkgconfig.cmake +++ b/cmake/pkgconfig.cmake @@ -1,6 +1,8 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 +include_guard(GLOBAL) + # Unlike functions, macros do not introduce a scope. This is an advantage when # trying to set global variables, as we do here. macro (opentelemetry_set_pkgconfig_paths) From bee03422ac61a061e9a54e1164f710621c693d25 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:48:28 -0400 Subject: [PATCH 07/49] [CMAKE] Fix CMake WITH_API_ONLY option (#4201) --- .github/workflows/ci.yml | 22 +++ CHANGELOG.md | 5 +- CMakeLists.txt | 54 ++++++-- ci/do_ci.sh | 12 +- .../test/cmake/api_only_test/CMakeLists.txt | 117 ++++++++++++++++ install/test/src/test_api.cc | 128 +++++++++++++++++- 6 files changed, 321 insertions(+), 17 deletions(-) create mode 100644 install/test/cmake/api_only_test/CMakeLists.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b97d89c74c..9098076489 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,28 @@ jobs: run: | ./ci/do_ci.sh cmake.test + cmake_api_only_test: + name: CMake API only test + runs-on: ubuntu-24.04 + env: + CXX_STANDARD: '17' + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + submodules: 'recursive' + - name: setup + run: | + sudo -E ./ci/setup_ci_environment.sh + sudo -E apt-get install -y zlib1g-dev + - name: run cmake API only test + run: | + ./ci/do_ci.sh cmake.api_only.test + cmake_fetch_content_test: name: CMake FetchContent usage with opentelemetry-cpp runs-on: ubuntu-24.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index cac61ba02e..f1873ef62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [CMAKE] Fix and test WITH_API_ONLY option + [#4201](https://github.com/open-telemetry/opentelemetry-cpp/pull/4201) + * [API] Fix `TraceState::IsValidKey()` to comply with the W3C Trace Context Level 2, where keys containing `@` and keys with more than 241 characters before `@` or more than 14 characters after `@` are now accepted. @@ -25,7 +28,7 @@ Increment the: [#4194](https://github.com/open-telemetry/opentelemetry-cpp/pull/4194) * [SDK] Add `TracerProvider::UpdateTracerConfigurator()` and example - [#4065](https://github.com/open-telemetry/opentelemetry-cpp/issues/4065) + [#4065](https://github.com/open-telemetry/opentelemetry-cpp/pull/4065) * [RELEASE] Bump main branch to 1.28.0-dev [#4081](https://github.com/open-telemetry/opentelemetry-cpp/pull/4081) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1431475bf6..e44aa1544f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,7 +269,9 @@ if(WITH_GSL) include("${opentelemetry-cpp_SOURCE_DIR}/cmake/ms-gsl.cmake") endif() -find_package(Threads) +if(NOT WITH_API_ONLY) + find_package(Threads) +endif() function(set_target_version target_name) if(OTELCPP_VERSIONED_LIBS) @@ -306,13 +308,36 @@ cmake_dependent_option( "WITH_OTLP_HTTP OR WITH_ELASTICSEARCH OR WITH_ZIPKIN OR BUILD_W3CTRACECONTEXT_TEST OR WITH_EXAMPLES_HTTP" OFF) +# +# Locally override options if WITH_API_ONLY is set to prevent third party +# dependencies from being imported +# + +if(WITH_API_ONLY) + set(WITH_EXAMPLES OFF) + set(WITH_EXAMPLES_HTTP OFF) + set(WITH_OTLP_GRPC OFF) + set(WITH_OTLP_HTTP OFF) + set(WITH_OTLP_FILE OFF) + set(WITH_ZIPKIN OFF) + set(WITH_PROMETHEUS OFF) + set(WITH_ELASTICSEARCH OFF) + set(WITH_OPENTRACING OFF) + set(WITH_ETW OFF) + set(WITH_CONFIGURATION OFF) + set(WITH_HTTP_CLIENT_CURL OFF) + set(WITH_OTLP_HTTP_COMPRESSION OFF) + set(WITH_BENCHMARK OFF) + set(WITH_FUNC_TESTS OFF) + set(BUILD_TESTING OFF) + set(BUILD_W3CTRACECONTEXT_TEST OFF) +endif() + # # Do we need ZLIB ? # -if((NOT WITH_API_ONLY) - AND WITH_HTTP_CLIENT_CURL - AND WITH_OTLP_HTTP_COMPRESSION) +if(WITH_HTTP_CLIENT_CURL AND WITH_OTLP_HTTP_COMPRESSION) include("${opentelemetry-cpp_SOURCE_DIR}/cmake/zlib.cmake") endif() @@ -320,7 +345,7 @@ endif() # Do we need CURL ? # -if((NOT WITH_API_ONLY) AND WITH_HTTP_CLIENT_CURL) +if(WITH_HTTP_CLIENT_CURL) include("${opentelemetry-cpp_SOURCE_DIR}/cmake/curl.cmake") endif() @@ -366,7 +391,7 @@ else() set(USE_NLOHMANN_JSON OFF) endif() -if((NOT WITH_API_ONLY) AND USE_NLOHMANN_JSON) +if(USE_NLOHMANN_JSON) include("${opentelemetry-cpp_SOURCE_DIR}/cmake/nlohmann-json.cmake") endif() @@ -374,7 +399,7 @@ endif() # Do we need RapidYaml ? # -if((NOT WITH_API_ONLY) AND WITH_CONFIGURATION) +if(WITH_CONFIGURATION) include("${opentelemetry-cpp_SOURCE_DIR}/cmake/ryml.cmake") endif() @@ -640,11 +665,11 @@ endif() add_subdirectory(api) -if(WITH_OPENTRACING) - add_subdirectory(opentracing-shim) -endif() - if(NOT WITH_API_ONLY) + if(WITH_OPENTRACING) + add_subdirectory(opentracing-shim) + endif() + set(BUILD_TESTING ${BUILD_TESTING}) add_subdirectory(sdk) @@ -663,11 +688,12 @@ if(NOT WITH_API_ONLY) if(WITH_FUNC_TESTS) add_subdirectory(functional) endif() + + include( + "${opentelemetry-cpp_SOURCE_DIR}/cmake/opentelemetry-build-external-component.cmake" + ) endif() -include( - "${opentelemetry-cpp_SOURCE_DIR}/cmake/opentelemetry-build-external-component.cmake" -) include("${opentelemetry-cpp_SOURCE_DIR}/cmake/patch-imported-config.cmake") if(OPENTELEMETRY_INSTALL) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index c6b85c988b..cc825c37f1 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -538,7 +538,17 @@ elif [[ "$1" == "cmake.fetch_content.test" ]]; then cmake --build . "${CMAKE_BUILD_ARGS[@]}" ctest --output-on-failure exit 0 - +elif [[ "$1" == "cmake.api_only.test" ]]; then + cd "${BUILD_DIR}" + rm -rf * + cmake "${CMAKE_OPTIONS[@]}" \ + -C ${SRC_DIR}/test_common/cmake/all-options-abiv1-preview.cmake \ + -DOPENTELEMETRY_INSTALL=OFF \ + -DOPENTELEMETRY_CPP_SRC_DIR="${SRC_DIR}" \ + "${SRC_DIR}/install/test/cmake/api_only_test" + cmake --build . "${CMAKE_BUILD_ARGS[@]}" + ctest --output-on-failure + exit 0 elif [[ "$1" == "cmake.test_example_plugin" ]]; then # Build the plugin cd "${BUILD_DIR}" diff --git a/install/test/cmake/api_only_test/CMakeLists.txt b/install/test/cmake/api_only_test/CMakeLists.txt new file mode 100644 index 0000000000..2ec3e04cc1 --- /dev/null +++ b/install/test/cmake/api_only_test/CMakeLists.txt @@ -0,0 +1,117 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# This test covers the WITH_API_ONLY build option of opentelemetry-cpp. It +# ensures that no third-party libraries are included in the build and that only +# the opentelemetry-cpp::api target is created. + +cmake_minimum_required(VERSION 3.16) + +project(opentelemetry-cpp-api-only-test LANGUAGES CXX) + +if(NOT OPENTELEMETRY_CPP_SRC_DIR) + message( + FATAL_ERROR + "OPENTELEMETRY_CPP_SRC_DIR must be defined when running cmake on this test project" + ) +endif() + +set(EXPECTED_OPTIONS_IN_CACHE + WITH_EXAMPLES + WITH_EXAMPLES_HTTP + BUILD_TESTING + WITH_FUNC_TESTS + WITH_BENCHMARK + WITH_OTLP_GRPC + WITH_OTLP_HTTP + WITH_OTLP_FILE + WITH_OTLP_HTTP_COMPRESSION + WITH_PROMETHEUS + WITH_ZIPKIN + WITH_ELASTICSEARCH + WITH_OPENTRACING + WITH_CONFIGURATION) + +function(check_options_in_cache) + foreach(component IN LISTS EXPECTED_OPTIONS_IN_CACHE) + if(NOT ${component}) + message( + FATAL_ERROR + "Expected component ${component} to be ON in the cache, but it is OFF." + ) + endif() + endforeach() +endfunction() + +# Verify that all options which may bring in a third-party dependency are ON in +# the cache. +check_options_in_cache() + +# Turn on the WITH_API_ONLY build option and disable GSL. This must override any +# other options and not import third party targets. +set(WITH_API_ONLY ON) +set(WITH_GSL OFF) + +# Add the opentelemetry-cpp source directory as a subdirectory to mimic +# vendoring the library. +add_subdirectory(${OPENTELEMETRY_CPP_SRC_DIR} + ${CMAKE_BINARY_DIR}/opentelemetry-cpp) + +# Verify that all options which may bring in a third-party dependency are still +# ON in the cache. +check_options_in_cache() + +set(UNEXPECTED_TARGETS + opentelemetry-cpp::sdk + Microsoft.GSL::GSL + Threads::Threads + ZLIB::ZLIB + CURL::libcurl + protobuf::libprotobuf + gRPC::grpc++ + nlohmann_json::nlohmann_json + OpenTracing::opentracing + prometheus-cpp::core + ryml::ryml + GTest::gtest + benchmark::benchmark) + +foreach(target IN LISTS UNEXPECTED_TARGETS) + if(TARGET ${target}) + message( + FATAL_ERROR + "Unexpected target ${target} was created in an WITH_API_ONLY build.") + endif() +endforeach() + +if(NOT TARGET opentelemetry-cpp::api) + message( + FATAL_ERROR + "opentelemetry-cpp::api target was not created in an WITH_API_ONLY build." + ) +endif() + +include(FetchContent) + +FetchContent_Declare( + googletest SOURCE_DIR ${OPENTELEMETRY_CPP_SRC_DIR}/third_party/googletest) + +FetchContent_MakeAvailable(googletest) + +message(STATUS "opentelemetry-cpp_SRC_DIR: ${OPENTELEMETRY_CPP_SRC_DIR}") + +set(BUILD_TESTING ON) + +add_executable(api_only_test + ${OPENTELEMETRY_CPP_SRC_DIR}/install/test/src/test_api.cc) + +target_link_libraries(api_only_test PRIVATE opentelemetry-cpp::api GTest::gtest + GTest::gtest_main) + +include(CTest) +include(GoogleTest) + +gtest_add_tests( + TARGET api_only_test + TEST_PREFIX api_only. + TEST_LIST api_only_test) diff --git a/install/test/src/test_api.cc b/install/test/src/test_api.cc index 6fc88663f5..ed2036e819 100644 --- a/install/test/src/test_api.cc +++ b/install/test/src/test_api.cc @@ -3,6 +3,9 @@ #include +#include +#include + #include #include @@ -20,6 +23,35 @@ #include #include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + TEST(ApiInstallTest, VersionCheck) { EXPECT_GE(OPENTELEMETRY_VERSION_MAJOR, 0); @@ -88,4 +120,98 @@ TEST(ApiInstallTest, MetricsApiCheck) auto async_updown_counter = meter->CreateInt64ObservableUpDownCounter("test-async-updown-counter"); ASSERT_TRUE(async_updown_counter != nullptr); -} \ No newline at end of file +} + +TEST(ApiInstallTest, BaggageApiCheck) +{ + auto baggage = opentelemetry::baggage::Baggage::GetDefault(); + ASSERT_TRUE(baggage != nullptr); + + auto baggage_with_entry = baggage->Set("key", "value"); + ASSERT_TRUE(baggage_with_entry != nullptr); + + std::string retrieved_value; + EXPECT_TRUE(baggage_with_entry->GetValue("key", retrieved_value)); + EXPECT_EQ(retrieved_value, "value"); + + auto context = opentelemetry::context::RuntimeContext::GetCurrent(); + auto ctx_with_baggage = opentelemetry::baggage::SetBaggage(context, baggage_with_entry); + auto retrieved_baggage = opentelemetry::baggage::GetBaggage(ctx_with_baggage); + ASSERT_TRUE(retrieved_baggage != nullptr); + + retrieved_value.clear(); + EXPECT_TRUE(retrieved_baggage->GetValue("key", retrieved_value)); + EXPECT_EQ(retrieved_value, "value"); +} + +TEST(ApiInstallTest, ContextPropagationApiCheck) +{ + struct MapCarrier : public opentelemetry::context::propagation::TextMapCarrier + { + std::unordered_map headers; + + opentelemetry::nostd::string_view Get( + opentelemetry::nostd::string_view key) const noexcept override + { + auto it = headers.find(std::string(key)); + if (it != headers.end()) + { + return it->second; + } + return ""; + } + void Set(opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view value) noexcept override + { + headers[std::string(key)] = std::string(value); + } + }; + + auto propagator = + opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); + ASSERT_TRUE(propagator != nullptr); + + MapCarrier carrier; + auto context = + opentelemetry::context::RuntimeContext::GetCurrent().SetValue("test-key", "test-value"); + propagator->Inject(carrier, context); + auto extracted = propagator->Extract(carrier, context); + EXPECT_EQ(extracted, context); + EXPECT_TRUE(extracted.HasKey("test-key")); +} + +TEST(SemconvInstallTest, SchemaUrl) +{ + EXPECT_NE(opentelemetry::semconv::kSchemaUrl, nullptr); + EXPECT_STRNE(opentelemetry::semconv::kSchemaUrl, ""); +} + +TEST(SemconvInstallTest, HttpMetrics) +{ + auto provider = opentelemetry::metrics::Provider::GetMeterProvider(); + ASSERT_TRUE(provider != nullptr); + + auto meter = provider->GetMeter("test-semconv-meter"); + ASSERT_TRUE(meter != nullptr); + + auto http_client_duration = + opentelemetry::semconv::http::CreateSyncDoubleMetricHttpClientRequestDuration(meter.get()); + ASSERT_TRUE(http_client_duration != nullptr); + + auto http_server_duration = + opentelemetry::semconv::http::CreateSyncInt64MetricHttpServerRequestDuration(meter.get()); + ASSERT_TRUE(http_server_duration != nullptr); +} + +TEST(SemconvInstallTest, DbMetrics) +{ + auto provider = opentelemetry::metrics::Provider::GetMeterProvider(); + ASSERT_TRUE(provider != nullptr); + + auto meter = provider->GetMeter("test-semconv-meter"); + ASSERT_TRUE(meter != nullptr); + + auto db_client_duration = + opentelemetry::semconv::db::CreateSyncDoubleMetricDbClientOperationDuration(meter.get()); + ASSERT_TRUE(db_client_duration != nullptr); +} From 604110ccffd0f7173fd93e1818aa4dfeaeea88be Mon Sep 17 00:00:00 2001 From: Mateen Anjum Date: Thu, 2 Jul 2026 19:17:09 -0400 Subject: [PATCH 08/49] [CODE HEALTH] Move context propagation test classes into anonymous namespace (#4200) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ api/test/context/propagation/composite_propagator_test.cc | 5 +++++ api/test/context/propagation/environment_carrier_test.cc | 5 +++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index fa7407d999..1225f05511 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 324 + warning_limit: 321 - cmake_options: all-options-abiv2-preview - warning_limit: 334 + warning_limit: 331 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index f1873ef62a..edd8db5012 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,9 @@ Increment the: * [CODE HEALTH] Move trace and baggage propagation test classes into anonymous namespace [#4199](https://github.com/open-telemetry/opentelemetry-cpp/pull/4199) +* [CODE HEALTH] Move context propagation test classes into anonymous namespace + [#4200](https://github.com/open-telemetry/opentelemetry-cpp/pull/4200) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/api/test/context/propagation/composite_propagator_test.cc b/api/test/context/propagation/composite_propagator_test.cc index 49b1668cb5..b2745aa331 100644 --- a/api/test/context/propagation/composite_propagator_test.cc +++ b/api/test/context/propagation/composite_propagator_test.cc @@ -38,6 +38,9 @@ static std::string Hex(const T &id_item) return std::string(buf, sizeof(buf)); } +namespace +{ + class TextMapCarrierTest : public context::propagation::TextMapCarrier { public: @@ -147,3 +150,5 @@ TEST_F(CompositePropagatorTest, Inject) EXPECT_EQ(fields[1], trace::propagation::kTraceState); EXPECT_EQ(fields[2], trace::propagation::kB3CombinedHeader); } + +} // namespace diff --git a/api/test/context/propagation/environment_carrier_test.cc b/api/test/context/propagation/environment_carrier_test.cc index 666630d01d..ef62cdb110 100644 --- a/api/test/context/propagation/environment_carrier_test.cc +++ b/api/test/context/propagation/environment_carrier_test.cc @@ -56,6 +56,9 @@ static std::string Hex(const T &id_item) return std::string(buf, sizeof(buf)); } +namespace +{ + class EnvironmentCarrierTest : public ::testing::Test { protected: @@ -306,3 +309,5 @@ TEST_F(EnvironmentCarrierTest, RoundTrip) EXPECT_EQ(extracted_span->GetContext().IsSampled(), span_context.IsSampled()); EXPECT_TRUE(extracted_span->GetContext().IsRemote()); } + +} // namespace From 533c08410fce4185b506a537248a295c039f8f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=80=E5=90=89?= <84045975+thc1006@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:52:00 +0800 Subject: [PATCH 09/49] [CODE HEALTH] Fix clang-tidy bugprone-unused-local-non-trivial-variable warnings (#4202) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ api/test/baggage/baggage_benchmark.cc | 4 ++-- examples/otlp/file_metric_main.cc | 3 --- examples/otlp/http_instrumented_main.cc | 3 --- examples/otlp/http_metric_main.cc | 3 --- exporters/otlp/test/otlp_file_client_test.cc | 4 ---- exporters/prometheus/src/exporter_utils.cc | 2 -- 8 files changed, 7 insertions(+), 19 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 1225f05511..4123329d86 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 321 + warning_limit: 309 - cmake_options: all-options-abiv2-preview - warning_limit: 331 + warning_limit: 319 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index edd8db5012..aa0af0bf8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,9 @@ Increment the: * [CODE HEALTH] Move context propagation test classes into anonymous namespace [#4200](https://github.com/open-telemetry/opentelemetry-cpp/pull/4200) +* [CODE HEALTH] Fix clang-tidy bugprone-unused-local-non-trivial-variable warnings + [#4202](https://github.com/open-telemetry/opentelemetry-cpp/pull/4202) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/api/test/baggage/baggage_benchmark.cc b/api/test/baggage/baggage_benchmark.cc index 3901bfecaa..a0a7c13264 100644 --- a/api/test/baggage/baggage_benchmark.cc +++ b/api/test/baggage/baggage_benchmark.cc @@ -102,7 +102,7 @@ void BM_BaggageToHeaderTenEntries(benchmark::State &state) auto baggage = Baggage::FromHeader(header_with_custom_entries(kNumEntries)); while (state.KeepRunning()) { - auto new_baggage = baggage->ToHeader(); + benchmark::DoNotOptimize(baggage->ToHeader()); } } BENCHMARK(BM_BaggageToHeaderTenEntries); @@ -112,7 +112,7 @@ void BM_BaggageToHeader180Entries(benchmark::State &state) auto baggage = Baggage::FromHeader(header_with_custom_entries(Baggage::kMaxKeyValuePairs)); while (state.KeepRunning()) { - auto new_baggage = baggage->ToHeader(); + benchmark::DoNotOptimize(baggage->ToHeader()); } } BENCHMARK(BM_BaggageToHeader180Entries); diff --git a/examples/otlp/file_metric_main.cc b/examples/otlp/file_metric_main.cc index 96aef81051..1e484485b1 100644 --- a/examples/otlp/file_metric_main.cc +++ b/examples/otlp/file_metric_main.cc @@ -40,9 +40,6 @@ void InitMetrics() { auto exporter = otlp_exporter::OtlpFileMetricExporterFactory::Create(exporter_options); - std::string version{"1.2.0"}; - std::string schema{"https://opentelemetry.io/schemas/1.2.0"}; - // Initialize and set the global MeterProvider metrics_sdk::PeriodicExportingMetricReaderOptions reader_options; reader_options.export_interval_millis = std::chrono::milliseconds(1000); diff --git a/examples/otlp/http_instrumented_main.cc b/examples/otlp/http_instrumented_main.cc index 7a82abde27..7f45c44ec4 100644 --- a/examples/otlp/http_instrumented_main.cc +++ b/examples/otlp/http_instrumented_main.cc @@ -217,9 +217,6 @@ void InitMetrics() auto exporter = opentelemetry::exporter::otlp::OtlpHttpMetricExporterFactory::Create(meter_opts, exp_rt_opts); - std::string version{"1.2.0"}; - std::string schema{"https://opentelemetry.io/schemas/1.2.0"}; - // Initialize and set the global MeterProvider opentelemetry::sdk::metrics::PeriodicExportingMetricReaderOptions reader_options; reader_options.export_interval_millis = std::chrono::milliseconds(1000); diff --git a/examples/otlp/http_metric_main.cc b/examples/otlp/http_metric_main.cc index 59527d4514..0e1403debd 100644 --- a/examples/otlp/http_metric_main.cc +++ b/examples/otlp/http_metric_main.cc @@ -43,9 +43,6 @@ void InitMetrics() { auto exporter = otlp_exporter::OtlpHttpMetricExporterFactory::Create(exporter_options); - std::string version{"1.2.0"}; - std::string schema{"https://opentelemetry.io/schemas/1.2.0"}; - // Initialize and set the global MeterProvider metrics_sdk::PeriodicExportingMetricReaderOptions reader_options; reader_options.export_interval_millis = std::chrono::milliseconds(1000); diff --git a/exporters/otlp/test/otlp_file_client_test.cc b/exporters/otlp/test/otlp_file_client_test.cc index 91ddcc944c..6260559720 100644 --- a/exporters/otlp/test/otlp_file_client_test.cc +++ b/exporters/otlp/test/otlp_file_client_test.cc @@ -267,8 +267,6 @@ TEST(OtlpFileClientTest, ExportToFileSystemRotateIndexTest) opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request; OtlpRecordableUtils::PopulateRequest(MakeSpan(recordable), &request); - std::stringstream output_stream; - // Clear old files { std::fstream clear_file1("otlp_file_client_test_dir/trace-1.jsonl", @@ -398,8 +396,6 @@ TEST(OtlpFileClientTest, ExportToFileSystemRotateByTimeTest) opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request; OtlpRecordableUtils::PopulateRequest(MakeSpan(recordable), &request); - std::stringstream output_stream; - opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions backend_opts; backend_opts.file_pattern = "otlp_file_client_test_dir/trace-%Y-%m-%d-%H-%M-%S.jsonl"; backend_opts.alias_pattern = ""; diff --git a/exporters/prometheus/src/exporter_utils.cc b/exporters/prometheus/src/exporter_utils.cc index 55105bace8..694a30a079 100644 --- a/exporters/prometheus/src/exporter_utils.cc +++ b/exporters/prometheus/src/exporter_utils.cc @@ -143,8 +143,6 @@ std::vector PrometheusExporterUtils::TranslateT { for (const auto &metric_data : instrumentation_info.metric_data_) { - auto origin_name = metric_data.instrument_descriptor.name_; - auto unit = metric_data.instrument_descriptor.unit_; prometheus_client::MetricFamily metric_family; metric_family.help = metric_data.instrument_descriptor.description_; auto front = metric_data.point_data_attr_.front(); From a164f73d6aa880e2192fd886e5ab79907073724a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=80=E5=90=89?= <84045975+thc1006@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:34:52 +0800 Subject: [PATCH 10/49] [SDK] LogRecord attribute limits enforcement (#4157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [SDK] LogRecord attribute limits enforcement Apply attribute count and value length limits described by the logs SDK spec (https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecord-limits) during attribute writes. Limits flow from LoggerProvider through LoggerContext to each LogRecord created by Logger::CreateLogRecord. * Add LogRecordLimits struct with spec defaults: attribute_count_limit = 128 and attribute_value_length_limit = SIZE_MAX (unlimited). * Recordable gains a virtual SetLogRecordLimits with a no-op default so existing implementations need not change. ReadableLogRecord gains a virtual GetDroppedAttributesCount returning zero by default. * ReadWriteLogRecord and OtlpLogRecordable enforce the limits in SetAttribute. An attribute beyond attribute_count_limit is dropped and counted as dropped; string and string-array values whose byte length exceeds attribute_value_length_limit are truncated. Truncation is byte-level, mirroring the existing Span attribute behavior. The OTLP path also populates dropped_attributes_count on the proto LogRecord. * MultiRecordable propagates the limits to every wrapped recordable. * LoggerContext owns a LogRecordLimits value; Logger calls SetLogRecordLimits on the recordable returned by MakeRecordable before any user attribute writes. A new LoggerProviderFactory::Create overload accepts LogRecordLimits. * The declarative configuration path (SdkBuilder) wires LogRecordLimitsConfiguration to the runtime LogRecordLimits. Tests cover ReadWriteLogRecord and OtlpLogRecordable: defaults, count enforcement (including the "replace existing key while at limit must not drop" case), length truncation of strings and string arrays, type selectivity (only string and array-of-string are truncated), the combined count plus length case, and a Logger-level wiring test that verifies the limits configured on LoggerProvider reach the recordable returned by Logger::CreateLogRecord. Fixes #4126 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: address review (store by value, OTLP UTF-8-aware truncate) Address three review comments from @lalitb on PR #4157, plus a follow-up from @owent (r3434178971). 1. Store LogRecordLimits by value inside ReadWriteLogRecord and OtlpLogRecordable instead of a raw pointer to a LoggerContext-owned object. A LogRecord is handed out as a unique_ptr, so a record outliving the context that produced it would otherwise dereference a dangling pointer when the user calls SetAttribute() later. The default-constructed value already carries the spec defaults (count=128, length=unlimited), so a fresh recordable enforces the spec count cap from construction, matching the PR's "enforcement" contract. 2. Drop the now-redundant `limits_ != nullptr` short-circuit at every enforcement site (4 in total). This also closes the Codecov-reported uncovered branch in otlp_log_recordable.cc. 3. Truncate OTLP string attributes at a UTF-8 code-point boundary instead of a raw byte boundary, so the protobuf string_value produced by truncation stays valid UTF-8 when the input was. Malformed UTF-8 and trailing lead bytes degrade to plain byte truncation. Logic adapted from #4132 with attribution. The SDK-side ReadWriteLogRecord truncation stays as plain byte cut. The in-memory `OwnedAttributeValue::std::string` variant may legitimately carry raw bytes when constructed from a non-UTF-8 source, so forcing UTF-8 boundary semantics there would over-truncate that case (per @owent's r3434178971, echoing the same point on #4132 r3409677314). Each recordable's truncation strategy now matches its own consumer's wire-format requirement: SDK in-memory has no wire requirement, OTLP protobuf requires valid UTF-8. While in the same truncation paths, also apply the byte-length cap to raw bytes attributes (`vector` on the SDK side, AnyValue `bytes_value` on the OTLP side). Both were previously passing through any size, even though the spec applies `attribute_value_length_limit` to bytes attributes as well. Test changes: - Rename DefaultsPassThroughWithoutLimitsObject to DefaultRecordEnforcesSpecCountCap (200 attrs in, 128 stored, 72 dropped) to reflect the new spec-correct default behavior. - Add bytes-truncation tests on both SDK and OTLP sides. - Add 3 UTF-8 regression tests on the OTLP side only (split prevention, exact fit at sequence boundary; malformed-fallback omitted since the algorithm's seq=1 fallback for invalid continuations is implementation detail rather than wire contract). - Add a default-cap test on the OTLP side. Refs: open-telemetry/opentelemetry-cpp#4126 Co-authored-by: Hyeonho Kim Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: address dbarker review (extract utilities, dedupe SetAttribute lookup) Address @dbarker's three review comments on PR #4157. * r3442854800: Extract Utf8SafePrefixLength and TruncateProtoAttributeValue from the anonymous namespace in otlp_log_recordable.cc into OtlpPopulateAttributeUtils as static methods, with new direct unit tests in otlp_populate_attribute_utils_test.cc. The upcoming SpanLimits PR will reuse these from the OTLP trace recordable. The OTLP helper that was previously TruncateProtoStringValue is renamed TruncateProtoAttributeValue to reflect that it covers string_value, bytes_value, and array_value branches. * r3443005793: Extract the SDK byte-length truncation helper into sdk::common::TruncateAttributeValueByteLength (inline, declared in the existing sdk/common/attribute_utils.h), with new direct unit tests in attribute_utils_test.cc. The upcoming SpanLimits PR will reuse this from ReadWriteSpanData. The new name reflects that the helper covers string, string-array, AND bytes variants rather than only strings. * r3443034336: Rewrite ReadWriteLogRecord::SetAttribute to use a single unordered_map lookup. The previous code did .find() to gate the count cap, then operator[] to fetch-or-insert; the new code does .find() followed by conditional .emplace(), so existing-key replacement and new-key insertion each cost one hash lookup. Refs: open-telemetry/opentelemetry-cpp#4126 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: fix noexcept correctness + restore Bazel link Two small fixes on top of c4532cd1: * Address @dbarker's r3444034234: rewrite sdk::common::TruncateAttributeValueByteLength to dispatch on the variant via nostd::get_if (returns nullptr if the alternative does not hold) instead of nostd::holds_alternative + nostd::get. The helper is declared noexcept; nostd::get throws when the alternative does not match, which would invoke std::terminate even though the preceding holds_alternative check makes that path unreachable in practice. The get_if rewrite removes the throwing call entirely so the noexcept contract is statically honored. * Restore the Bazel link of the new otlp_populate_attribute_utils_test target by adding //sdk/src/metrics to its deps, matching the existing otlp_log_recordable_test target. The new test target links against :otlp_recordable, which transitively references sdk::metrics::AdaptingCircularBufferCounter symbols; Bazel's strict layering requires the dep to be declared at the cc_test level. CMake did not catch this because its default link aggregates the whole library. Refs: open-telemetry/opentelemetry-cpp#4126 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: fix MSVC variable shadow + drop dead includes + clang-tidy NOLINT Three CI failure classes surfaced after the branch-update merge of main into the PR branch (ccdd5e9286). Address each: * MSVC C2220 (warning-as-error) on attribute_utils.h:93 — the `for (auto &s : *vec)` loop inside TruncateAttributeValueByteLength shadowed the `if (auto *s = get_if(...))` on line 84. GCC/Clang accept if-init scoping for the two `s`, MSVC /W4 /WX treats C4456 (declaration hides previous local) as an error. Rename the loop variable to `element` to remove the shadow. * IWYU drop four includes that the v3+v4 utility-extraction refactor made redundant: - sdk/src/logs/read_write_log_record.cc: - exporters/otlp/src/otlp_log_recordable.cc: - exporters/otlp/test/otlp_populate_attribute_utils_test.cc: and * clang-tidy abiv2-preview misc-no-recursion on OtlpPopulateAttributeUtils::TruncateProtoAttributeValue — the recursive descent into AnyValue::kArrayValue children is intentional and bounded by the SDK-side AttributeValue variant depth. Suppress with a NOLINTBEGIN/END(misc-no-recursion) block, matching the codebase precedent in sdk/src/configuration/configuration_parser.cc. The remaining clang-tidy bugprone-exception-escape warnings in the same config (read_write_log_record.cc:68 SetBody, :158 SetAttribute and otlp_populate_attribute_utils.cc:62/220 PopulateAnyValue) trace through nostd::visit / nostd::get into bad_alloc paths that pre-date this PR; they are out of scope for the LogRecord limits change. Refs: open-telemetry/opentelemetry-cpp#4126 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: address dbarker v3 review (truncate during conversion) Address @dbarker's three review comments from 2026-06-23 (r3461438236, r3461500683, r3461573442) by moving attribute-value-length truncation from a separate post-conversion step into the conversion paths themselves. Architecture change: Before: convert, default-construct map slot, assign converted, post-truncate (helper) After: convert-with-limit, single emplace sdk/include/opentelemetry/sdk/common/attribute_utils.h * AttributeConverter gains an `explicit AttributeConverter(std::size_t max_length)` constructor. String, bytes, and string-array overloads apply the byte-length cap during the OwnedAttributeValue construction. The default-constructed converter is unchanged (`max_length_ = numeric_limits::max()`), so the existing callers in `instrumentation_scope.h` and `read_write_log_record.cc SetBody` keep their current no-truncation behavior. * Delete `TruncateAttributeValueByteLength` (its three branches now live in the converter; the lone production caller was the SDK ReadWriteLogRecord SetAttribute). sdk/src/logs/read_write_log_record.cc * `SetAttribute` rewritten to a single `find` + conditional `emplace`, with the limit-aware converter producing the truncated value in one step. Removes the previous default-construct-then-assign sequence and the now-redundant post-truncate branch. exporters/otlp/include/.../otlp_populate_attribute_utils.h exporters/otlp/src/otlp_populate_attribute_utils.cc * All four `PopulateAttribute` / `PopulateAnyValue` overloads gain a trailing `std::size_t max_length = numeric_limits::max()` parameter (default preserves the existing call-site behavior for the other callers in metrics/recordable/resource paths). * String and bytes branches apply truncation in place during the proto set: `Utf8SafePrefixLength` for `string_value` (preserves protobuf wire-format valid UTF-8), plain byte cut for `bytes_value`. * Delete `TruncateProtoAttributeValue`. The flat-AttributeValue input means the recursive array descent it carried was dead code, as dbarker noted. The `NOLINT(misc-no-recursion)` block goes with it. * `Utf8SafePrefixLength` primary overload now takes `(const char*, std::size_t, std::size_t)` so it can be called without constructing a temporary `std::string` from a `string_view`. A thin inline `(const std::string&, std::size_t)` overload preserves backward compatibility for any existing direct user. exporters/otlp/src/otlp_log_recordable.cc * `SetAttribute` now calls the limit-aware `PopulateAttribute` directly in one step, removing the separate post-truncate branch. Test changes: * Delete the five `TruncateAttributeValueByteLength` unit tests in `sdk/test/common/attribute_utils_test.cc` (helper is gone; end-to-end coverage is preserved by `log_record_limits_test.cc`). * Delete the six `TruncateProtoAttributeValue` unit tests in `exporters/otlp/test/otlp_populate_attribute_utils_test.cc` (helper is gone; end-to-end coverage is preserved by `otlp_log_recordable_test.cc`). * The seven `Utf8SafePrefixLength` unit tests still cover the helper that the new in-place truncation path calls into. Refs: open-telemetry/opentelemetry-cpp#4126 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: gate SetLogRecordLimits on processor capability CreateLogRecord() called SetLogRecordLimits() on every record unconditionally, paying a virtual dispatch even for recordables that do not enforce limits. Add a RecordableEnforcesLogRecordLimits() capability query on LogRecordExporter and LogRecordProcessor (default false; OTLP and ostream exporters return true; Simple and Batch processors delegate to their exporter; Multi returns true if any child does). LoggerContext caches the result at construction and refreshes it in AddProcessor, so the Logger hot path reads a plain bool and only calls SetLogRecordLimits() when a processor actually enforces limits. The new virtuals are appended at the end of their vtables to keep the change additive. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: UTF-8-safe truncation in AttributeConverter The in-memory AttributeConverter cut string values at a raw byte boundary, which can split a multi-byte UTF-8 sequence and leave an invalid std::string. Move Utf8SafePrefixLength from the OTLP recordable into sdk::common so the converter can share it, and truncate the std::string, string_view, const char*, and string-array alternatives at a UTF-8 code-point boundary. Raw bytes (span) keep the exact byte cut since they carry no encoding. The OTLP helper now forwards to the shared implementation, leaving its public surface and tests unchanged. Also fold in review nits: delegate the const char* overload to the string_view overload to avoid copying an oversized C string before truncating, drop a redundant pointer cast in the bytes overload, and use std::strlen. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: satisfy the clang-tidy warning limit A recent merge of main lowered the abiv1-preview and abiv2-preview clang-tidy ceilings to 331 and 341. Two warnings need addressing to stay within them: - Initialize LoggerContext::recordable_enforces_limits_ in the member initializer list instead of the constructor body (cppcoreguidelines-prefer-member-initializer). - Move the rvalue reference parameter in the test TrackingProcessor OnEmit stub (cppcoreguidelines-rvalue-reference-param-not-moved). Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: default recordable to no limits, inject via provider A default-constructed ReadWriteLogRecord / OtlpLogRecordable previously carried the spec-default LogRecordLimits (count 128), so a recordable used without any LoggerProvider wiring enforced the 128 cap. That changed behavior for code that constructs a recordable directly. Default the recordable to no limits and let the LoggerProvider wiring inject the configured limits through SetLogRecordLimits, which it already does only when a processor enforces them. LoggerContext keeps the spec default, so a record created through the standard provider path still gets the 128 cap (or the configured value), while a bare recordable no longer caps on its own. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: address dbarker review (comments, UTF-8 scan shortcut, test) - Correct the SetLogRecordLimits comments on the base Recordable, ReadWriteLogRecord, and OtlpLogRecordable: the limits are copied, so the caller does not need to keep the supplied object alive (the previous "must outlive" wording was stale). - Drop the vtable-append implementation-detail paragraph from the RecordableEnforcesLogRecordLimits comments on LogRecordExporter and LogRecordProcessor. - Short-circuit Utf8SafePrefixLength when the whole value fits within the budget (max_bytes >= size, including the unbounded default), so the common path skips the per-byte scan. - Add a const char* attribute value truncation test. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * [SDK] LogRecord limits: drop vtable implementation-detail comment Remove the second paragraph of the Recordable::SetLogRecordLimits doc comment (the vtable-append note) per review. It is an implementation detail and matches the paragraphs already dropped from the exporter and processor headers. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> Co-authored-by: Hyeonho Kim Co-authored-by: Doug Barker <3782873+dbarker@users.noreply.github.com> Co-authored-by: Tom Tan Co-authored-by: Marc Alff --- CHANGELOG.md | 3 + .../exporters/ostream/log_record_exporter.h | 7 + exporters/otlp/BUILD | 17 + exporters/otlp/CMakeLists.txt | 10 + .../otlp/otlp_file_log_record_exporter.h | 6 + .../otlp/otlp_grpc_log_record_exporter.h | 6 + .../otlp/otlp_http_log_record_exporter.h | 6 + .../exporters/otlp/otlp_log_recordable.h | 15 + .../otlp/otlp_populate_attribute_utils.h | 70 +++- exporters/otlp/src/otlp_log_recordable.cc | 17 +- .../otlp/src/otlp_populate_attribute_utils.cc | 84 +++-- .../otlp/test/otlp_log_recordable_test.cc | 130 +++++++ .../otlp_populate_attribute_utils_test.cc | 76 ++++ .../sdk/common/attribute_utils.h | 128 ++++++- .../sdk/logs/batch_log_record_processor.h | 5 + sdk/include/opentelemetry/sdk/logs/exporter.h | 9 + .../sdk/logs/log_record_limits.h | 48 +++ .../opentelemetry/sdk/logs/logger_context.h | 26 +- .../sdk/logs/logger_provider_factory.h | 10 + .../sdk/logs/multi_log_record_processor.h | 2 + .../opentelemetry/sdk/logs/multi_recordable.h | 7 + .../opentelemetry/sdk/logs/processor.h | 10 + .../sdk/logs/read_write_log_record.h | 21 ++ .../sdk/logs/readable_log_record.h | 8 + .../opentelemetry/sdk/logs/recordable.h | 12 + .../sdk/logs/simple_log_record_processor.h | 5 + sdk/src/configuration/sdk_builder.cc | 30 +- sdk/src/logs/logger.cc | 8 + sdk/src/logs/logger_context.cc | 25 +- sdk/src/logs/logger_provider_factory.cc | 14 + sdk/src/logs/multi_log_record_processor.cc | 12 + sdk/src/logs/multi_recordable.cc | 11 + sdk/src/logs/read_write_log_record.cc | 30 +- sdk/test/common/attribute_utils_test.cc | 87 +++++ sdk/test/logs/BUILD | 16 + sdk/test/logs/CMakeLists.txt | 1 + sdk/test/logs/log_record_limits_test.cc | 348 ++++++++++++++++++ 37 files changed, 1255 insertions(+), 65 deletions(-) create mode 100644 exporters/otlp/test/otlp_populate_attribute_utils_test.cc create mode 100644 sdk/include/opentelemetry/sdk/logs/log_record_limits.h create mode 100644 sdk/test/logs/log_record_limits_test.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0af0bf8c..bc28d693fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,9 @@ Increment the: * [CI] iwyu and clang-tidy: use install_thirdparty.sh for third-party [#4136](https://github.com/open-telemetry/opentelemetry-cpp/pull/4136) +* [SDK] LogRecord attribute limits enforcement + [#4157](https://github.com/open-telemetry/opentelemetry-cpp/pull/4157) + * [CONFIGURATION] File configuration: declarative resource detection types [#4148](https://github.com/open-telemetry/opentelemetry-cpp/pull/4148) diff --git a/exporters/ostream/include/opentelemetry/exporters/ostream/log_record_exporter.h b/exporters/ostream/include/opentelemetry/exporters/ostream/log_record_exporter.h index 840d7d5b8a..f0bd0dc39e 100644 --- a/exporters/ostream/include/opentelemetry/exporters/ostream/log_record_exporter.h +++ b/exporters/ostream/include/opentelemetry/exporters/ostream/log_record_exporter.h @@ -37,6 +37,13 @@ class OStreamLogRecordExporter final : public opentelemetry::sdk::logs::LogRecor std::unique_ptr MakeRecordable() noexcept override; + /** + * The ostream exporter uses ReadWriteLogRecord, which applies the configured + * LogRecord attribute limits, so the SDK should push the limits onto each + * record it produces. + */ + bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; } + /** * Exports a span of logs sent from the processor. */ diff --git a/exporters/otlp/BUILD b/exporters/otlp/BUILD index e28ba407b2..86b26c57be 100644 --- a/exporters/otlp/BUILD +++ b/exporters/otlp/BUILD @@ -666,6 +666,23 @@ cc_test( ], ) +cc_test( + name = "otlp_populate_attribute_utils_test", + srcs = [ + "test/otlp_populate_attribute_utils_test.cc", + ], + tags = [ + "otlp", + "test", + ], + deps = [ + ":otlp_recordable", + "//sdk/src/metrics", + "@com_github_opentelemetry_proto//:common_proto_cc", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "otlp_file_client_test", srcs = ["test/otlp_file_client_test.cc"], diff --git a/exporters/otlp/CMakeLists.txt b/exporters/otlp/CMakeLists.txt index 7cbe414785..89e229ca4a 100644 --- a/exporters/otlp/CMakeLists.txt +++ b/exporters/otlp/CMakeLists.txt @@ -892,6 +892,16 @@ if(BUILD_TESTING) TEST_PREFIX exporter.otlp. TEST_LIST otlp_log_recordable_test) + add_executable(otlp_populate_attribute_utils_test + test/otlp_populate_attribute_utils_test.cc) + target_link_libraries( + otlp_populate_attribute_utils_test ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_otlp_recordable) + gtest_add_tests( + TARGET otlp_populate_attribute_utils_test + TEST_PREFIX exporter.otlp. + TEST_LIST otlp_populate_attribute_utils_test) + add_executable(otlp_metrics_serialization_test test/otlp_metrics_serialization_test.cc) target_link_libraries( diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_file_log_record_exporter.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_file_log_record_exporter.h index 7742616351..3584a8b878 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_file_log_record_exporter.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_file_log_record_exporter.h @@ -51,6 +51,12 @@ class OtlpFileLogRecordExporter final : public opentelemetry::sdk::logs::LogReco */ std::unique_ptr MakeRecordable() noexcept override; + /** + * The OTLP recordable applies the configured LogRecord attribute limits, so + * the SDK should push the limits onto each record it produces. + */ + bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; } + /** * Exports a vector of log records to the Elasticsearch instance. Guaranteed to return after a * timeout specified from the options passed from the constructor. diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_record_exporter.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_record_exporter.h index 888c368b28..9b6dc5c115 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_record_exporter.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_record_exporter.h @@ -68,6 +68,12 @@ class OtlpGrpcLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExpo */ std::unique_ptr MakeRecordable() noexcept override; + /** + * The OTLP recordable applies the configured LogRecord attribute limits, so + * the SDK should push the limits onto each record it produces. + */ + bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; } + /** * Exports a vector of log records to the configured gRPC endpoint. Guaranteed to return after a * timeout specified from the options passed to the constructor. diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_record_exporter.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_record_exporter.h index 4259c28998..7bcc98210e 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_record_exporter.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_record_exporter.h @@ -90,6 +90,12 @@ class OtlpHttpLogRecordExporter final : public opentelemetry::sdk::logs::LogReco */ std::unique_ptr MakeRecordable() noexcept override; + /** + * The OTLP recordable applies the configured LogRecord attribute limits, so + * the SDK should push the limits onto each record it produces. + */ + bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; } + /** * Exports a vector of log records to the Elasticsearch instance. Guaranteed to return after a * timeout specified from the options passed from the constructor. diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h index efcb0d846b..1d7f774bd7 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h @@ -7,6 +7,7 @@ #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/recordable.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/version.h" @@ -96,6 +97,14 @@ class OtlpLogRecordable final : public opentelemetry::sdk::logs::Recordable void SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept override; + /** + * Apply attribute count and value length limits. Must be called before any + * SetAttribute call to take effect. The limits are copied into this + * recordable. + */ + void SetLogRecordLimits( + const opentelemetry::sdk::logs::LogRecordLimits &limits) noexcept override; + /** * Set Resource of this log * @param Resource the resource to set @@ -114,6 +123,12 @@ class OtlpLogRecordable final : public opentelemetry::sdk::logs::Recordable const opentelemetry::sdk::resource::Resource *resource_ = nullptr; const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_ = nullptr; + // Stored by value so the recordable does not depend on the limits object + // outliving the LoggerContext that supplied it. Defaults to no limits; the + // LoggerProvider wiring injects the configured limits via SetLogRecordLimits, + // so a recordable used outside a provider does not cap attributes on its own. + opentelemetry::sdk::logs::LogRecordLimits limits_ = + opentelemetry::sdk::logs::LogRecordLimits::NoLimits(); }; } // namespace otlp diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h index 02273938bf..fe4f5ac0e0 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h @@ -3,6 +3,10 @@ #pragma once +#include +#include +#include + #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/attribute_utils.h" @@ -55,23 +59,61 @@ class OtlpPopulateAttributeUtils const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope) noexcept; - static void PopulateAnyValue(opentelemetry::proto::common::v1::AnyValue *proto_value, - const opentelemetry::common::AttributeValue &value, - bool allow_bytes) noexcept; + /** + * Populate a proto AnyValue from a non-owning AttributeValue. + * When `max_length` is less than `std::numeric_limits::max()`, + * string alternatives are truncated to at most `max_length` bytes using + * UTF-8-safe truncation (Utf8SafePrefixLength) so the resulting proto + * `string_value` stays valid UTF-8 when the input was. Raw bytes + * (`span` when `allow_bytes` is true) are cut at the raw + * byte boundary since they are not UTF-8 text. Non-string alternatives + * are unaffected. + */ + static void PopulateAnyValue( + opentelemetry::proto::common::v1::AnyValue *proto_value, + const opentelemetry::common::AttributeValue &value, + bool allow_bytes, + std::size_t max_length = (std::numeric_limits::max)()) noexcept; + + static void PopulateAnyValue( + opentelemetry::proto::common::v1::AnyValue *proto_value, + const opentelemetry::sdk::common::OwnedAttributeValue &value, + bool allow_bytes, + std::size_t max_length = (std::numeric_limits::max)()) noexcept; + + static void PopulateAttribute( + opentelemetry::proto::common::v1::KeyValue *attribute, + nostd::string_view key, + const opentelemetry::common::AttributeValue &value, + bool allow_bytes, + std::size_t max_length = (std::numeric_limits::max)()) noexcept; - static void PopulateAnyValue(opentelemetry::proto::common::v1::AnyValue *proto_value, - const opentelemetry::sdk::common::OwnedAttributeValue &value, - bool allow_bytes) noexcept; + static void PopulateAttribute( + opentelemetry::proto::common::v1::KeyValue *attribute, + nostd::string_view key, + const opentelemetry::sdk::common::OwnedAttributeValue &value, + bool allow_bytes, + std::size_t max_length = (std::numeric_limits::max)()) noexcept; - static void PopulateAttribute(opentelemetry::proto::common::v1::KeyValue *attribute, - nostd::string_view key, - const opentelemetry::common::AttributeValue &value, - bool allow_bytes) noexcept; + /** + * Byte length of the longest prefix of `value` that fits within `max_bytes` + * without splitting a well-formed UTF-8 multi-byte sequence. A lead byte's + * declared length is only honored when its continuation bytes are present + * and in range (0x80-0xBF); otherwise the lead is treated as a one-byte + * unit, so malformed input degrades to plain byte truncation. The protobuf + * `string` field type requires valid UTF-8, so this utility lets callers + * truncate at a code-point boundary instead of cutting through a multi-byte + * sequence. + */ + static std::size_t Utf8SafePrefixLength(const char *data, + std::size_t size, + std::size_t max_bytes) noexcept; - static void PopulateAttribute(opentelemetry::proto::common::v1::KeyValue *attribute, - nostd::string_view key, - const opentelemetry::sdk::common::OwnedAttributeValue &value, - bool allow_bytes) noexcept; + /// Convenience overload that delegates to the pointer + size variant. + static std::size_t Utf8SafePrefixLength(const std::string &value, std::size_t max_bytes) noexcept + { + return Utf8SafePrefixLength(value.data(), value.size(), max_bytes); + } }; } // namespace otlp diff --git a/exporters/otlp/src/otlp_log_recordable.cc b/exporters/otlp/src/otlp_log_recordable.cc index 4f1877bb41..dc10abfe41 100644 --- a/exporters/otlp/src/otlp_log_recordable.cc +++ b/exporters/otlp/src/otlp_log_recordable.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "opentelemetry/exporters/otlp/otlp_log_recordable.h" +#include #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/timestamp.h" #include "opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h" @@ -9,6 +10,7 @@ #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/readable_log_record.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/trace/span_id.h" @@ -236,7 +238,20 @@ void OtlpLogRecordable::SetTraceFlags(const opentelemetry::trace::TraceFlags &tr void OtlpLogRecordable::SetAttribute(opentelemetry::nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept { - OtlpPopulateAttributeUtils::PopulateAttribute(proto_record_.add_attributes(), key, value, true); + if (static_cast(proto_record_.attributes_size()) >= limits_.attribute_count_limit) + { + proto_record_.set_dropped_attributes_count(proto_record_.dropped_attributes_count() + 1); + return; + } + + OtlpPopulateAttributeUtils::PopulateAttribute(proto_record_.add_attributes(), key, value, true, + limits_.attribute_value_length_limit); +} + +void OtlpLogRecordable::SetLogRecordLimits( + const opentelemetry::sdk::logs::LogRecordLimits &limits) noexcept +{ + limits_ = limits; } void OtlpLogRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept diff --git a/exporters/otlp/src/otlp_populate_attribute_utils.cc b/exporters/otlp/src/otlp_populate_attribute_utils.cc index 14259773db..943121e570 100644 --- a/exporters/otlp/src/otlp_populate_attribute_utils.cc +++ b/exporters/otlp/src/otlp_populate_attribute_utils.cc @@ -6,7 +6,8 @@ #endif #include -#include +#include +#include #include #include #include @@ -62,7 +63,8 @@ inline void SetUint64Value(opentelemetry::proto::common::v1::AnyValue *proto_val void OtlpPopulateAttributeUtils::PopulateAnyValue( opentelemetry::proto::common::v1::AnyValue *proto_value, const opentelemetry::common::AttributeValue &value, - bool allow_bytes) noexcept + bool allow_bytes, + std::size_t max_length) noexcept { if (nullptr == proto_value) { @@ -101,48 +103,54 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue( } else if (nostd::holds_alternative(value)) { - const char *str_value = nostd::get(value); + const char *str_value = nostd::get(value); + const std::size_t str_len = std::strlen(str_value); + const std::size_t kept_len = Utf8SafePrefixLength(str_value, str_len, max_length); #if defined(ENABLE_OTLP_UTF8_VALIDITY) - if (utf8_range::IsStructurallyValid(str_value)) + // Validity is decided by the original input, so truncation that happens + // to remove invalid bytes does not flip the proto field type. + if (utf8_range::IsStructurallyValid({str_value, str_len})) { - proto_value->set_string_value(str_value); + proto_value->set_string_value(str_value, kept_len); } else { - proto_value->set_bytes_value(str_value, strlen(str_value)); + proto_value->set_bytes_value(str_value, kept_len); } #else - proto_value->set_string_value(str_value); + proto_value->set_string_value(str_value, kept_len); #endif } else if (nostd::holds_alternative(value)) { nostd::string_view str_value = nostd::get(value); + const std::size_t kept_len = + Utf8SafePrefixLength(str_value.data(), str_value.size(), max_length); #if defined(ENABLE_OTLP_UTF8_VALIDITY) if (utf8_range::IsStructurallyValid({str_value.data(), str_value.size()})) { - proto_value->set_string_value(str_value.data(), str_value.size()); + proto_value->set_string_value(str_value.data(), kept_len); } else { - proto_value->set_bytes_value(str_value.data(), str_value.size()); + proto_value->set_bytes_value(str_value.data(), kept_len); } #else - proto_value->set_string_value(str_value.data(), str_value.size()); + proto_value->set_string_value(str_value.data(), kept_len); #endif } else if (nostd::holds_alternative>(value)) { + const auto &bytes = nostd::get>(value); if (allow_bytes) { - proto_value->set_bytes_value( - reinterpret_cast(nostd::get>(value).data()), - nostd::get>(value).size()); + const std::size_t kept_len = (std::min)(bytes.size(), max_length); + proto_value->set_bytes_value(reinterpret_cast(bytes.data()), kept_len); } else { auto array_value = proto_value->mutable_array_value(); - for (const auto &val : nostd::get>(value)) + for (const auto &val : bytes) { array_value->add_values()->set_int_value(val); } @@ -201,17 +209,18 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue( auto array_value = proto_value->mutable_array_value(); for (const auto &val : nostd::get>(value)) { + const std::size_t kept_len = Utf8SafePrefixLength(val.data(), val.size(), max_length); #if defined(ENABLE_OTLP_UTF8_VALIDITY) if (utf8_range::IsStructurallyValid({val.data(), val.size()})) { - array_value->add_values()->set_string_value(val.data(), val.size()); + array_value->add_values()->set_string_value(val.data(), kept_len); } else { - array_value->add_values()->set_bytes_value(val.data(), val.size()); + array_value->add_values()->set_bytes_value(val.data(), kept_len); } #else - array_value->add_values()->set_string_value(val.data(), val.size()); + array_value->add_values()->set_string_value(val.data(), kept_len); #endif } } @@ -220,7 +229,8 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue( void OtlpPopulateAttributeUtils::PopulateAnyValue( opentelemetry::proto::common::v1::AnyValue *proto_value, const opentelemetry::sdk::common::OwnedAttributeValue &value, - bool allow_bytes) noexcept + bool allow_bytes, + std::size_t max_length) noexcept { if (nullptr == proto_value) { @@ -262,8 +272,8 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue( if (allow_bytes) { const std::vector &byte_array = nostd::get>(value); - proto_value->set_bytes_value(reinterpret_cast(byte_array.data()), - byte_array.size()); + const std::size_t kept_len = (std::min)(byte_array.size(), max_length); + proto_value->set_bytes_value(reinterpret_cast(byte_array.data()), kept_len); } else { @@ -277,17 +287,18 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue( else if (nostd::holds_alternative(value)) { const std::string &str_value = nostd::get(value); + const std::size_t kept_len = Utf8SafePrefixLength(str_value, max_length); #if defined(ENABLE_OTLP_UTF8_VALIDITY) if (utf8_range::IsStructurallyValid(str_value)) { - proto_value->set_string_value(str_value); + proto_value->set_string_value(str_value.data(), kept_len); } else { - proto_value->set_bytes_value(str_value); + proto_value->set_bytes_value(str_value.data(), kept_len); } #else - proto_value->set_string_value(str_value); + proto_value->set_string_value(str_value.data(), kept_len); #endif } else if (nostd::holds_alternative>(value)) @@ -344,17 +355,18 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue( auto array_value = proto_value->mutable_array_value(); for (const auto &val : nostd::get>(value)) { + const std::size_t kept_len = Utf8SafePrefixLength(val, max_length); #if defined(ENABLE_OTLP_UTF8_VALIDITY) if (utf8_range::IsStructurallyValid(val)) { - array_value->add_values()->set_string_value(val); + array_value->add_values()->set_string_value(val.data(), kept_len); } else { - array_value->add_values()->set_bytes_value(val); + array_value->add_values()->set_bytes_value(val.data(), kept_len); } #else - array_value->add_values()->set_string_value(val); + array_value->add_values()->set_string_value(val.data(), kept_len); #endif } } @@ -364,7 +376,8 @@ void OtlpPopulateAttributeUtils::PopulateAttribute( opentelemetry::proto::common::v1::KeyValue *attribute, nostd::string_view key, const opentelemetry::common::AttributeValue &value, - bool allow_bytes) noexcept + bool allow_bytes, + std::size_t max_length) noexcept { if (nullptr == attribute) { @@ -378,7 +391,7 @@ void OtlpPopulateAttributeUtils::PopulateAttribute( "AttributeValue contains unknown type"); attribute->set_key(key.data(), key.size()); - PopulateAnyValue(attribute->mutable_value(), value, allow_bytes); + PopulateAnyValue(attribute->mutable_value(), value, allow_bytes, max_length); } /** Maps from C++ attribute into OTLP proto attribute. */ @@ -386,7 +399,8 @@ void OtlpPopulateAttributeUtils::PopulateAttribute( opentelemetry::proto::common::v1::KeyValue *attribute, nostd::string_view key, const opentelemetry::sdk::common::OwnedAttributeValue &value, - bool allow_bytes) noexcept + bool allow_bytes, + std::size_t max_length) noexcept { if (nullptr == attribute) { @@ -400,7 +414,7 @@ void OtlpPopulateAttributeUtils::PopulateAttribute( "OwnedAttributeValue contains unknown type"); attribute->set_key(key.data(), key.size()); - PopulateAnyValue(attribute->mutable_value(), value, allow_bytes); + PopulateAnyValue(attribute->mutable_value(), value, allow_bytes, max_length); } void OtlpPopulateAttributeUtils::PopulateAttribute( @@ -431,6 +445,16 @@ void OtlpPopulateAttributeUtils::PopulateAttribute( } } +// The UTF-8-safe prefix algorithm now lives in the SDK common layer so the +// in-memory AttributeConverter can share it. This member is kept as a thin +// forwarder for backward compatibility with existing callers and tests. +std::size_t OtlpPopulateAttributeUtils::Utf8SafePrefixLength(const char *data, + std::size_t size, + std::size_t max_bytes) noexcept +{ + return opentelemetry::sdk::common::Utf8SafePrefixLength(data, size, max_bytes); +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/test/otlp_log_recordable_test.cc b/exporters/otlp/test/otlp_log_recordable_test.cc index b722c6c055..d471c86414 100644 --- a/exporters/otlp/test/otlp_log_recordable_test.cc +++ b/exporters/otlp/test/otlp_log_recordable_test.cc @@ -19,6 +19,7 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/readable_log_record.h" #include "opentelemetry/sdk/logs/recordable.h" #include "opentelemetry/sdk/resource/resource.h" @@ -419,6 +420,135 @@ TEST(OtlpLogRecordable, PopulateRequestSameScope) EXPECT_EQ(req.resource_logs(0).scope_logs(0).log_records_size(), 2); EXPECT_EQ(req.resource_logs(0).scope_logs(0).scope().name(), "lib"); } + +TEST(OtlpLogRecordable, AttributeCountLimitReportsDroppedCount) +{ + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_count_limit = 2; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + rec.SetAttribute("a", static_cast(1)); + rec.SetAttribute("b", static_cast(2)); + rec.SetAttribute("c", static_cast(3)); + rec.SetAttribute("d", static_cast(4)); + + EXPECT_EQ(rec.log_record().attributes_size(), 2); + EXPECT_EQ(rec.log_record().dropped_attributes_count(), 2u); +} + +TEST(OtlpLogRecordable, AttributeValueLengthLimitTruncatesString) +{ + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_value_length_limit = 4; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + rec.SetAttribute("k", nostd::string_view("abcdefghij")); + + ASSERT_EQ(rec.log_record().attributes_size(), 1); + EXPECT_EQ(rec.log_record().attributes(0).value().string_value(), "abcd"); + EXPECT_EQ(rec.log_record().dropped_attributes_count(), 0u); +} + +TEST(OtlpLogRecordable, AttributeValueLengthLimitTruncatesArrayElements) +{ + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_value_length_limit = 3; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + nostd::string_view values[] = {nostd::string_view("aaaaaa"), nostd::string_view("bb"), + nostd::string_view("cccc")}; + rec.SetAttribute("k", nostd::span(values, 3)); + + ASSERT_EQ(rec.log_record().attributes_size(), 1); + const auto &array = rec.log_record().attributes(0).value().array_value(); + ASSERT_EQ(array.values_size(), 3); + EXPECT_EQ(array.values(0).string_value(), "aaa"); + EXPECT_EQ(array.values(1).string_value(), "bb"); + EXPECT_EQ(array.values(2).string_value(), "ccc"); +} + +TEST(OtlpLogRecordable, AttributeValueLengthLimitLeavesNonStringTypesUnchanged) +{ + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_value_length_limit = 1; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + rec.SetAttribute("i", static_cast(1234567890)); + rec.SetAttribute("d", 3.14); + rec.SetAttribute("b", true); + + ASSERT_EQ(rec.log_record().attributes_size(), 3); + EXPECT_EQ(rec.log_record().attributes(0).value().int_value(), 1234567890); + EXPECT_DOUBLE_EQ(rec.log_record().attributes(1).value().double_value(), 3.14); + EXPECT_EQ(rec.log_record().attributes(2).value().bool_value(), true); +} + +TEST(OtlpLogRecordable, AttributeValueLengthLimitPreservesUtf8MultiByte) +{ + // "h\xC3\xA9llo" is "héllo" in UTF-8; 'é' occupies bytes 1-2. With a 2-byte + // budget the truncator keeps 'h' alone and drops the partial 'é', so the + // resulting protobuf string_value stays valid UTF-8. + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_value_length_limit = 2; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + rec.SetAttribute("k", nostd::string_view("h\xC3\xA9llo")); + + ASSERT_EQ(rec.log_record().attributes_size(), 1); + EXPECT_EQ(rec.log_record().attributes(0).value().string_value(), "h"); +} + +TEST(OtlpLogRecordable, AttributeValueLengthLimitKeepsCompletedUtf8Sequence) +{ + // Same input as above, with a 3-byte budget that exactly fits 'h' + 'é'. + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_value_length_limit = 3; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + rec.SetAttribute("k", nostd::string_view("h\xC3\xA9llo")); + + ASSERT_EQ(rec.log_record().attributes_size(), 1); + EXPECT_EQ(rec.log_record().attributes(0).value().string_value(), "h\xC3\xA9"); +} + +TEST(OtlpLogRecordable, AttributeValueLengthLimitTruncatesBytesAttribute) +{ + opentelemetry::sdk::logs::LogRecordLimits limits; + limits.attribute_value_length_limit = 3; + + OtlpLogRecordable rec; + rec.SetLogRecordLimits(limits); + const uint8_t bytes_in[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + rec.SetAttribute("k", nostd::span(bytes_in, 5)); + + ASSERT_EQ(rec.log_record().attributes_size(), 1); + const auto &b = rec.log_record().attributes(0).value().bytes_value(); + ASSERT_EQ(b.size(), 3u); + EXPECT_EQ(static_cast(b[0]), 0x01); + EXPECT_EQ(static_cast(b[1]), 0x02); + EXPECT_EQ(static_cast(b[2]), 0x03); +} + +TEST(OtlpLogRecordable, DefaultRecordAppliesNoLimitUntilConfigured) +{ + // No SetLogRecordLimits() call: a bare recordable applies no cap. The + // spec-default count limit is injected by the LoggerProvider wiring, not by + // the recordable itself, so every attribute is kept. + OtlpLogRecordable rec; + for (int i = 0; i < 200; ++i) + { + rec.SetAttribute("attr_" + std::to_string(i), static_cast(i)); + } + EXPECT_EQ(rec.log_record().attributes_size(), 200); + EXPECT_EQ(rec.log_record().dropped_attributes_count(), 0u); +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/test/otlp_populate_attribute_utils_test.cc b/exporters/otlp/test/otlp_populate_attribute_utils_test.cc new file mode 100644 index 0000000000..ed1f5f5adb --- /dev/null +++ b/exporters/otlp/test/otlp_populate_attribute_utils_test.cc @@ -0,0 +1,76 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "opentelemetry/exporters/otlp/otlp_populate_attribute_utils.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace otlp +{ + +// --------------------------------------------------------------------------- +// Utf8SafePrefixLength +// --------------------------------------------------------------------------- +// +// "h\xC3\xA9llo" is "héllo" in UTF-8 (6 bytes). 'é' occupies bytes 1-2 as a +// 2-byte sequence (0xC3 0xA9). These tests pin the behavior at the boundary. + +TEST(Utf8SafePrefixLength, ReturnsFullSizeWhenValueShorterThanMax) +{ + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("abc", 10), 3u); +} + +TEST(Utf8SafePrefixLength, ReturnsZeroForZeroBudget) +{ + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("anything", 0), 0u); +} + +TEST(Utf8SafePrefixLength, AsciiBoundaryEqualsRawByteCount) +{ + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("0123456789", 5), 5u); +} + +TEST(Utf8SafePrefixLength, DropsPartialMultiByteSequenceAtBoundary) +{ + // limit=2: 'h' fits at byte 0; the next codepoint 'é' would span bytes 1-2, + // ending past the limit, so the algorithm stops at byte 1. + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("h\xC3\xA9llo", 2), 1u); +} + +TEST(Utf8SafePrefixLength, KeepsCompletedMultiByteSequenceWithinBudget) +{ + // limit=3: 'h' (1) + 'é' (2) = 3 bytes exactly, still valid UTF-8. + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("h\xC3\xA9llo", 3), 3u); +} + +TEST(Utf8SafePrefixLength, MalformedContinuationFallsBackToByteCount) +{ + // 0xC3 lead byte announces a 2-byte sequence, but 0x28 ('(') is not a valid + // continuation byte (not in 0x80-0xBF). The lead is treated as a one-byte + // unit, then '(' is its own one-byte unit, yielding a 2-byte prefix. + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("\xC3(abc", 2), 2u); +} + +TEST(Utf8SafePrefixLength, InvalidLeadByteCountedAsOneByte) +{ + // 0xFF is not a valid UTF-8 lead byte (>= 0xF8). The algorithm treats it as + // a one-byte unit, identical to raw byte truncation in this case. + std::string value("\xFF\xFF\xFF\xFF\xFF", 5); + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength(value, 3), 3u); +} + +TEST(Utf8SafePrefixLength, TruncatedTailLeadByteFallsBack) +{ + // String ends mid-sequence (lone 0xC3 at the tail); the algorithm should + // treat it as one byte rather than reading past the buffer. + EXPECT_EQ(OtlpPopulateAttributeUtils::Utf8SafePrefixLength("ab\xC3", 10), 3u); +} + +} // namespace otlp +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/common/attribute_utils.h b/sdk/include/opentelemetry/sdk/common/attribute_utils.h index 3010c6b9bd..c2e75a0f97 100644 --- a/sdk/include/opentelemetry/sdk/common/attribute_utils.h +++ b/sdk/include/opentelemetry/sdk/common/attribute_utils.h @@ -3,8 +3,11 @@ #pragma once +#include +#include #include #include +#include #include #include #include @@ -68,11 +71,94 @@ enum OwnedAttributeType : std::uint8_t kTypeSpanByte }; +/** + * Byte length of the longest prefix of `data[0, size)` that fits within + * `max_bytes` without splitting a well-formed UTF-8 multi-byte sequence. A lead + * byte's declared length is only honored when its continuation bytes are + * present and in range (0x80-0xBF); otherwise the lead is treated as a one-byte + * unit, so malformed input degrades to plain byte truncation. Callers that need + * the result to stay valid UTF-8 (string attributes, whose protobuf/JSON wire + * form requires it) truncate at this boundary instead of cutting through a + * multi-byte sequence. + */ +// UTF-8 lead-byte ranges used below: +// 0x00-0x7F 0xxxxxxx ASCII, 1 byte +// 0x80-0xBF 10xxxxxx continuation byte (never a valid lead) +// 0xC0-0xDF 110xxxxx lead of a 2-byte sequence +// 0xE0-0xEF 1110xxxx lead of a 3-byte sequence +// 0xF0-0xF7 11110xxx lead of a 4-byte sequence +// 0xF8-0xFF not a valid lead byte +inline std::size_t Utf8SafePrefixLength(const char *data, + std::size_t size, + std::size_t max_bytes) noexcept +{ + // When the whole value fits within the budget (including the common + // unbounded case where max_bytes is SIZE_MAX), no truncation is possible, + // so skip the per-byte scan. + if (max_bytes >= size) + { + return size; + } + std::size_t i = 0; + while (i < size) + { + const auto lead = static_cast(data[i]); + std::size_t seq = (lead < 0x80) ? 1 + : (lead < 0xC0) ? 1 + : (lead < 0xE0) ? 2 + : (lead < 0xF0) ? 3 + : (lead < 0xF8) ? 4 + : 1; + if (seq > 1) + { + if (i + seq > size) + { + seq = 1; + } + else + { + for (std::size_t k = 1; k < seq; ++k) + { + const auto continuation = static_cast(data[i + k]); + if (continuation < 0x80 || continuation > 0xBF) + { + seq = 1; + break; + } + } + } + } + if (i + seq > max_bytes) + { + break; + } + i += seq; + } + return i; +} + /** * Creates an owned copy (OwnedAttributeValue) of a non-owning AttributeValue. + * + * The default-constructed converter does not truncate. To apply a byte-length + * cap during conversion, construct with the `(std::size_t max_length)` + * overload. Only the string, string-array, and bytes alternatives are capped; + * other alternatives are unaffected. String alternatives (std::string, + * string_view, const char*, and arrays of them) are truncated at a UTF-8 + * code-point boundary via Utf8SafePrefixLength so the kept value stays valid + * UTF-8 when the input was, matching the OTel convention that std::string holds + * UTF-8 text. The bytes alternative (span) carries raw binary + * data and is cut at the exact byte boundary with no encoding semantics. */ struct AttributeConverter { + AttributeConverter() = default; + + /// Constructs a converter that truncates string and bytes alternatives to + /// at most `max_length` bytes during conversion. Other alternatives are + /// unaffected. Used by recordables enforcing attribute_value_length_limit. + explicit AttributeConverter(std::size_t max_length) noexcept : max_length_(max_length) {} + OwnedAttributeValue operator()(bool v) { return OwnedAttributeValue(v); } OwnedAttributeValue operator()(int32_t v) { return OwnedAttributeValue(v); } OwnedAttributeValue operator()(uint32_t v) { return OwnedAttributeValue(v); } @@ -81,11 +167,31 @@ struct AttributeConverter OwnedAttributeValue operator()(double v) { return OwnedAttributeValue(v); } OwnedAttributeValue operator()(nostd::string_view v) { - return OwnedAttributeValue(std::string(v)); + const std::size_t kept = + v.size() > max_length_ ? Utf8SafePrefixLength(v.data(), v.size(), max_length_) : v.size(); + return OwnedAttributeValue(std::string(v.data(), kept)); + } + OwnedAttributeValue operator()(std::string v) + { + if (v.size() > max_length_) + { + v.resize(Utf8SafePrefixLength(v.data(), v.size(), max_length_)); + } + return OwnedAttributeValue(std::move(v)); + } + OwnedAttributeValue operator()(const char *v) + { + // Delegate to the string_view overload so an oversized C string is not + // copied in full before being truncated, and so it shares the UTF-8-safe + // boundary handling. + return (*this)(nostd::string_view(v)); + } + OwnedAttributeValue operator()(nostd::span v) + { + // Raw bytes carry no encoding, so cut at the exact byte boundary. + const std::size_t kept = (std::min)(v.size(), max_length_); + return OwnedAttributeValue(std::vector(v.data(), v.data() + kept)); } - OwnedAttributeValue operator()(std::string v) { return OwnedAttributeValue(std::move(v)); } - OwnedAttributeValue operator()(const char *v) { return OwnedAttributeValue(std::string(v)); } - OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } @@ -94,7 +200,16 @@ struct AttributeConverter OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } OwnedAttributeValue operator()(nostd::span v) { - return convertSpan(v); + std::vector result; + result.reserve(v.size()); + for (const auto &sv : v) + { + const std::size_t kept = sv.size() > max_length_ + ? Utf8SafePrefixLength(sv.data(), sv.size(), max_length_) + : sv.size(); + result.emplace_back(sv.data(), kept); + } + return OwnedAttributeValue(std::move(result)); } template @@ -102,6 +217,9 @@ struct AttributeConverter { return OwnedAttributeValue(std::vector(vals.begin(), vals.end())); } + +private: + std::size_t max_length_ = (std::numeric_limits::max)(); }; /** diff --git a/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h b/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h index 1ec8504bb7..4ce2370798 100644 --- a/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/batch_log_record_processor.h @@ -109,6 +109,11 @@ class BatchLogRecordProcessor : public LogRecordProcessor bool HasEnabledFilter() const noexcept override { return false; } + bool RecordableEnforcesLogRecordLimits() const noexcept override + { + return exporter_ != nullptr && exporter_->RecordableEnforcesLogRecordLimits(); + } + /** * Class destructor which invokes the Shutdown() method. */ diff --git a/sdk/include/opentelemetry/sdk/logs/exporter.h b/sdk/include/opentelemetry/sdk/logs/exporter.h index 42d90b3305..1fae5cb85c 100644 --- a/sdk/include/opentelemetry/sdk/logs/exporter.h +++ b/sdk/include/opentelemetry/sdk/logs/exporter.h @@ -68,6 +68,15 @@ class OPENTELEMETRY_EXPORT LogRecordExporter */ virtual bool Shutdown( std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept = 0; + + /** + * Returns true when the recordable produced by MakeRecordable() enforces + * LogRecord attribute limits (count and value length). The default returns + * false so the SDK can skip the per-record SetLogRecordLimits() call for + * recordables that ignore limits. Exporters whose recordable applies the + * limits (OTLP, ostream) override this to return true. + */ + virtual bool RecordableEnforcesLogRecordLimits() const noexcept { return false; } }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/logs/log_record_limits.h b/sdk/include/opentelemetry/sdk/logs/log_record_limits.h new file mode 100644 index 0000000000..0d16bf3ccd --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/log_record_limits.h @@ -0,0 +1,48 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +/** + * LogRecordLimits carries the SDK-level attribute limits applied to every + * LogRecord emitted through a LoggerProvider. The defaults match the + * specification: at most 128 attributes per record, attribute value length + * unlimited (no truncation). + * + * See https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecord-limits. + */ +struct LogRecordLimits +{ + static constexpr std::size_t kDefaultAttributeCountLimit = 128; + static constexpr std::size_t kDefaultAttributeValueLengthLimit = + (std::numeric_limits::max)(); + + std::size_t attribute_count_limit = kDefaultAttributeCountLimit; + std::size_t attribute_value_length_limit = kDefaultAttributeValueLengthLimit; + + /** + * Limits that impose no cap. A recordable uses these until a LoggerProvider + * injects the configured limits, so a recordable created outside a provider + * does not enforce any limit on its own. + */ + static constexpr LogRecordLimits NoLimits() noexcept + { + return LogRecordLimits{(std::numeric_limits::max)(), + (std::numeric_limits::max)()}; + } +}; + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/logs/logger_context.h b/sdk/include/opentelemetry/sdk/logs/logger_context.h index 038da2d948..65d740f722 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_context.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_context.h @@ -9,6 +9,7 @@ #include "logger_config.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/version.h" @@ -43,7 +44,8 @@ class LoggerContext std::make_unique>( instrumentationscope::ScopeConfigurator::Builder( LoggerConfig::Default()) - .Build())) noexcept; + .Build()), + LogRecordLimits log_record_limits = LogRecordLimits()) noexcept; /** * Attaches a log processor to list of configured processors to this logger context. @@ -76,6 +78,21 @@ class LoggerContext const instrumentationscope::ScopeConfigurator &GetLoggerConfigurator() const noexcept; + /** + * Obtain the LogRecord limits applied by this context. + * @return The LogRecordLimits for this logger context. + */ + const LogRecordLimits &GetLogRecordLimits() const noexcept; + + /** + * Whether any configured processor produces a recordable that enforces the + * LogRecord attribute limits. Computed once at construction (and refreshed by + * AddProcessor) so the Logger hot path can skip the per-record + * SetLogRecordLimits() virtual call when no processor needs it. + * @return true if at least one processor enforces limits. + */ + bool RecordableEnforcesLogRecordLimits() const noexcept; + /** * Force all active LogProcessors to flush any buffered logs * within the given timeout. @@ -93,6 +110,13 @@ class LoggerContext std::unique_ptr processor_; std::unique_ptr> logger_configurator_; + + LogRecordLimits log_record_limits_; + + // Cached capability flag: true when at least one configured processor's + // recordable enforces the LogRecord attribute limits. Refreshed by + // AddProcessor so the Logger hot path reads a plain bool. + bool recordable_enforces_limits_{false}; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h b/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h index 6745b39cee..6edc2c2983 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h @@ -6,6 +6,7 @@ #include #include +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/processor.h" @@ -66,6 +67,15 @@ class OPENTELEMETRY_EXPORT LoggerProviderFactory const opentelemetry::sdk::resource::Resource &resource, std::unique_ptr> logger_configurator); + /** + * Create a LoggerProvider with explicit LogRecord limits. + */ + static std::unique_ptr Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> logger_configurator, + LogRecordLimits log_record_limits); + /** * Create a LoggerProvider. */ diff --git a/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h b/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h index 8158a2dbf6..b7f73ec0c1 100644 --- a/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/multi_log_record_processor.h @@ -64,6 +64,8 @@ class MultiLogRecordProcessor : public LogRecordProcessor bool HasEnabledFilter() const noexcept override; + bool RecordableEnforcesLogRecordLimits() const noexcept override; + protected: /** * Exports all log records that have not yet been exported to the configured Exporter. diff --git a/sdk/include/opentelemetry/sdk/logs/multi_recordable.h b/sdk/include/opentelemetry/sdk/logs/multi_recordable.h index fe7a8b571f..d1c2fe5451 100644 --- a/sdk/include/opentelemetry/sdk/logs/multi_recordable.h +++ b/sdk/include/opentelemetry/sdk/logs/multi_recordable.h @@ -13,6 +13,7 @@ #include "opentelemetry/common/timestamp.h" #include "opentelemetry/logs/log_record.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/recordable.h" #include "opentelemetry/sdk/resource/resource.h" @@ -107,6 +108,12 @@ class MultiRecordable final : public Recordable void SetInstrumentationScope(const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope) noexcept override; + /** + * Propagate attribute limits to every wrapped recordable. Must be called + * before any SetAttribute call. + */ + void SetLogRecordLimits(const LogRecordLimits &limits) noexcept override; + private: std::unordered_map> recordables_; }; diff --git a/sdk/include/opentelemetry/sdk/logs/processor.h b/sdk/include/opentelemetry/sdk/logs/processor.h index 557db92250..41e47d1a27 100644 --- a/sdk/include/opentelemetry/sdk/logs/processor.h +++ b/sdk/include/opentelemetry/sdk/logs/processor.h @@ -111,6 +111,16 @@ class LogRecordProcessor { return true; } + +public: + /** + * Returns true when records produced through this processor enforce LogRecord + * attribute limits (count and value length). The default returns false. + * Processors backed by an exporter delegate to the exporter; composite + * processors return true if any child does. The SDK Logger consults this once + * per context to decide whether to push limits onto each recordable. + */ + virtual bool RecordableEnforcesLogRecordLimits() const noexcept { return false; } }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h b/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h index 044285540f..ad0c6086c0 100644 --- a/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h +++ b/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h @@ -14,6 +14,7 @@ #include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/readable_log_record.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_flags.h" @@ -153,6 +154,13 @@ class ReadWriteLogRecord final : public ReadableLogRecord void SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept override; + /** + * Apply attribute count and value length limits. Must be called before any + * SetAttribute call to take effect. The limits are copied into this + * recordable. + */ + void SetLogRecordLimits(const LogRecordLimits &limits) noexcept override; + /** * Get attributes of this log. * @return the body field of this log @@ -160,6 +168,12 @@ class ReadWriteLogRecord final : public ReadableLogRecord const std::unordered_map & GetAttributes() const noexcept override; + /** + * Get the number of attributes dropped because the attribute count limit + * was reached. Truncated string values are not counted as dropped. + */ + uint32_t GetDroppedAttributesCount() const noexcept override; + /** * Get resource of this log * @return the resource of this log @@ -200,6 +214,13 @@ class ReadWriteLogRecord final : public ReadableLogRecord int64_t event_id_{0}; std::string event_name_; + // Stored by value so the recordable does not depend on the limits object + // outliving the LoggerContext that supplied it. Defaults to no limits; the + // LoggerProvider wiring injects the configured limits via SetLogRecordLimits, + // so a recordable used outside a provider does not cap attributes on its own. + LogRecordLimits limits_ = LogRecordLimits::NoLimits(); + uint32_t dropped_attributes_count_{0}; + // We do not pay for trace state when not necessary struct TraceState { diff --git a/sdk/include/opentelemetry/sdk/logs/readable_log_record.h b/sdk/include/opentelemetry/sdk/logs/readable_log_record.h index 3245717ce2..b612bcc51d 100644 --- a/sdk/include/opentelemetry/sdk/logs/readable_log_record.h +++ b/sdk/include/opentelemetry/sdk/logs/readable_log_record.h @@ -114,6 +114,14 @@ class ReadableLogRecord : public Recordable virtual const std::unordered_map & GetAttributes() const noexcept = 0; + /** + * Get the number of attributes dropped because the attribute count limit + * was reached. The default implementation reports zero so existing + * recordables that do not enforce limits compile without changes. + * @return the number of dropped attributes + */ + virtual uint32_t GetDroppedAttributesCount() const noexcept { return 0; } + /** * Get resource of this log * @return the resource of this log diff --git a/sdk/include/opentelemetry/sdk/logs/recordable.h b/sdk/include/opentelemetry/sdk/logs/recordable.h index ea203fb8a4..d7ddb3b34a 100644 --- a/sdk/include/opentelemetry/sdk/logs/recordable.h +++ b/sdk/include/opentelemetry/sdk/logs/recordable.h @@ -21,6 +21,9 @@ class InstrumentationScope; namespace logs { + +struct LogRecordLimits; + /** * Maintains a representation of a log in a format that can be processed by a recorder. * @@ -43,6 +46,15 @@ class Recordable : public opentelemetry::logs::LogRecord virtual void SetInstrumentationScope( const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope) noexcept = 0; + + /** + * Apply attribute count and value length limits to this log. The default + * implementation is a no-op; concrete recordables that wish to enforce + * limits override this and copy the supplied limits before any + * SetAttribute call is observed, so the caller does not need to keep the + * supplied object alive. + */ + virtual void SetLogRecordLimits(const LogRecordLimits & /* limits */) noexcept {} }; } // namespace logs diff --git a/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h b/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h index 94b0bb7bdd..42f1b1b7e4 100644 --- a/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/simple_log_record_processor.h @@ -51,6 +51,11 @@ class SimpleLogRecordProcessor : public LogRecordProcessor bool HasEnabledFilter() const noexcept override { return false; } + bool RecordableEnforcesLogRecordLimits() const noexcept override + { + return exporter_ != nullptr && exporter_->RecordableEnforcesLogRecordLimits(); + } + bool IsShutdown() const noexcept; private: diff --git a/sdk/src/configuration/sdk_builder.cc b/sdk/src/configuration/sdk_builder.cc index f60362fa9e..98f5fde13b 100644 --- a/sdk/src/configuration/sdk_builder.cc +++ b/sdk/src/configuration/sdk_builder.cc @@ -64,6 +64,7 @@ #include "opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h" #include "opentelemetry/sdk/configuration/log_record_exporter_configuration.h" #include "opentelemetry/sdk/configuration/log_record_exporter_configuration_visitor.h" +#include "opentelemetry/sdk/configuration/log_record_limits_configuration.h" #include "opentelemetry/sdk/configuration/log_record_processor_configuration.h" #include "opentelemetry/sdk/configuration/log_record_processor_configuration_visitor.h" #include "opentelemetry/sdk/configuration/logger_config_configuration.h" @@ -133,6 +134,7 @@ #include "opentelemetry/sdk/logs/batch_log_record_processor_factory.h" #include "opentelemetry/sdk/logs/batch_log_record_processor_options.h" #include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/logger_provider_factory.h" @@ -1907,20 +1909,34 @@ std::unique_ptr SdkBuilder::CreateLogg sdk_processors.push_back(CreateLogRecordProcessor(processor_model)); } - // FIXME-SDK: https://github.com/open-telemetry/opentelemetry-cpp/issues/3303 - // FIXME-SDK: use limits + opentelemetry::sdk::logs::LogRecordLimits log_record_limits; + if (model->limits) + { + log_record_limits.attribute_value_length_limit = model->limits->attribute_value_length_limit; + log_record_limits.attribute_count_limit = model->limits->attribute_count_limit; + } + + std::unique_ptr> + logger_configurator; if (model->logger_configurator) { - auto logger_configurator = CreateLoggerConfigurator(model->logger_configurator); - sdk = opentelemetry::sdk::logs::LoggerProviderFactory::Create( - std::move(sdk_processors), resource, std::move(logger_configurator)); + logger_configurator = CreateLoggerConfigurator(model->logger_configurator); } else { - sdk = opentelemetry::sdk::logs::LoggerProviderFactory::Create(std::move(sdk_processors), - resource); + logger_configurator = + std::make_unique>( + opentelemetry::sdk::instrumentationscope:: + ScopeConfigurator::Builder( + opentelemetry::sdk::logs::LoggerConfig::Default()) + .Build()); } + sdk = opentelemetry::sdk::logs::LoggerProviderFactory::Create( + std::move(sdk_processors), resource, std::move(logger_configurator), log_record_limits); + return sdk; } diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index bdef3d445d..8f10f3a0b9 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -132,6 +132,10 @@ opentelemetry::nostd::unique_ptr Logger::CreateL auto recordable = context_->GetProcessor().MakeRecordable(); + if (context_->RecordableEnforcesLogRecordLimits()) + { + recordable->SetLogRecordLimits(context_->GetLogRecordLimits()); + } recordable->SetObservedTimestamp(std::chrono::system_clock::now()); StampSpanContextFromVariant( @@ -154,6 +158,10 @@ opentelemetry::nostd::unique_ptr Logger::CreateL auto recordable = context_->GetProcessor().MakeRecordable(); + if (context_->RecordableEnforcesLogRecordLimits()) + { + recordable->SetLogRecordLimits(context_->GetLogRecordLimits()); + } recordable->SetObservedTimestamp(std::chrono::system_clock::now()); StampSpanContextFromVariant(context_or_span, *recordable); diff --git a/sdk/src/logs/logger_context.cc b/sdk/src/logs/logger_context.cc index f7ad9db0c6..a1e8defbfa 100644 --- a/sdk/src/logs/logger_context.cc +++ b/sdk/src/logs/logger_context.cc @@ -7,6 +7,7 @@ #include #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/multi_log_record_processor.h" @@ -20,20 +21,24 @@ namespace sdk namespace logs { -LoggerContext::LoggerContext(std::vector> &&processors, - const opentelemetry::sdk::resource::Resource &resource, - std::unique_ptr> - logger_configurator) noexcept +LoggerContext::LoggerContext( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> logger_configurator, + LogRecordLimits log_record_limits) noexcept : resource_(resource), processor_( std::unique_ptr(new MultiLogRecordProcessor(std::move(processors)))), - logger_configurator_(std::move(logger_configurator)) + logger_configurator_(std::move(logger_configurator)), + log_record_limits_(log_record_limits), + recordable_enforces_limits_(processor_->RecordableEnforcesLogRecordLimits()) {} void LoggerContext::AddProcessor(std::unique_ptr processor) noexcept { auto multi_processor = static_cast(processor_.get()); multi_processor->AddProcessor(std::move(processor)); + recordable_enforces_limits_ = processor_->RecordableEnforcesLogRecordLimits(); } LogRecordProcessor &LoggerContext::GetProcessor() const noexcept @@ -52,6 +57,16 @@ const instrumentationscope::ScopeConfigurator &LoggerContext::GetL return *logger_configurator_; } +const LogRecordLimits &LoggerContext::GetLogRecordLimits() const noexcept +{ + return log_record_limits_; +} + +bool LoggerContext::RecordableEnforcesLogRecordLimits() const noexcept +{ + return recordable_enforces_limits_; +} + bool LoggerContext::ForceFlush(std::chrono::microseconds timeout) noexcept { return processor_->ForceFlush(timeout); diff --git a/sdk/src/logs/logger_provider_factory.cc b/sdk/src/logs/logger_provider_factory.cc index f80dd43b5d..e8056f94d9 100644 --- a/sdk/src/logs/logger_provider_factory.cc +++ b/sdk/src/logs/logger_provider_factory.cc @@ -6,6 +6,7 @@ #include #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/logger_provider.h" @@ -76,6 +77,19 @@ std::unique_ptr LoggerProviderFactory: return provider; } +std::unique_ptr LoggerProviderFactory::Create( + std::vector> &&processors, + const resource::Resource &resource, + std::unique_ptr> logger_configurator, + LogRecordLimits log_record_limits) +{ + auto context = std::make_unique(std::move(processors), resource, + std::move(logger_configurator), log_record_limits); + std::unique_ptr provider( + new LoggerProvider(std::move(context))); + return provider; +} + std::unique_ptr LoggerProviderFactory::Create( std::unique_ptr context) { diff --git a/sdk/src/logs/multi_log_record_processor.cc b/sdk/src/logs/multi_log_record_processor.cc index 17826bcf43..8ebb004213 100644 --- a/sdk/src/logs/multi_log_record_processor.cc +++ b/sdk/src/logs/multi_log_record_processor.cc @@ -110,6 +110,18 @@ bool MultiLogRecordProcessor::HasEnabledFilter() const noexcept return false; } +bool MultiLogRecordProcessor::RecordableEnforcesLogRecordLimits() const noexcept +{ + for (const auto &processor : processors_) + { + if (processor != nullptr && processor->RecordableEnforcesLogRecordLimits()) + { + return true; + } + } + return false; +} + bool MultiLogRecordProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept { return InternalForceFlush(timeout); diff --git a/sdk/src/logs/multi_recordable.cc b/sdk/src/logs/multi_recordable.cc index 1f4f1b8311..f63ea6ce41 100644 --- a/sdk/src/logs/multi_recordable.cc +++ b/sdk/src/logs/multi_recordable.cc @@ -184,6 +184,17 @@ void MultiRecordable::SetInstrumentationScope( } } +void MultiRecordable::SetLogRecordLimits(const LogRecordLimits &limits) noexcept +{ + for (auto &recordable : recordables_) + { + if (recordable.second) + { + recordable.second->SetLogRecordLimits(limits); + } + } +} + } // namespace logs } // namespace sdk diff --git a/sdk/src/logs/read_write_log_record.cc b/sdk/src/logs/read_write_log_record.cc index 160fa59848..ebf46a0778 100644 --- a/sdk/src/logs/read_write_log_record.cc +++ b/sdk/src/logs/read_write_log_record.cc @@ -6,12 +6,14 @@ #include #include #include +#include #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/timestamp.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/read_write_log_record.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_flags.h" @@ -155,8 +157,32 @@ void ReadWriteLogRecord::SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept { std::string safe_key(key); - opentelemetry::sdk::common::AttributeConverter converter; - attributes_map_[safe_key] = nostd::visit(converter, value); + + auto it = attributes_map_.find(safe_key); + if (it == attributes_map_.end()) + { + if (attributes_map_.size() >= limits_.attribute_count_limit) + { + ++dropped_attributes_count_; + return; + } + opentelemetry::sdk::common::AttributeConverter converter(limits_.attribute_value_length_limit); + attributes_map_.emplace(std::move(safe_key), nostd::visit(converter, value)); + return; + } + + opentelemetry::sdk::common::AttributeConverter converter(limits_.attribute_value_length_limit); + it->second = nostd::visit(converter, value); +} + +void ReadWriteLogRecord::SetLogRecordLimits(const LogRecordLimits &limits) noexcept +{ + limits_ = limits; +} + +uint32_t ReadWriteLogRecord::GetDroppedAttributesCount() const noexcept +{ + return dropped_attributes_count_; } const std::unordered_map & diff --git a/sdk/test/common/attribute_utils_test.cc b/sdk/test/common/attribute_utils_test.cc index 466179594c..c4b6abe378 100644 --- a/sdk/test/common/attribute_utils_test.cc +++ b/sdk/test/common/attribute_utils_test.cc @@ -13,6 +13,7 @@ #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/utility.h" #include "opentelemetry/nostd/variant.h" @@ -193,3 +194,89 @@ TEST(AttributeMapTest, EqualTo) EXPECT_FALSE(attribute_map.EqualTo(kv_iterable_different_size)); EXPECT_FALSE(attribute_map.EqualTo(kv_iterable_different_all)); } + +// --------------------------------------------------------------------------- +// AttributeConverter truncation +// --------------------------------------------------------------------------- +// +// "h\xC3\xA9llo" is "héllo" in UTF-8 (6 bytes). 'é' occupies bytes 1-2 as a +// 2-byte sequence (0xC3 0xA9). String alternatives are truncated at a UTF-8 +// code-point boundary; the raw bytes alternative is cut at the exact byte. + +TEST(AttributeConverterTruncation, DefaultConverterDoesNotTruncate) +{ + opentelemetry::sdk::common::AttributeConverter converter; + auto v = converter(opentelemetry::nostd::string_view("0123456789")); + EXPECT_EQ(opentelemetry::nostd::get(v), "0123456789"); +} + +TEST(AttributeConverterTruncation, AsciiTruncatesAtByteCount) +{ + opentelemetry::sdk::common::AttributeConverter converter(5); + auto v = converter(opentelemetry::nostd::string_view("0123456789")); + EXPECT_EQ(opentelemetry::nostd::get(v), "01234"); +} + +TEST(AttributeConverterTruncation, KeepsCompleteMultiByteSequenceWithinBudget) +{ + // 'h' (1 byte) + 'é' (2 bytes) = 3 bytes exactly, still valid UTF-8. + opentelemetry::sdk::common::AttributeConverter converter(3); + auto v = converter(opentelemetry::nostd::string_view("h\xC3\xA9llo")); + EXPECT_EQ(opentelemetry::nostd::get(v), std::string("h\xC3\xA9")); +} + +TEST(AttributeConverterTruncation, DropsPartialMultiByteSequenceAtBoundary) +{ + // limit=2: 'h' fits at byte 0; 'é' would span bytes 1-2, ending past the + // limit, so the kept prefix stops at byte 1. + opentelemetry::sdk::common::AttributeConverter converter(2); + auto v = converter(opentelemetry::nostd::string_view("h\xC3\xA9llo")); + EXPECT_EQ(opentelemetry::nostd::get(v), "h"); +} + +TEST(AttributeConverterTruncation, StdStringIsTruncatedUtf8Safe) +{ + opentelemetry::sdk::common::AttributeConverter converter(2); + auto v = converter(std::string("h\xC3\xA9llo")); + EXPECT_EQ(opentelemetry::nostd::get(v), "h"); +} + +TEST(AttributeConverterTruncation, ConstCharPtrIsTruncatedUtf8Safe) +{ + opentelemetry::sdk::common::AttributeConverter converter(2); + const char *value = "h\xC3\xA9llo"; + auto v = converter(value); + EXPECT_EQ(opentelemetry::nostd::get(v), "h"); +} + +TEST(AttributeConverterTruncation, BytesAreCutAtRawByteBoundary) +{ + // Raw bytes have no encoding, so truncation is a plain byte cut even when the + // leading bytes look like a multi-byte UTF-8 sequence. + const uint8_t bytes[] = {0xC3, 0xA9, 0x01, 0x02}; + opentelemetry::sdk::common::AttributeConverter converter(1); + auto v = converter(opentelemetry::nostd::span(bytes, 4)); + const auto &stored = opentelemetry::nostd::get>(v); + ASSERT_EQ(stored.size(), 1u); + EXPECT_EQ(stored[0], 0xC3); +} + +TEST(AttributeConverterTruncation, StringArrayTruncatedPerElementUtf8Safe) +{ + opentelemetry::nostd::string_view values[] = {opentelemetry::nostd::string_view("h\xC3\xA9llo"), + opentelemetry::nostd::string_view("ab")}; + opentelemetry::sdk::common::AttributeConverter converter(2); + auto v = + converter(opentelemetry::nostd::span(values, 2)); + const auto &stored = opentelemetry::nostd::get>(v); + ASSERT_EQ(stored.size(), 2u); + EXPECT_EQ(stored[0], "h"); + EXPECT_EQ(stored[1], "ab"); +} + +TEST(AttributeConverterTruncation, ShortStringUnderLimitUnchanged) +{ + opentelemetry::sdk::common::AttributeConverter converter(100); + auto v = converter(opentelemetry::nostd::string_view("short")); + EXPECT_EQ(opentelemetry::nostd::get(v), "short"); +} diff --git a/sdk/test/logs/BUILD b/sdk/test/logs/BUILD index ccf035c163..8e2198c318 100644 --- a/sdk/test/logs/BUILD +++ b/sdk/test/logs/BUILD @@ -81,6 +81,22 @@ cc_test( ], ) +cc_test( + name = "log_record_limits_test", + srcs = [ + "log_record_limits_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//api", + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "batch_log_record_processor_test", srcs = [ diff --git a/sdk/test/logs/CMakeLists.txt b/sdk/test/logs/CMakeLists.txt index 8ef79df68f..1fcbc3bb27 100644 --- a/sdk/test/logs/CMakeLists.txt +++ b/sdk/test/logs/CMakeLists.txt @@ -7,6 +7,7 @@ foreach( logger_provider_sdk_test logger_sdk_test log_record_test + log_record_limits_test simple_log_record_processor_test batch_log_record_processor_test logger_config_test) diff --git a/sdk/test/logs/log_record_limits_test.cc b/sdk/test/logs/log_record_limits_test.cc new file mode 100644 index 0000000000..cef7958b27 --- /dev/null +++ b/sdk/test/logs/log_record_limits_test.cc @@ -0,0 +1,348 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/timestamp.h" +#include "opentelemetry/logs/log_record.h" +#include "opentelemetry/logs/logger.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/log_record_limits.h" +#include "opentelemetry/sdk/logs/logger_config.h" +#include "opentelemetry/sdk/logs/logger_provider.h" +#include "opentelemetry/sdk/logs/logger_provider_factory.h" +#include "opentelemetry/sdk/logs/processor.h" +#include "opentelemetry/sdk/logs/read_write_log_record.h" +#include "opentelemetry/sdk/logs/recordable.h" +#include "opentelemetry/sdk/resource/resource.h" + +using opentelemetry::sdk::logs::LogRecordLimits; +using opentelemetry::sdk::logs::ReadWriteLogRecord; +namespace nostd = opentelemetry::nostd; + +TEST(LogRecordLimits, DefaultRecordAppliesNoLimitUntilConfigured) +{ + // A ReadWriteLogRecord that never receives an explicit SetLogRecordLimits() + // call applies no cap. The spec-default count limit is injected by the + // LoggerProvider wiring, not by a bare recordable, so a recordable used + // outside a provider keeps every attribute (matching pre-limits behavior). + ReadWriteLogRecord record; + for (int i = 0; i < 200; ++i) + { + record.SetAttribute("attr_" + std::to_string(i), static_cast(i)); + } + ASSERT_EQ(record.GetAttributes().size(), 200u); + ASSERT_EQ(record.GetDroppedAttributesCount(), 0u); +} + +TEST(LogRecordLimits, CountLimitDropsExcessAttributes) +{ + LogRecordLimits limits; + limits.attribute_count_limit = 3; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + record.SetAttribute("k1", static_cast(1)); + record.SetAttribute("k2", static_cast(2)); + record.SetAttribute("k3", static_cast(3)); + record.SetAttribute("k4", static_cast(4)); + record.SetAttribute("k5", static_cast(5)); + + ASSERT_EQ(record.GetAttributes().size(), 3u); + ASSERT_EQ(record.GetDroppedAttributesCount(), 2u); + ASSERT_TRUE(record.GetAttributes().count("k1") == 1); + ASSERT_TRUE(record.GetAttributes().count("k2") == 1); + ASSERT_TRUE(record.GetAttributes().count("k3") == 1); + ASSERT_TRUE(record.GetAttributes().count("k4") == 0); + ASSERT_TRUE(record.GetAttributes().count("k5") == 0); +} + +TEST(LogRecordLimits, CountLimitReplaceExistingKeyDoesNotDrop) +{ + LogRecordLimits limits; + limits.attribute_count_limit = 2; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + record.SetAttribute("k1", static_cast(1)); + record.SetAttribute("k2", static_cast(2)); + // Replace existing key while at limit; must not increment dropped count. + record.SetAttribute("k1", static_cast(99)); + + ASSERT_EQ(record.GetAttributes().size(), 2u); + ASSERT_EQ(record.GetDroppedAttributesCount(), 0u); + ASSERT_EQ(nostd::get(record.GetAttributes().at("k1")), 99); +} + +TEST(LogRecordLimits, LengthLimitTruncatesString) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 5; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + record.SetAttribute("k", nostd::string_view("0123456789")); + + ASSERT_EQ(record.GetDroppedAttributesCount(), 0u); + ASSERT_EQ(nostd::get(record.GetAttributes().at("k")), "01234"); +} + +TEST(LogRecordLimits, LengthLimitTruncatesConstCharPtr) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 5; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + // A const char* attribute value goes through the AttributeConverter + // const char* overload (which delegates to the string_view path) and must + // be truncated the same way. + const char *value = "0123456789"; + record.SetAttribute("k", value); + + ASSERT_EQ(record.GetDroppedAttributesCount(), 0u); + ASSERT_EQ(nostd::get(record.GetAttributes().at("k")), "01234"); +} + +TEST(LogRecordLimits, LengthLimitTruncatesEachStringInArray) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 3; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + nostd::string_view values[] = {nostd::string_view("aaaaaa"), nostd::string_view("bb"), + nostd::string_view("cccc")}; + record.SetAttribute("k", nostd::span(values, 3)); + + const auto &stored = nostd::get>(record.GetAttributes().at("k")); + ASSERT_EQ(stored.size(), 3u); + ASSERT_EQ(stored[0], "aaa"); + ASSERT_EQ(stored[1], "bb"); + ASSERT_EQ(stored[2], "ccc"); +} + +TEST(LogRecordLimits, LengthLimitKeepsUtf8Boundary) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 2; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + // "h\xC3\xA9llo" is "héllo"; 'é' occupies bytes 1-2. A budget of 2 must drop + // the partial multi-byte sequence and keep just "h", not a split byte that + // would leave the stored value as invalid UTF-8. + record.SetAttribute("k", nostd::string_view("h\xC3\xA9llo")); + + ASSERT_EQ(nostd::get(record.GetAttributes().at("k")), "h"); +} + +TEST(LogRecordLimits, LengthLimitDoesNotAffectNonStringTypes) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 1; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + record.SetAttribute("i", static_cast(1234567890)); + record.SetAttribute("d", 3.14); + record.SetAttribute("b", true); + + ASSERT_EQ(nostd::get(record.GetAttributes().at("i")), 1234567890); + ASSERT_DOUBLE_EQ(nostd::get(record.GetAttributes().at("d")), 3.14); + ASSERT_EQ(nostd::get(record.GetAttributes().at("b")), true); +} + +TEST(LogRecordLimits, LengthLimitShorterThanValueLeavesValueAlone) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 100; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + record.SetAttribute("k", nostd::string_view("short")); + + ASSERT_EQ(nostd::get(record.GetAttributes().at("k")), "short"); +} + +TEST(LogRecordLimits, CountAndLengthCombined) +{ + LogRecordLimits limits; + limits.attribute_count_limit = 2; + limits.attribute_value_length_limit = 4; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + record.SetAttribute("k1", nostd::string_view("abcdefgh")); + record.SetAttribute("k2", nostd::string_view("12345678")); + record.SetAttribute("k3", nostd::string_view("ignored")); + + ASSERT_EQ(record.GetAttributes().size(), 2u); + ASSERT_EQ(record.GetDroppedAttributesCount(), 1u); + ASSERT_EQ(nostd::get(record.GetAttributes().at("k1")), "abcd"); + ASSERT_EQ(nostd::get(record.GetAttributes().at("k2")), "1234"); +} + +TEST(LogRecordLimits, LengthLimitTruncatesBytesAttribute) +{ + LogRecordLimits limits; + limits.attribute_value_length_limit = 3; + ReadWriteLogRecord record; + record.SetLogRecordLimits(limits); + + const uint8_t bytes_in[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + record.SetAttribute("k", nostd::span(bytes_in, 5)); + + const auto &stored = nostd::get>(record.GetAttributes().at("k")); + ASSERT_EQ(stored.size(), 3u); + ASSERT_EQ(stored[0], 0x01); + ASSERT_EQ(stored[1], 0x02); + ASSERT_EQ(stored[2], 0x03); +} + +namespace +{ + +// Minimal Recordable that records whether SetLogRecordLimits was called and +// what limits arrived. Stubs every other Recordable method. +class TrackingRecordable final : public opentelemetry::sdk::logs::Recordable +{ +public: + int set_limits_call_count = 0; + std::size_t captured_count_limit = 0; + std::size_t captured_value_length_limit = 0; + + void SetLogRecordLimits(const LogRecordLimits &limits) noexcept override + { + ++set_limits_call_count; + captured_count_limit = limits.attribute_count_limit; + captured_value_length_limit = limits.attribute_value_length_limit; + } + + void SetResource(const opentelemetry::sdk::resource::Resource &) noexcept override {} + void SetInstrumentationScope( + const opentelemetry::sdk::instrumentationscope::InstrumentationScope &) noexcept override + {} + void SetTimestamp(opentelemetry::common::SystemTimestamp) noexcept override {} + void SetObservedTimestamp(opentelemetry::common::SystemTimestamp) noexcept override {} + void SetSeverity(opentelemetry::logs::Severity) noexcept override {} + void SetBody(const opentelemetry::common::AttributeValue &) noexcept override {} + void SetEventId(int64_t, nostd::string_view) noexcept override {} + void SetTraceId(const opentelemetry::trace::TraceId &) noexcept override {} + void SetSpanId(const opentelemetry::trace::SpanId &) noexcept override {} + void SetTraceFlags(const opentelemetry::trace::TraceFlags &) noexcept override {} + void SetAttribute(nostd::string_view, + const opentelemetry::common::AttributeValue &) noexcept override + {} +}; + +// Processor that hands out TrackingRecordable instances and exposes a pointer +// to the most recently produced one for inspection. +class TrackingProcessor final : public opentelemetry::sdk::logs::LogRecordProcessor +{ +public: + explicit TrackingProcessor(bool enforces_limits = true) : enforces_limits_(enforces_limits) {} + + TrackingRecordable *last = nullptr; + + std::unique_ptr MakeRecordable() noexcept override + { + auto recordable = std::unique_ptr(new TrackingRecordable()); + last = recordable.get(); + return recordable; + } + + void OnEmit(std::unique_ptr &&record) noexcept override + { + auto dropped = std::move(record); + } + + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } + + bool RecordableEnforcesLogRecordLimits() const noexcept override { return enforces_limits_; } + +private: + bool enforces_limits_; +}; + +} // namespace + +TEST(LogRecordLimitsWiring, LoggerForwardsContextLimitsToRecordable) +{ + using opentelemetry::sdk::instrumentationscope::ScopeConfigurator; + using opentelemetry::sdk::logs::LoggerConfig; + using opentelemetry::sdk::logs::LoggerProviderFactory; + using opentelemetry::sdk::logs::LogRecordProcessor; + + LogRecordLimits limits; + limits.attribute_count_limit = 7; + limits.attribute_value_length_limit = 42; + + std::vector> processors; + // Processor advertises that its recordable enforces limits (the default), so + // the Logger pushes the context limits onto each record it creates. + auto tracking_processor = std::unique_ptr(new TrackingProcessor(true)); + auto *tracker_ptr = tracking_processor.get(); + processors.push_back(std::move(tracking_processor)); + + auto configurator = std::make_unique>( + ScopeConfigurator::Builder(LoggerConfig::Default()).Build()); + + auto provider = LoggerProviderFactory::Create(std::move(processors), + opentelemetry::sdk::resource::Resource::Create({}), + std::move(configurator), limits); + + auto logger = provider->GetLogger("wiring_test", ""); + auto record = logger->CreateLogRecord(); + + ASSERT_NE(tracker_ptr->last, nullptr); + EXPECT_EQ(tracker_ptr->last->set_limits_call_count, 1); + EXPECT_EQ(tracker_ptr->last->captured_count_limit, 7u); + EXPECT_EQ(tracker_ptr->last->captured_value_length_limit, 42u); +} + +TEST(LogRecordLimitsWiring, LoggerSkipsLimitsWhenProcessorDoesNotEnforce) +{ + using opentelemetry::sdk::instrumentationscope::ScopeConfigurator; + using opentelemetry::sdk::logs::LoggerConfig; + using opentelemetry::sdk::logs::LoggerProviderFactory; + using opentelemetry::sdk::logs::LogRecordProcessor; + + LogRecordLimits limits; + limits.attribute_count_limit = 7; + limits.attribute_value_length_limit = 42; + + std::vector> processors; + // Processor reports that its recordable ignores limits, so the Logger must + // skip the per-record SetLogRecordLimits() call on the hot path. + auto tracking_processor = std::unique_ptr(new TrackingProcessor(false)); + auto *tracker_ptr = tracking_processor.get(); + processors.push_back(std::move(tracking_processor)); + + auto configurator = std::make_unique>( + ScopeConfigurator::Builder(LoggerConfig::Default()).Build()); + + auto provider = LoggerProviderFactory::Create(std::move(processors), + opentelemetry::sdk::resource::Resource::Create({}), + std::move(configurator), limits); + + auto logger = provider->GetLogger("wiring_test", ""); + auto record = logger->CreateLogRecord(); + + ASSERT_NE(tracker_ptr->last, nullptr); + EXPECT_EQ(tracker_ptr->last->set_limits_call_count, 0); +} From 994ab0efd0e00be09b18a6edce2648cfa2a96cc2 Mon Sep 17 00:00:00 2001 From: Shreyas S Joshi <156504459+BlackPool25@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:31:18 +0530 Subject: [PATCH 11/49] [SDK] Rename ComposableTraceIdRatioBasedSampler to ComposableProbabilitySampler (#4210) --- CHANGELOG.md | 6 ++++- ...ce_id_ratio.h => composable_probability.h} | 6 ++--- sdk/src/trace/CMakeLists.txt | 2 +- ..._id_ratio.cc => composable_probability.cc} | 10 ++++----- sdk/test/trace/composable_sampler_test.cc | 22 +++++++++---------- 5 files changed, 24 insertions(+), 22 deletions(-) rename sdk/include/opentelemetry/sdk/trace/samplers/{composable_trace_id_ratio.h => composable_probability.h} (85%) rename sdk/src/trace/samplers/{composable_trace_id_ratio.cc => composable_probability.cc} (77%) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc28d693fe..895cb6d37e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,10 +115,14 @@ Increment the: [#4144](https://github.com/open-telemetry/opentelemetry-cpp/pull/4144) * [SDK] Add ComposableSampler and CompositeSampler with the consistent - probability sampling variants (AlwaysOn, AlwaysOff, TraceIdRatioBased, + probability sampling variants (AlwaysOn, AlwaysOff, ComposableProbability, ParentThreshold, RuleBased) [#4028](https://github.com/open-telemetry/opentelemetry-cpp/issues/4028) +* [SDK] Rename ComposableTraceIdRatioBasedSampler to ComposableProbabilitySampler + to align with the OpenTelemetry specification + [#4161](https://github.com/open-telemetry/opentelemetry-cpp/issues/4161) + * [BUILD] Fix protobuf build failure [#4154](https://github.com/open-telemetry/opentelemetry-cpp/pull/4154) diff --git a/sdk/include/opentelemetry/sdk/trace/samplers/composable_trace_id_ratio.h b/sdk/include/opentelemetry/sdk/trace/samplers/composable_probability.h similarity index 85% rename from sdk/include/opentelemetry/sdk/trace/samplers/composable_trace_id_ratio.h rename to sdk/include/opentelemetry/sdk/trace/samplers/composable_probability.h index fbec155def..9274670fd4 100644 --- a/sdk/include/opentelemetry/sdk/trace/samplers/composable_trace_id_ratio.h +++ b/sdk/include/opentelemetry/sdk/trace/samplers/composable_probability.h @@ -16,7 +16,7 @@ namespace trace { /** - * ComposableTraceIdRatioBasedSampler samples a configurable fraction of traces + * ComposableProbabilitySampler samples a configurable fraction of traces * using consistent probability sampling. It corresponds to the specification's * ComposableProbability sampler and the ComposableProbabilitySamplerConfiguration * config model. @@ -24,10 +24,10 @@ namespace trace * The ratio maps to a 56-bit rejection threshold. A ratio of 0 drops every span * (an unreliable, non-probabilistic intent, equivalent to ComposableAlwaysOff). */ -class ComposableTraceIdRatioBasedSampler : public ComposableSampler +class ComposableProbabilitySampler : public ComposableSampler { public: - explicit ComposableTraceIdRatioBasedSampler(double ratio); + explicit ComposableProbabilitySampler(double ratio); SamplingIntent GetSamplingIntent( const opentelemetry::trace::SpanContext &parent_context, diff --git a/sdk/src/trace/CMakeLists.txt b/sdk/src/trace/CMakeLists.txt index cc9d2fabfd..0d3e69d96b 100644 --- a/sdk/src/trace/CMakeLists.txt +++ b/sdk/src/trace/CMakeLists.txt @@ -25,7 +25,7 @@ add_library( samplers/ot_trace_state.cc samplers/composite_sampler.cc samplers/composite_sampler_factory.cc - samplers/composable_trace_id_ratio.cc + samplers/composable_probability.cc samplers/composable_parent_threshold.cc samplers/composable_rule_based.cc multi_recordable.cc diff --git a/sdk/src/trace/samplers/composable_trace_id_ratio.cc b/sdk/src/trace/samplers/composable_probability.cc similarity index 77% rename from sdk/src/trace/samplers/composable_trace_id_ratio.cc rename to sdk/src/trace/samplers/composable_probability.cc index 3e86aedd1a..be341242c4 100644 --- a/sdk/src/trace/samplers/composable_trace_id_ratio.cc +++ b/sdk/src/trace/samplers/composable_probability.cc @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include "opentelemetry/sdk/trace/samplers/composable_trace_id_ratio.h" +#include "opentelemetry/sdk/trace/samplers/composable_probability.h" #include #include @@ -18,7 +18,7 @@ namespace sdk namespace trace { -ComposableTraceIdRatioBasedSampler::ComposableTraceIdRatioBasedSampler(double ratio) +ComposableProbabilitySampler::ComposableProbabilitySampler(double ratio) { if (ratio > 1.0) { @@ -33,10 +33,10 @@ ComposableTraceIdRatioBasedSampler::ComposableTraceIdRatioBasedSampler(double ra // this to behave like ComposableAlwaysOff: a non-probabilistic drop. has_threshold_ = threshold < kMaxThreshold; threshold_ = has_threshold_ ? threshold : 0; - description_ = "ComposableTraceIdRatioBasedSampler{" + std::to_string(ratio) + "}"; + description_ = "ComposableProbabilitySampler{" + std::to_string(ratio) + "}"; } -SamplingIntent ComposableTraceIdRatioBasedSampler::GetSamplingIntent( +SamplingIntent ComposableProbabilitySampler::GetSamplingIntent( const opentelemetry::trace::SpanContext & /* parent_context */, opentelemetry::trace::TraceId /* trace_id */, nostd::string_view /* name */, @@ -51,7 +51,7 @@ SamplingIntent ComposableTraceIdRatioBasedSampler::GetSamplingIntent( return intent; } -nostd::string_view ComposableTraceIdRatioBasedSampler::GetDescription() const noexcept +nostd::string_view ComposableProbabilitySampler::GetDescription() const noexcept { return description_; } diff --git a/sdk/test/trace/composable_sampler_test.cc b/sdk/test/trace/composable_sampler_test.cc index 8c8641511f..e5885e7ab7 100644 --- a/sdk/test/trace/composable_sampler_test.cc +++ b/sdk/test/trace/composable_sampler_test.cc @@ -20,9 +20,9 @@ #include "opentelemetry/sdk/trace/samplers/composable_always_off.h" #include "opentelemetry/sdk/trace/samplers/composable_always_on.h" #include "opentelemetry/sdk/trace/samplers/composable_parent_threshold.h" +#include "opentelemetry/sdk/trace/samplers/composable_probability.h" #include "opentelemetry/sdk/trace/samplers/composable_rule_based.h" #include "opentelemetry/sdk/trace/samplers/composable_sampler.h" -#include "opentelemetry/sdk/trace/samplers/composable_trace_id_ratio.h" #include "opentelemetry/sdk/trace/samplers/composite_sampler.h" #include "opentelemetry/sdk/trace/samplers/composite_sampler_factory.h" #include "opentelemetry/sdk/trace/samplers/predicate.h" @@ -40,9 +40,9 @@ namespace common = opentelemetry::common; using opentelemetry::sdk::trace::ComposableAlwaysOffSampler; using opentelemetry::sdk::trace::ComposableAlwaysOnSampler; using opentelemetry::sdk::trace::ComposableParentThresholdSampler; +using opentelemetry::sdk::trace::ComposableProbabilitySampler; using opentelemetry::sdk::trace::ComposableRuleBasedSampler; using opentelemetry::sdk::trace::ComposableSampler; -using opentelemetry::sdk::trace::ComposableTraceIdRatioBasedSampler; using opentelemetry::sdk::trace::CompositeSampler; using opentelemetry::sdk::trace::CompositeSamplerFactory; using opentelemetry::sdk::trace::Decision; @@ -207,12 +207,11 @@ TEST(ComposableSampler, AlwaysOffDrops) TEST(ComposableSampler, TraceIdRatioBoundaries) { auto always = - CompositeSamplerFactory::Create(std::make_shared(1.0)); + CompositeSamplerFactory::Create(std::make_shared(1.0)); EXPECT_EQ(Decision::RECORD_AND_SAMPLE, Sample(*always, trace_api::SpanContext::GetInvalid(), MakeTraceId(0x00))); - auto never = - CompositeSamplerFactory::Create(std::make_shared(0.0)); + auto never = CompositeSamplerFactory::Create(std::make_shared(0.0)); EXPECT_EQ(Decision::DROP, Sample(*never, trace_api::SpanContext::GetInvalid(), MakeTraceId(0xFF))); } @@ -220,7 +219,7 @@ TEST(ComposableSampler, TraceIdRatioBoundaries) TEST(ComposableSampler, TraceIdRatioUsesTraceRandomness) { auto sampler = - CompositeSamplerFactory::Create(std::make_shared(0.5)); + CompositeSamplerFactory::Create(std::make_shared(0.5)); // Threshold for p=0.5 is 2^55, encoded as th:8. Maximum randomness keeps. opentelemetry::sdk::trace::SamplingResult kept; @@ -236,7 +235,7 @@ TEST(ComposableSampler, TraceIdRatioUsesTraceRandomness) TEST(ComposableSampler, ExplicitRandomnessOverridesTraceId) { auto sampler = - CompositeSamplerFactory::Create(std::make_shared(0.5)); + CompositeSamplerFactory::Create(std::make_shared(0.5)); // rv present and at maximum: keep regardless of the (zero) trace id bits, and // the explicit randomness must be preserved in the outgoing tracestate. @@ -315,8 +314,8 @@ TEST(ComposableSampler, RuleBasedNoMatchDrops) TEST(ComposableSampler, Descriptions) { - ComposableTraceIdRatioBasedSampler ratio(0.5); - EXPECT_EQ("ComposableTraceIdRatioBasedSampler{0.500000}", std::string(ratio.GetDescription())); + ComposableProbabilitySampler ratio(0.5); + EXPECT_EQ("ComposableProbabilitySampler{0.500000}", std::string(ratio.GetDescription())); CompositeSampler composite(std::make_shared()); EXPECT_EQ("CompositeSampler{ComposableAlwaysOnSampler}", std::string(composite.GetDescription())); @@ -365,13 +364,12 @@ TEST(ComposableSampler, DropsTrailingSubkeysOverSizeLimit) TEST(ComposableSampler, RatioClampedToValidRange) { - auto over = - CompositeSamplerFactory::Create(std::make_shared(2.0)); + auto over = CompositeSamplerFactory::Create(std::make_shared(2.0)); EXPECT_EQ(Decision::RECORD_AND_SAMPLE, Sample(*over, trace_api::SpanContext::GetInvalid(), MakeTraceId(0x00))); auto under = - CompositeSamplerFactory::Create(std::make_shared(-1.0)); + CompositeSamplerFactory::Create(std::make_shared(-1.0)); EXPECT_EQ(Decision::DROP, Sample(*under, trace_api::SpanContext::GetInvalid(), MakeTraceId(0xFF))); } From 7f977939d54e935e0e1a77737cbaa3f1edd793d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:39:26 +0200 Subject: [PATCH 12/49] Bump docker/build-push-action from 7.2.0 to 7.3.0 (#4204) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.2.0 to 7.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/f9f3042f7e2789586610d6e8b85c8f03e5195baf...53b7df96c91f9c12dcc8a07bcb9ccacbed38856a) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 7.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependencies_image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependencies_image.yml b/.github/workflows/dependencies_image.yml index 40bc4e1e6b..546ff4bd75 100644 --- a/.github/workflows/dependencies_image.yml +++ b/.github/workflows/dependencies_image.yml @@ -29,7 +29,7 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Build Image - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: builder: ${{ steps.buildx.outputs.name }} context: ci/ From a6547231c7bf172d97a4c2b1123d66672a2368f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:04:30 +0200 Subject: [PATCH 13/49] Bump docker/setup-qemu-action from 4.1.0 to 4.2.0 (#4205) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/06116385d9baf250c9f4dcb4858b16962ea869c3...96fe6ef7f33517b61c61be40b68a1882f3264fb8) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependencies_image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependencies_image.yml b/.github/workflows/dependencies_image.yml index 546ff4bd75..bf7fd6e9c5 100644 --- a/.github/workflows/dependencies_image.yml +++ b/.github/workflows/dependencies_image.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - name: Set up Docker Buildx id: buildx From bb454938cade3f7d542e0ee6506b191fd856eec7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:52:38 +0200 Subject: [PATCH 14/49] Bump docker/setup-buildx-action from 4.1.0 to 4.2.0 (#4207) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5...bb05f3f5519dd87d3ba754cc423b652a5edd6d2c) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependencies_image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependencies_image.yml b/.github/workflows/dependencies_image.yml index bf7fd6e9c5..d7388a24b8 100644 --- a/.github/workflows/dependencies_image.yml +++ b/.github/workflows/dependencies_image.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - name: Build Image uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 From cac09e53a52efacfe2cbff123231c10a99e0d285 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:52:16 +0200 Subject: [PATCH 15/49] Bump rules_cc from 0.2.20 to 0.2.21 (#4214) Bumps [rules_cc](https://github.com/bazelbuild/rules_cc) from 0.2.20 to 0.2.21. - [Release notes](https://github.com/bazelbuild/rules_cc/releases) - [Commits](https://github.com/bazelbuild/rules_cc/compare/0.2.20...0.2.21) --- updated-dependencies: - dependency-name: rules_cc dependency-version: 0.2.21 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 07e443a3c3..c4d9574873 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -19,7 +19,7 @@ bazel_dep(name = "platforms", version = "1.1.0") bazel_dep(name = "prometheus-cpp", version = "1.3.0.bcr.2", repo_name = "com_github_jupp0r_prometheus_cpp") bazel_dep(name = "protobuf", version = "32.0", repo_name = "com_google_protobuf") bazel_dep(name = "rapidyaml", version = "0.12.1") -bazel_dep(name = "rules_cc", version = "0.2.20") +bazel_dep(name = "rules_cc", version = "0.2.21") bazel_dep(name = "rules_foreign_cc", version = "0.15.1") bazel_dep(name = "rules_proto", version = "7.1.0") bazel_dep(name = "zlib", version = "1.3.2") From b6b37b27a02cc8c8fcd1cf41b6f2c95bd2118227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:06:43 +0200 Subject: [PATCH 16/49] Bump github/codeql-action/upload-sarif from 4.36.2 to 4.36.3 (#4213) Bumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a) --- updated-dependencies: - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 4d8cfca765..7bcdce7067 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -47,6 +47,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 with: sarif_file: results.sarif From c1e67de05c6de7e63f7beb6cc515f692adcb9d37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:17:41 +0200 Subject: [PATCH 17/49] Bump fossas/fossa-action from 1.9.0 to 2.0.0 (#4212) Bumps [fossas/fossa-action](https://github.com/fossas/fossa-action) from 1.9.0 to 2.0.0. - [Release notes](https://github.com/fossas/fossa-action/releases) - [Commits](https://github.com/fossas/fossa-action/compare/ff70fe9fe17cbd2040648f1c45e8ec4e4884dcf3...29693cc50323968e039056be419b32989fc5880c) --- updated-dependencies: - dependency-name: fossas/fossa-action dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/fossa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 7fd9e5ef6c..f41b0e1a20 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: fossas/fossa-action@ff70fe9fe17cbd2040648f1c45e8ec4e4884dcf3 # v1.9.0 + - uses: fossas/fossa-action@29693cc50323968e039056be419b32989fc5880c # v2.0.0 with: api-key: ${{secrets.FOSSA_API_KEY}} team: OpenTelemetry From bc0aa177b3bccf6f43b5e05b4366f05a930413d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 22:51:33 +0200 Subject: [PATCH 18/49] Bump github/codeql-action/init from 4.36.2 to 4.36.3 (#4209) * Bump github/codeql-action/init from 4.36.2 to 4.36.3 Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update .github/workflows/codeql-analysis.yml * Update .github/workflows/codeql-analysis.yml --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marc Alff --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 166f552d43..41622c46df 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: run: | sudo -E ./ci/install_thirdparty.sh --install-dir /usr/local --tags-file third_party_release --packages "ryml" - name: Initialize CodeQL - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 with: languages: cpp config: | @@ -71,4 +71,4 @@ jobs: "${GITHUB_WORKSPACE}" cmake --build . --parallel - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 From 5cd5f4ebe90f424c455f65768fb08c3dc69497b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=80=E5=90=89?= <84045975+thc1006@users.noreply.github.com> Date: Tue, 7 Jul 2026 15:18:52 +0800 Subject: [PATCH 19/49] [CODE HEALTH] Move api/test and sdk/test classes into anonymous namespace (#4217) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ api/test/nostd/shared_ptr_test.cc | 5 +++++ api/test/nostd/unique_ptr_test.cc | 5 +++++ api/test/nostd/variant_test.cc | 5 +++++ api/test/singleton/singleton_test.cc | 5 +++++ sdk/test/common/global_log_handle_test.cc | 5 +++++ sdk/test/logs/batch_log_record_processor_test.cc | 5 +++++ sdk/test/logs/log_record_test.cc | 5 +++++ sdk/test/logs/logger_config_test.cc | 5 +++++ sdk/test/metrics/attributes_hashmap_test.cc | 5 +++++ sdk/test/metrics/cardinality_limit_test.cc | 5 +++++ sdk/test/metrics/circular_buffer_counter_test.cc | 5 +++++ sdk/test/metrics/meter_config_test.cc | 5 +++++ sdk/test/metrics/metric_test_stress.cc | 5 +++++ sdk/test/metrics/multi_metric_storage_test.cc | 5 +++++ sdk/test/metrics/periodic_exporting_metric_reader_test.cc | 5 +++++ sdk/test/metrics/sum_aggregation_test.cc | 5 +++++ sdk/test/metrics/sync_metric_storage_histogram_test.cc | 5 +++++ sdk/test/resource/resource_test.cc | 5 +++++ sdk/test/trace/simple_processor_test.cc | 5 +++++ sdk/test/trace/tracer_config_test.cc | 5 +++++ 22 files changed, 105 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 4123329d86..e7b4495a44 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 309 + warning_limit: 278 - cmake_options: all-options-abiv2-preview - warning_limit: 319 + warning_limit: 288 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 895cb6d37e..1b860c68d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [CODE HEALTH] Move api/test and sdk/test classes into anonymous namespace + [#4217](https://github.com/open-telemetry/opentelemetry-cpp/pull/4217) + * [CMAKE] Fix and test WITH_API_ONLY option [#4201](https://github.com/open-telemetry/opentelemetry-cpp/pull/4201) diff --git a/api/test/nostd/shared_ptr_test.cc b/api/test/nostd/shared_ptr_test.cc index de57293e09..782eb193f4 100644 --- a/api/test/nostd/shared_ptr_test.cc +++ b/api/test/nostd/shared_ptr_test.cc @@ -13,6 +13,9 @@ using opentelemetry::nostd::shared_ptr; +namespace +{ + class A { public: @@ -237,3 +240,5 @@ TEST(SharedPtrTest, Sort) { SharedPtrTest_Sort(); } + +} // namespace diff --git a/api/test/nostd/unique_ptr_test.cc b/api/test/nostd/unique_ptr_test.cc index 8307135355..83562092cc 100644 --- a/api/test/nostd/unique_ptr_test.cc +++ b/api/test/nostd/unique_ptr_test.cc @@ -10,6 +10,9 @@ using opentelemetry::nostd::unique_ptr; +namespace +{ + class A { public: @@ -174,3 +177,5 @@ TEST(UniquePtrTest, Comparison) EXPECT_EQ(ptr3, nullptr); EXPECT_EQ(nullptr, ptr3); } + +} // namespace diff --git a/api/test/nostd/variant_test.cc b/api/test/nostd/variant_test.cc index 5ebadd6eac..220f5b4bed 100644 --- a/api/test/nostd/variant_test.cc +++ b/api/test/nostd/variant_test.cc @@ -8,6 +8,9 @@ namespace nostd = opentelemetry::nostd; +namespace +{ + class DestroyCounter { public: @@ -120,3 +123,5 @@ TEST(VariantTest, Construction) nostd::variant v{"abc"}; EXPECT_EQ(v.index(), 1); } + +} // namespace diff --git a/api/test/singleton/singleton_test.cc b/api/test/singleton/singleton_test.cc index 66722fe96f..944f0d6b83 100644 --- a/api/test/singleton/singleton_test.cc +++ b/api/test/singleton/singleton_test.cc @@ -163,6 +163,9 @@ static void reset_counts() unknown_span_count = 0; } +namespace +{ + class MyTracer : public trace::Tracer { public: @@ -458,3 +461,5 @@ TEST(SingletonTest, Uniqueness) EXPECT_EQ(span_h_f2_count, 0); EXPECT_EQ(unknown_span_count, 0); } + +} // namespace diff --git a/sdk/test/common/global_log_handle_test.cc b/sdk/test/common/global_log_handle_test.cc index 59d2c85fe4..6cbedd1392 100644 --- a/sdk/test/common/global_log_handle_test.cc +++ b/sdk/test/common/global_log_handle_test.cc @@ -10,6 +10,9 @@ #include "opentelemetry/sdk/common/attribute_utils.h" #include "opentelemetry/sdk/common/global_log_handler.h" +namespace +{ + class CustomLogHandler : public opentelemetry::sdk::common::internal_log::LogHandler { public: @@ -76,3 +79,5 @@ TEST(GlobalLogHandleTest, CustomLogHandler) opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(backup_log_handle); opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(backup_log_level); } + +} // namespace diff --git a/sdk/test/logs/batch_log_record_processor_test.cc b/sdk/test/logs/batch_log_record_processor_test.cc index 7bbbab93a1..ad868bae6f 100644 --- a/sdk/test/logs/batch_log_record_processor_test.cc +++ b/sdk/test/logs/batch_log_record_processor_test.cc @@ -36,6 +36,9 @@ using namespace opentelemetry::sdk::common; namespace nostd = opentelemetry::nostd; +namespace +{ + class MockLogRecordable final : public opentelemetry::sdk::logs::Recordable { public: @@ -474,3 +477,5 @@ TEST_F(BatchLogRecordProcessorTest, TestOptionsReadFromMultipleEnvVars) unsetenv("OTEL_BLRP_EXPORT_TIMEOUT"); unsetenv("OTEL_BLRP_MAX_EXPORT_BATCH_SIZE"); } + +} // namespace diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc index 02c0921fe5..2283fc1dc7 100644 --- a/sdk/test/logs/log_record_test.cc +++ b/sdk/test/logs/log_record_test.cc @@ -85,6 +85,9 @@ TEST(ReadWriteLogRecord, SetAndGet) ASSERT_EQ(record.GetTimestamp().time_since_epoch(), now.time_since_epoch()); } +namespace +{ + // Define a basic Logger class class TestBodyLogger : public opentelemetry::logs::Logger { @@ -343,3 +346,5 @@ TEST(LogBody, BodyConversation) } } } + +} // namespace diff --git a/sdk/test/logs/logger_config_test.cc b/sdk/test/logs/logger_config_test.cc index af9b1fdfa9..f6bdedf944 100644 --- a/sdk/test/logs/logger_config_test.cc +++ b/sdk/test/logs/logger_config_test.cc @@ -89,6 +89,9 @@ const std::array instrumentati &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, }; +namespace +{ + // Test fixture for VerifyDefaultConfiguratorBehavior class DefaultLoggerConfiguratorTestFixture : public ::testing::TestWithParam @@ -125,3 +128,5 @@ TEST(LoggerConfig, ScopeConfiguratorPreservesCustomConfig) INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, DefaultLoggerConfiguratorTestFixture, ::testing::ValuesIn(instrumentation_scopes)); + +} // namespace diff --git a/sdk/test/metrics/attributes_hashmap_test.cc b/sdk/test/metrics/attributes_hashmap_test.cc index bd243fd3f2..04ccd4fe48 100644 --- a/sdk/test/metrics/attributes_hashmap_test.cc +++ b/sdk/test/metrics/attributes_hashmap_test.cc @@ -82,6 +82,9 @@ TEST(AttributesHashMap, BasicTests) EXPECT_EQ(count, hash_map.Size()); } +namespace +{ + class MetricAttributeMapHashForCollision { public: @@ -223,3 +226,5 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) EXPECT_NE(map_copy.Get(attr), nullptr); } } + +} // namespace diff --git a/sdk/test/metrics/cardinality_limit_test.cc b/sdk/test/metrics/cardinality_limit_test.cc index 4a5e47139b..bb6fda78e4 100644 --- a/sdk/test/metrics/cardinality_limit_test.cc +++ b/sdk/test/metrics/cardinality_limit_test.cc @@ -100,6 +100,9 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) } } +namespace +{ + class WritableMetricStorageCardinalityLimitTestFixture : public ::testing::TestWithParam {}; @@ -181,3 +184,5 @@ TEST(CardinalityLimitOverflowAttribute, MatchesSpecLiteral) EXPECT_EQ(entry.first, "otel.metric.overflow"); EXPECT_EQ(nostd::get(entry.second), true); } + +} // namespace diff --git a/sdk/test/metrics/circular_buffer_counter_test.cc b/sdk/test/metrics/circular_buffer_counter_test.cc index d98077c9bc..b79289563d 100644 --- a/sdk/test/metrics/circular_buffer_counter_test.cc +++ b/sdk/test/metrics/circular_buffer_counter_test.cc @@ -10,6 +10,9 @@ using namespace opentelemetry::sdk::metrics; +namespace +{ + class AdaptingIntegerArrayTest : public testing::TestWithParam {}; @@ -149,3 +152,5 @@ TEST(AdaptingCircularBufferCounterTest, Clear) counter.Clear(); EXPECT_TRUE(counter.Empty()); } + +} // namespace diff --git a/sdk/test/metrics/meter_config_test.cc b/sdk/test/metrics/meter_config_test.cc index a13db6e3e2..bdc737063d 100644 --- a/sdk/test/metrics/meter_config_test.cc +++ b/sdk/test/metrics/meter_config_test.cc @@ -71,6 +71,9 @@ const std::array instrumentati &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, }; +namespace +{ + // Test fixture for VerifyDefaultConfiguratorBehavior class DefaultMeterConfiguratorTestFixture : public ::testing::TestWithParam @@ -91,3 +94,5 @@ TEST_P(DefaultMeterConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, DefaultMeterConfiguratorTestFixture, ::testing::ValuesIn(instrumentation_scopes)); + +} // namespace diff --git a/sdk/test/metrics/metric_test_stress.cc b/sdk/test/metrics/metric_test_stress.cc index 3673af988e..0a4ac6faac 100644 --- a/sdk/test/metrics/metric_test_stress.cc +++ b/sdk/test/metrics/metric_test_stress.cc @@ -35,6 +35,9 @@ using namespace opentelemetry; using namespace opentelemetry::sdk::instrumentationscope; using namespace opentelemetry::sdk::metrics; +namespace +{ + class MockMetricExporterForStress : public opentelemetry::sdk::metrics::PushMetricExporter { public: @@ -175,3 +178,5 @@ TEST(HistogramStress, UnsignedInt64) ASSERT_EQ(expected_count, collected_count); ASSERT_EQ(*expected_sum, collected_sum); } + +} // namespace diff --git a/sdk/test/metrics/multi_metric_storage_test.cc b/sdk/test/metrics/multi_metric_storage_test.cc index 5c1a284e57..3168f54c1d 100644 --- a/sdk/test/metrics/multi_metric_storage_test.cc +++ b/sdk/test/metrics/multi_metric_storage_test.cc @@ -14,6 +14,9 @@ using namespace opentelemetry; using namespace opentelemetry::sdk::metrics; +namespace +{ + class TestMetricStorage : public SyncWritableMetricStorage { public: @@ -62,3 +65,5 @@ TEST(MultiMetricStorageTest, BasicTests) EXPECT_EQ(static_cast(storage.get())->num_calls_long, 3); EXPECT_EQ(static_cast(storage.get())->num_calls_double, 1); } + +} // namespace diff --git a/sdk/test/metrics/periodic_exporting_metric_reader_test.cc b/sdk/test/metrics/periodic_exporting_metric_reader_test.cc index 5ac001b0a8..134e67c834 100644 --- a/sdk/test/metrics/periodic_exporting_metric_reader_test.cc +++ b/sdk/test/metrics/periodic_exporting_metric_reader_test.cc @@ -29,6 +29,9 @@ using namespace opentelemetry; using namespace opentelemetry::sdk::instrumentationscope; using namespace opentelemetry::sdk::metrics; +namespace +{ + class MockPushMetricExporter : public PushMetricExporter { public: @@ -168,3 +171,5 @@ TEST(PeriodicExportingMetricReaderOptions, UsesDefault) EXPECT_EQ(options.export_interval_millis, std::chrono::milliseconds(60000)); EXPECT_EQ(options.export_timeout_millis, std::chrono::milliseconds(30000)); } + +} // namespace diff --git a/sdk/test/metrics/sum_aggregation_test.cc b/sdk/test/metrics/sum_aggregation_test.cc index 99aa5df6d5..1a4e9c2545 100644 --- a/sdk/test/metrics/sum_aggregation_test.cc +++ b/sdk/test/metrics/sum_aggregation_test.cc @@ -414,6 +414,9 @@ TEST(CounterToSumFilterAttributesWithCardinalityLimit, Double) } } +namespace +{ + class UpDownCounterToSumFixture : public ::testing::TestWithParam {}; @@ -471,3 +474,5 @@ INSTANTIATE_TEST_SUITE_P(UpDownCounterToSum, UpDownCounterToSumFixture, ::testing::Values(true, false)); #endif + +} // namespace diff --git a/sdk/test/metrics/sync_metric_storage_histogram_test.cc b/sdk/test/metrics/sync_metric_storage_histogram_test.cc index aabef07f14..d363a6413f 100644 --- a/sdk/test/metrics/sync_metric_storage_histogram_test.cc +++ b/sdk/test/metrics/sync_metric_storage_histogram_test.cc @@ -33,6 +33,9 @@ using namespace opentelemetry::sdk::metrics; using namespace opentelemetry::common; +namespace +{ + class WritableMetricStorageHistogramTestFixture : public ::testing::TestWithParam {}; @@ -537,3 +540,5 @@ INSTANTIATE_TEST_SUITE_P(WritableMetricStorageHistogramTestBase2ExponentialDoubl WritableMetricStorageHistogramTestFixture, ::testing::Values(AggregationTemporality::kCumulative, AggregationTemporality::kDelta)); + +} // namespace diff --git a/sdk/test/resource/resource_test.cc b/sdk/test/resource/resource_test.cc index 696509f892..101a7a5a32 100644 --- a/sdk/test/resource/resource_test.cc +++ b/sdk/test/resource/resource_test.cc @@ -27,6 +27,9 @@ using namespace opentelemetry::sdk::resource; namespace nostd = opentelemetry::nostd; namespace semconv = opentelemetry::semconv; +namespace +{ + class TestResource : public Resource { public: @@ -292,3 +295,5 @@ TEST(ResourceTest, DerivedResourceDetector) EXPECT_EQ(resource.GetSchemaURL(), detector.schema_url); EXPECT_TRUE(received_attributes.find("key") != received_attributes.end()); } + +} // namespace diff --git a/sdk/test/trace/simple_processor_test.cc b/sdk/test/trace/simple_processor_test.cc index 755fd483e8..b88e89eb72 100644 --- a/sdk/test/trace/simple_processor_test.cc +++ b/sdk/test/trace/simple_processor_test.cc @@ -42,6 +42,9 @@ TEST(SimpleProcessor, ToInMemorySpanExporter) EXPECT_TRUE(processor.Shutdown()); } +namespace +{ + // An exporter that does nothing but record (and give back ) the # of times Shutdown was called. class RecordShutdownExporter final : public SpanExporter { @@ -141,3 +144,5 @@ TEST(SimpleSpanProcessor, ForceFlushFail) std::unique_ptr{new FailShutDownForceFlushExporter()}); EXPECT_EQ(false, processor.ForceFlush()); } + +} // namespace diff --git a/sdk/test/trace/tracer_config_test.cc b/sdk/test/trace/tracer_config_test.cc index 4f342ba11c..6f849bc94b 100644 --- a/sdk/test/trace/tracer_config_test.cc +++ b/sdk/test/trace/tracer_config_test.cc @@ -71,6 +71,9 @@ const std::array instrumentati &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, }; +namespace +{ + // Test fixture for VerifyDefaultConfiguratorBehavior class DefaultTracerConfiguratorTestFixture : public ::testing::TestWithParam @@ -91,3 +94,5 @@ TEST_P(DefaultTracerConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, DefaultTracerConfiguratorTestFixture, ::testing::ValuesIn(instrumentation_scopes)); + +} // namespace From 37bfc9a4c806ff33317a7d16a879902e3580d04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=80=E5=90=89?= <84045975+thc1006@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:34:36 +0800 Subject: [PATCH 20/49] [CODE HEALTH] Fix clang-tidy bugprone-unchecked-string-to-number-conversion warnings (#4216) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ examples/grpc/client.cc | 4 ++-- examples/grpc/server.cc | 4 ++-- examples/http/client.cc | 4 ++-- examples/http/server.cc | 4 ++-- .../ext/http/server/http_server.h | 18 +++++++++++++++++- .../ext/http/server/socket_tools.h | 15 ++++++++++++++- .../w3c_tracecontext_http_test_server/main.cc | 4 ++-- sdk/src/configuration/configuration_parser.cc | 1 + sdk/test/metrics/measurements_benchmark.cc | 2 +- 11 files changed, 48 insertions(+), 15 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index e7b4495a44..cb0f9a3513 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 278 + warning_limit: 269 - cmake_options: all-options-abiv2-preview - warning_limit: 288 + warning_limit: 279 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b860c68d6..d45ea6ee90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,9 @@ Increment the: * [CODE HEALTH] Fix clang-tidy bugprone-unused-local-non-trivial-variable warnings [#4202](https://github.com/open-telemetry/opentelemetry-cpp/pull/4202) +* [CODE HEALTH] Fix clang-tidy bugprone-unchecked-string-to-number-conversion warnings + [#4216](https://github.com/open-telemetry/opentelemetry-cpp/pull/4216) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/examples/grpc/client.cc b/examples/grpc/client.cc index a9e08a9125..c0987ba3f1 100644 --- a/examples/grpc/client.cc +++ b/examples/grpc/client.cc @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -124,7 +124,7 @@ int main(int argc, char **argv) uint16_t port{default_port}; if (argc > 1) { - port = static_cast(atoi(argv[1])); + port = static_cast(std::strtol(argv[1], nullptr, 10)); } RunClient(port); diff --git a/examples/grpc/server.cc b/examples/grpc/server.cc index 7bd1533316..4044282572 100644 --- a/examples/grpc/server.cc +++ b/examples/grpc/server.cc @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -122,7 +122,7 @@ int main(int argc, char **argv) uint16_t port{default_port}; if (argc > 1) { - port = static_cast(atoi(argv[1])); + port = static_cast(std::strtol(argv[1], nullptr, 10)); } RunServer(port); diff --git a/examples/http/client.cc b/examples/http/client.cc index 529d1b8c75..b919e86430 100644 --- a/examples/http/client.cc +++ b/examples/http/client.cc @@ -21,7 +21,7 @@ #include "tracer_common.h" #include -#include +#include #include #include #include @@ -106,7 +106,7 @@ int main(int argc, char *argv[]) // The port the validation service listens to can be specified via the command line. if (argc > 1) { - port = static_cast(atoi(argv[1])); + port = static_cast(std::strtol(argv[1], nullptr, 10)); } std::string url = "http://" + std::string(default_host) + ":" + std::to_string(port) + diff --git a/examples/http/server.cc b/examples/http/server.cc index 4c689830cc..3452e7a495 100644 --- a/examples/http/server.cc +++ b/examples/http/server.cc @@ -20,8 +20,8 @@ #include "opentelemetry/trace/tracer.h" #include "tracer_common.h" -#include #include +#include #include #include #include @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) // The port the validation service listens to can be specified via the command line. if (argc > 1) { - server_port = static_cast(atoi(argv[1])); + server_port = static_cast(std::strtol(argv[1], nullptr, 10)); } HttpServer http_server(server_name, server_port); diff --git a/ext/include/opentelemetry/ext/http/server/http_server.h b/ext/include/opentelemetry/ext/http/server/http_server.h index 941713f2b9..2129c2b643 100644 --- a/ext/include/opentelemetry/ext/http/server/http_server.h +++ b/ext/include/opentelemetry/ext/http/server/http_server.h @@ -3,7 +3,9 @@ #pragma once +#include #include +#include #include #include #include @@ -433,7 +435,21 @@ class HttpServer : private SocketTools::Reactor::SocketCallback auto const contentLength = conn.request.headers.find("Content-Length"); if (contentLength != conn.request.headers.end()) { - conn.contentLength = atoi(contentLength->second.c_str()); + char *lengthEnd = nullptr; + errno = 0; + auto const declared = std::strtol(contentLength->second.c_str(), &lengthEnd, 10); + // Reject a non-numeric, negative, or out-of-range Content-Length; tolerate trailing + // characters (e.g. optional whitespace) after a valid leading count. + if (errno != 0 || lengthEnd == contentLength->second.c_str() || declared < 0) + { + LOG_WARN("HttpServer: [%s] invalid Content-Length - %s", conn.request.client.c_str(), + contentLength->second.c_str()); + conn.response.code = 400; // Bad Request + conn.keepalive = false; + conn.state = Connection::Processing; + continue; + } + conn.contentLength = static_cast(declared); } else { diff --git a/ext/include/opentelemetry/ext/http/server/socket_tools.h b/ext/include/opentelemetry/ext/http/server/socket_tools.h index 5c8bd92a4e..b2e3217818 100644 --- a/ext/include/opentelemetry/ext/http/server/socket_tools.h +++ b/ext/include/opentelemetry/ext/http/server/socket_tools.h @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -196,7 +198,18 @@ struct SocketAddr char const *colon = strchr(addr, ':'); if (colon) { - inet4.sin_port = htons(atoi(colon + 1)); + char *portEnd = nullptr; + errno = 0; + auto const parsed = std::strtol(colon + 1, &portEnd, 10); + // Accept only a converted, in-range port value; fall back to 0 otherwise. + if (errno == 0 && portEnd != colon + 1 && parsed >= 0 && parsed <= 65535) + { + inet4.sin_port = htons(static_cast(parsed)); + } + else + { + inet4.sin_port = 0; + } char buf[16]; memcpy(buf, addr, (std::min)(15, colon - addr)); buf[15] = '\0'; diff --git a/ext/test/w3c_tracecontext_http_test_server/main.cc b/ext/test/w3c_tracecontext_http_test_server/main.cc index 67beaea45e..9e3f009dd0 100644 --- a/ext/test/w3c_tracecontext_http_test_server/main.cc +++ b/ext/test/w3c_tracecontext_http_test_server/main.cc @@ -3,9 +3,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -186,7 +186,7 @@ int main(int argc, char *argv[]) // The port the validation service listens to can be specified via the command line. if (argc > 1) { - port = static_cast(atoi(argv[1])); + port = static_cast(std::strtol(argv[1], nullptr, 10)); } auto root_span = get_tracer()->StartSpan(__func__); diff --git a/sdk/src/configuration/configuration_parser.cc b/sdk/src/configuration/configuration_parser.cc index 7173ce75be..5e00cbc7de 100644 --- a/sdk/src/configuration/configuration_parser.cc +++ b/sdk/src/configuration/configuration_parser.cc @@ -2700,6 +2700,7 @@ std::unique_ptr ConfigurationParser::Parse(std::unique_ptrfile_format.c_str(), "%d.%d", &major, &minor); if (count != 2) { diff --git a/sdk/test/metrics/measurements_benchmark.cc b/sdk/test/metrics/measurements_benchmark.cc index b534e84813..75a5c34d14 100644 --- a/sdk/test/metrics/measurements_benchmark.cc +++ b/sdk/test/metrics/measurements_benchmark.cc @@ -62,7 +62,7 @@ size_t GetBenchmarkThreads() const char *env = std::getenv("BENCHMARK_THREADS"); if (env != nullptr && env[0] != '\0') { - int val = std::atoi(env); + int val = static_cast(std::strtol(env, nullptr, 10)); if (val > 0) { return static_cast(val); From b80d89261e1f4da2a4847b7580f621e24897cf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=80=E5=90=89?= <84045975+thc1006@users.noreply.github.com> Date: Tue, 7 Jul 2026 21:58:17 +0800 Subject: [PATCH 21/49] [CODE HEALTH] Fix clang-tidy misc-override-with-different-visibility warnings (#4215) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ .../opentelemetry/trace/propagation/http_trace_context.h | 1 + api/test/logs/logger_test.cc | 2 ++ api/test/logs/provider_test.cc | 1 + api/test/trace/provider_test.cc | 2 +- examples/configuration/custom_pull_metric_exporter.h | 2 +- ext/test/http/curl_http_test.cc | 5 +++++ sdk/include/opentelemetry/sdk/logs/logger.h | 4 +++- .../exemplar/aligned_histogram_bucket_exemplar_reservoir.h | 4 +++- sdk/include/opentelemetry/sdk/metrics/view/predicate.h | 2 ++ sdk/test/logs/logger_provider_sdk_test.cc | 1 + sdk/test/logs/logger_provider_set_test.cc | 1 + sdk/test/metrics/common.h | 2 +- sdk/test/trace/tracer_provider_set_test.cc | 2 +- 15 files changed, 28 insertions(+), 8 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index cb0f9a3513..96d4c50cfd 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 269 + warning_limit: 240 - cmake_options: all-options-abiv2-preview - warning_limit: 279 + warning_limit: 250 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index d45ea6ee90..88ded66fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -150,6 +150,9 @@ Increment the: * [CODE HEALTH] Fix clang-tidy bugprone-unchecked-string-to-number-conversion warnings [#4216](https://github.com/open-telemetry/opentelemetry-cpp/pull/4216) +* [CODE HEALTH] Fix clang-tidy misc-override-with-different-visibility warnings + [#4215](https://github.com/open-telemetry/opentelemetry-cpp/pull/4215) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/api/include/opentelemetry/trace/propagation/http_trace_context.h b/api/include/opentelemetry/trace/propagation/http_trace_context.h index 76b94284ef..f8f349d33e 100644 --- a/api/include/opentelemetry/trace/propagation/http_trace_context.h +++ b/api/include/opentelemetry/trace/propagation/http_trace_context.h @@ -191,6 +191,7 @@ class HttpTraceContext : public context::propagation::TextMapPropagator return ExtractContextFromTraceHeaders(trace_parent, trace_state); } +public: bool Fields(nostd::function_ref callback) const noexcept override { return (callback(kTraceParent) && callback(kTraceState)); diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 9f0bfcf481..932a28455c 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -263,6 +263,7 @@ TEST(Logger, EventLogMethodOverloads) // Define a basic Logger class class TestLogger : public Logger { +public: const nostd::string_view GetName() noexcept override { return "test logger"; } using Logger::CreateLogRecord; @@ -459,6 +460,7 @@ class EnablementAwareTestLogger : public Logger // Define a basic LoggerProvider class that returns an instance of the logger class defined above class TestProvider : public LoggerProvider { +public: nostd::shared_ptr GetLogger(nostd::string_view /* logger_name */, nostd::string_view /* library_name */, nostd::string_view /* library_version */, diff --git a/api/test/logs/provider_test.cc b/api/test/logs/provider_test.cc index 83c66a7a07..817092b4b6 100644 --- a/api/test/logs/provider_test.cc +++ b/api/test/logs/provider_test.cc @@ -30,6 +30,7 @@ namespace nostd = opentelemetry::nostd; class TestProvider : public LoggerProvider { +public: nostd::shared_ptr GetLogger( nostd::string_view /* logger_name */, nostd::string_view /* library_name */, diff --git a/api/test/trace/provider_test.cc b/api/test/trace/provider_test.cc index 441d7f226d..9745c51191 100644 --- a/api/test/trace/provider_test.cc +++ b/api/test/trace/provider_test.cc @@ -17,7 +17,7 @@ namespace nostd = opentelemetry::nostd; class TestProvider : public TracerProvider { - +public: #if OPENTELEMETRY_ABI_VERSION_NO >= 2 nostd::shared_ptr GetTracer( nostd::string_view /* name */, diff --git a/examples/configuration/custom_pull_metric_exporter.h b/examples/configuration/custom_pull_metric_exporter.h index d7fc3b3282..5082b9bf2a 100644 --- a/examples/configuration/custom_pull_metric_exporter.h +++ b/examples/configuration/custom_pull_metric_exporter.h @@ -21,12 +21,12 @@ class CustomPullMetricExporter : public opentelemetry::sdk::metrics::MetricReade opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( opentelemetry::sdk::metrics::InstrumentType instrument_type) const noexcept override; +private: bool OnForceFlush(std::chrono::microseconds timeout) noexcept override; bool OnShutDown(std::chrono::microseconds timeout) noexcept override; void OnInitialized() noexcept override; -private: std::string comment_; }; diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 0c1c6c68a1..0807a405dc 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -72,6 +72,7 @@ class CustomEventHandler : public http_client::EventHandler class GetEventHandler : public CustomEventHandler { +public: void OnResponse(http_client::Response &response) noexcept override { ASSERT_EQ(200, response.GetStatusCode()); @@ -83,6 +84,7 @@ class GetEventHandler : public CustomEventHandler class PostEventHandler : public CustomEventHandler { +public: void OnResponse(http_client::Response &response) noexcept override { ASSERT_EQ(200, response.GetStatusCode()); @@ -120,6 +122,7 @@ class FinishInCallbackHandler : public CustomEventHandler class RetryEventHandler : public CustomEventHandler { +public: void OnResponse(http_client::Response &response) noexcept override { ASSERT_EQ(429, response.GetStatusCode()); @@ -145,6 +148,7 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe public: BasicCurlHttpTests() : is_setup_(false), is_running_(false) {} +protected: void SetUp() override { if (is_setup_.exchange(true)) @@ -173,6 +177,7 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe is_running_ = false; } +public: int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, HTTP_SERVER_NS::HttpResponse &response) override { diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index 8be05720a1..6f72587bc9 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -72,7 +72,7 @@ class Logger final : public opentelemetry::logs::Logger return GetInstrumentationScope(); } -private: +protected: bool EnabledImplementation(opentelemetry::logs::Severity severity, const opentelemetry::logs::EventId &event_id) const noexcept override; @@ -89,6 +89,8 @@ class Logger final : public opentelemetry::logs::Logger opentelemetry::logs::Severity severity, const opentelemetry::logs::EventId &event_id) const noexcept override; #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 + +private: // The name of this logger std::string logger_name_; diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/aligned_histogram_bucket_exemplar_reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/aligned_histogram_bucket_exemplar_reservoir.h index 82985b2da6..0ae31b4be3 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/aligned_histogram_bucket_exemplar_reservoir.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/aligned_histogram_bucket_exemplar_reservoir.h @@ -75,11 +75,13 @@ class AlignedHistogramBucketExemplarReservoir : public FixedSizeExemplarReservoi return static_cast(max_size); } - private: + public: void reset() override { // Do nothing } + + private: std::vector boundaries_; }; }; diff --git a/sdk/include/opentelemetry/sdk/metrics/view/predicate.h b/sdk/include/opentelemetry/sdk/metrics/view/predicate.h index b4170e9698..85cdef0ba4 100644 --- a/sdk/include/opentelemetry/sdk/metrics/view/predicate.h +++ b/sdk/include/opentelemetry/sdk/metrics/view/predicate.h @@ -75,11 +75,13 @@ class ExactPredicate : public Predicate class MatchEverythingPattern : public Predicate { +public: bool Match(opentelemetry::nostd::string_view /* str */) const noexcept override { return true; } }; class MatchNothingPattern : public Predicate { +public: bool Match(opentelemetry::nostd::string_view /* str */) const noexcept override { return false; } }; } // namespace metrics diff --git a/sdk/test/logs/logger_provider_sdk_test.cc b/sdk/test/logs/logger_provider_sdk_test.cc index e33c9d8116..8fb47d2cfe 100644 --- a/sdk/test/logs/logger_provider_sdk_test.cc +++ b/sdk/test/logs/logger_provider_sdk_test.cc @@ -242,6 +242,7 @@ class DummyLogRecordable final : public opentelemetry::sdk::logs::Recordable class DummyProcessor : public LogRecordProcessor { +public: std::unique_ptr MakeRecordable() noexcept override { return std::unique_ptr(new DummyLogRecordable()); diff --git a/sdk/test/logs/logger_provider_set_test.cc b/sdk/test/logs/logger_provider_set_test.cc index a48d26eb47..aa899bbcbd 100644 --- a/sdk/test/logs/logger_provider_set_test.cc +++ b/sdk/test/logs/logger_provider_set_test.cc @@ -30,6 +30,7 @@ namespace logs_sdk = opentelemetry::sdk::logs; class TestProvider : public LoggerProvider { +public: nostd::shared_ptr GetLogger( nostd::string_view /* logger_name */, nostd::string_view /* library_name */, diff --git a/sdk/test/metrics/common.h b/sdk/test/metrics/common.h index 7d83c03c1d..323696f847 100644 --- a/sdk/test/metrics/common.h +++ b/sdk/test/metrics/common.h @@ -42,13 +42,13 @@ class MockMetricReader : public opentelemetry::sdk::metrics::MetricReader opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( opentelemetry::sdk::metrics::InstrumentType) const noexcept override; +private: bool OnForceFlush(std::chrono::microseconds) noexcept override; bool OnShutDown(std::chrono::microseconds) noexcept override; void OnInitialized() noexcept override; -private: std::unique_ptr exporter_; }; diff --git a/sdk/test/trace/tracer_provider_set_test.cc b/sdk/test/trace/tracer_provider_set_test.cc index 395b173e93..3991582264 100644 --- a/sdk/test/trace/tracer_provider_set_test.cc +++ b/sdk/test/trace/tracer_provider_set_test.cc @@ -26,7 +26,7 @@ namespace trace_sdk = opentelemetry::sdk::trace; class TestProvider : public TracerProvider { - +public: #if OPENTELEMETRY_ABI_VERSION_NO >= 2 nostd::shared_ptr GetTracer( nostd::string_view /* name */, From 464c115faaef53506e0acd3337db66711b0ff591 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Wed, 8 Jul 2026 10:05:12 -0400 Subject: [PATCH 22/49] [BUILD] add missing link dependency for bazel (#4220) --- exporters/otlp/BUILD | 1 + exporters/prometheus/BUILD | 1 + 2 files changed, 2 insertions(+) diff --git a/exporters/otlp/BUILD b/exporters/otlp/BUILD index 86b26c57be..b01fac5674 100644 --- a/exporters/otlp/BUILD +++ b/exporters/otlp/BUILD @@ -47,6 +47,7 @@ cc_library( deps = [ ":otlp_utf8_validity", "//sdk/src/logs", + "//sdk/src/metrics", "//sdk/src/resource", "//sdk/src/trace", "@com_github_opentelemetry_proto//:logs_service_proto_cc", diff --git a/exporters/prometheus/BUILD b/exporters/prometheus/BUILD index 71d8b00031..f9165945b5 100644 --- a/exporters/prometheus/BUILD +++ b/exporters/prometheus/BUILD @@ -58,6 +58,7 @@ cc_library( deps = [ "//api", "//sdk:headers", + "//sdk/src/metrics", "@com_github_jupp0r_prometheus_cpp//core", "@com_github_jupp0r_prometheus_cpp//pull", ], From 15dd3a8bdc1d324c16d8be2d1d791f4f795fce38 Mon Sep 17 00:00:00 2001 From: Ravi Date: Wed, 8 Jul 2026 13:50:31 -0400 Subject: [PATCH 23/49] [CODE HEALTH] Fix clang-tidy `bugprone-throwing-static-initialization` warnings (#4206) --- CHANGELOG.md | 3 + .../opentelemetry/baggage/baggage_context.h | 2 +- .../common/key_value_iterable_view.h | 3 +- .../trace/span_context_kv_iterable_view.h | 2 +- .../propagation/baggage_propagator_test.cc | 22 +-- examples/configuration/main.cc | 10 +- examples/otlp/file_log_main.cc | 13 +- examples/otlp/file_main.cc | 7 +- examples/otlp/file_metric_main.cc | 7 +- examples/otlp/grpc_log_main.cc | 15 +- examples/otlp/grpc_main.cc | 7 +- examples/otlp/grpc_metric_main.cc | 8 +- examples/otlp/http_instrumented_main.cc | 19 ++- examples/otlp/http_log_main.cc | 14 +- examples/otlp/http_main.cc | 7 +- examples/otlp/http_metric_main.cc | 7 +- examples/zipkin/main.cc | 6 +- exporters/prometheus/src/exporter_utils.cc | 13 +- exporters/zipkin/src/recordable.cc | 28 ++-- .../http/client/curl/http_operation_curl.h | 6 +- functional/otlp/func_grpc_main.cc | 7 +- functional/otlp/func_http_main.cc | 7 +- .../sdk/metrics/state/attributes_hashmap.h | 26 ++-- .../sdk/metrics/state/sync_metric_storage.h | 12 +- sdk/include/opentelemetry/sdk/trace/tracer.h | 1 - .../metrics/instrument_metadata_validator.cc | 8 +- sdk/src/metrics/state/sync_metric_storage.cc | 2 +- sdk/src/trace/tracer.cc | 5 +- sdk/test/common/circular_buffer_test.cc | 11 +- sdk/test/logs/logger_config_test.cc | 94 ++++++++---- sdk/test/logs/logger_sdk_test.cc | 143 ++++++++++++------ sdk/test/metrics/attributes_hashmap_test.cc | 4 +- sdk/test/metrics/cardinality_limit_test.cc | 8 +- sdk/test/metrics/meter_config_test.cc | 90 +++++++---- sdk/test/metrics/sync_instruments_test.cc | 4 - sdk/test/trace/tracer_config_test.cc | 90 +++++++---- 36 files changed, 428 insertions(+), 283 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ded66fd1..a7c886bc47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,6 +153,9 @@ Increment the: * [CODE HEALTH] Fix clang-tidy misc-override-with-different-visibility warnings [#4215](https://github.com/open-telemetry/opentelemetry-cpp/pull/4215) +* [CODE HEALTH] Fix clang-tidy `bugprone-throwing-static-initialization` warnings + [#4206](https://github.com/open-telemetry/opentelemetry-cpp/pull/4206) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/api/include/opentelemetry/baggage/baggage_context.h b/api/include/opentelemetry/baggage/baggage_context.h index e3eedf019f..69d42040c5 100644 --- a/api/include/opentelemetry/baggage/baggage_context.h +++ b/api/include/opentelemetry/baggage/baggage_context.h @@ -13,7 +13,7 @@ OPENTELEMETRY_BEGIN_NAMESPACE namespace baggage { -static const std::string kBaggageHeader = "baggage"; +constexpr char kBaggageHeader[] = "baggage"; inline nostd::shared_ptr GetBaggage(const context::Context &context) noexcept { diff --git a/api/include/opentelemetry/common/key_value_iterable_view.h b/api/include/opentelemetry/common/key_value_iterable_view.h index 750d4207d0..c11e5b5ed7 100644 --- a/api/include/opentelemetry/common/key_value_iterable_view.h +++ b/api/include/opentelemetry/common/key_value_iterable_view.h @@ -39,7 +39,8 @@ std::false_type is_key_value_iterable_impl(...); template struct is_key_value_iterable { - static const bool value = decltype(detail::is_key_value_iterable_impl(std::declval()))::value; + static constexpr bool value = + decltype(detail::is_key_value_iterable_impl(std::declval()))::value; }; } // namespace detail diff --git a/api/include/opentelemetry/trace/span_context_kv_iterable_view.h b/api/include/opentelemetry/trace/span_context_kv_iterable_view.h index d6234ef1f3..cd014a23fb 100644 --- a/api/include/opentelemetry/trace/span_context_kv_iterable_view.h +++ b/api/include/opentelemetry/trace/span_context_kv_iterable_view.h @@ -48,7 +48,7 @@ std::false_type is_span_context_kv_iterable_impl(...); template struct is_span_context_kv_iterable { - static const bool value = + static constexpr bool value = decltype(detail::is_span_context_kv_iterable_impl(std::declval()))::value; }; } // namespace detail diff --git a/api/test/baggage/propagation/baggage_propagator_test.cc b/api/test/baggage/propagation/baggage_propagator_test.cc index fcb3a7f56d..3fc63bc4db 100644 --- a/api/test/baggage/propagation/baggage_propagator_test.cc +++ b/api/test/baggage/propagation/baggage_propagator_test.cc @@ -77,13 +77,13 @@ TEST(BaggagePropagatorTest, ExtractAndInjectBaggage) for (const auto &baggage : baggages) { BaggageCarrierTest carrier1; - carrier1.headers_[baggage::kBaggageHeader.data()] = baggage.first; - context::Context ctx1 = context::Context{}; - context::Context ctx2 = format.Extract(carrier1, ctx1); + carrier1.headers_[baggage::kBaggageHeader] = baggage.first; + context::Context ctx1 = context::Context{}; + context::Context ctx2 = format.Extract(carrier1, ctx1); BaggageCarrierTest carrier2; format.Inject(carrier2, ctx2); - EXPECT_EQ(carrier2.headers_[baggage::kBaggageHeader.data()], baggage.second); + EXPECT_EQ(carrier2.headers_[baggage::kBaggageHeader], baggage.second); std::vector fields; format.Fields([&fields](nostd::string_view field) { @@ -91,7 +91,7 @@ TEST(BaggagePropagatorTest, ExtractAndInjectBaggage) return true; }); EXPECT_EQ(fields.size(), 1); - EXPECT_EQ(fields[0], baggage::kBaggageHeader.data()); + EXPECT_EQ(fields[0], baggage::kBaggageHeader); } } @@ -106,18 +106,18 @@ TEST(BaggagePropagatorTest, InjectEmptyHeader) { // Test empty baggage in context BaggageCarrierTest carrier1; - carrier1.headers_[baggage::kBaggageHeader.data()] = ""; - context::Context ctx1 = context::Context{}; - context::Context ctx2 = format.Extract(carrier1, ctx1); + carrier1.headers_[baggage::kBaggageHeader] = ""; + context::Context ctx1 = context::Context{}; + context::Context ctx2 = format.Extract(carrier1, ctx1); format.Inject(carrier, ctx2); EXPECT_EQ(carrier.headers_.find(baggage::kBaggageHeader), carrier.headers_.end()); } { // Invalid baggage in context BaggageCarrierTest carrier1; - carrier1.headers_[baggage::kBaggageHeader.data()] = "InvalidBaggageData"; - context::Context ctx1 = context::Context{}; - context::Context ctx2 = format.Extract(carrier1, ctx1); + carrier1.headers_[baggage::kBaggageHeader] = "InvalidBaggageData"; + context::Context ctx1 = context::Context{}; + context::Context ctx2 = format.Extract(carrier1, ctx1); format.Inject(carrier, ctx2); EXPECT_EQ(carrier.headers_.find(baggage::kBaggageHeader), carrier.headers_.end()); diff --git a/examples/configuration/main.cc b/examples/configuration/main.cc index 77bd4c747e..2c37dd3a0f 100644 --- a/examples/configuration/main.cc +++ b/examples/configuration/main.cc @@ -54,11 +54,11 @@ # include "opentelemetry/exporters/prometheus/prometheus_pull_builder.h" #endif -static bool opt_help = false; -static bool opt_debug = false; -static bool opt_test = false; -static bool opt_no_registry = false; -static std::string yaml_file_path = ""; +static bool opt_help = false; +static bool opt_debug = false; +static bool opt_test = false; +static bool opt_no_registry = false; +static std::string yaml_file_path; static std::unique_ptr sdk; diff --git a/examples/otlp/file_log_main.cc b/examples/otlp/file_log_main.cc index 253bf37381..0fd20ba1a2 100644 --- a/examples/otlp/file_log_main.cc +++ b/examples/otlp/file_log_main.cc @@ -41,13 +41,10 @@ namespace trace_sdk = opentelemetry::sdk::trace; namespace { -opentelemetry::exporter::otlp::OtlpFileExporterOptions opts; -opentelemetry::exporter::otlp::OtlpFileLogRecordExporterOptions log_opts; - std::shared_ptr tracer_provider; std::shared_ptr logger_provider; -void InitTracer() +void InitTracer(const otlp::OtlpFileExporterOptions &opts) { // Create OTLP exporter instance auto exporter = otlp::OtlpFileExporterFactory::Create(opts); @@ -72,7 +69,7 @@ void CleanupTracer() trace_sdk::Provider::SetTracerProvider(none); } -void InitLogger() +void InitLogger(const otlp::OtlpFileLogRecordExporterOptions &log_opts) { // Create OTLP exporter instance auto exporter = otlp::OtlpFileLogRecordExporterFactory::Create(log_opts); @@ -99,6 +96,8 @@ void CleanupLogger() int main(int argc, char *argv[]) { + otlp::OtlpFileExporterOptions opts; + otlp::OtlpFileLogRecordExporterOptions log_opts; if (argc > 1) { opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions fs_backend; @@ -119,8 +118,8 @@ int main(int argc, char *argv[]) { opts.backend_options = std::ref(std::cout); } - InitLogger(); - InitTracer(); + InitLogger(log_opts); + InitTracer(opts); foo_library(); CleanupTracer(); CleanupLogger(); diff --git a/examples/otlp/file_main.cc b/examples/otlp/file_main.cc index 1d69f64efa..b70cec23a6 100644 --- a/examples/otlp/file_main.cc +++ b/examples/otlp/file_main.cc @@ -27,11 +27,9 @@ namespace otlp = opentelemetry::exporter::otlp; namespace { -opentelemetry::exporter::otlp::OtlpFileExporterOptions opts; - std::shared_ptr provider; -void InitTracer() +void InitTracer(const otlp::OtlpFileExporterOptions &opts) { // Create OTLP exporter instance auto exporter = otlp::OtlpFileExporterFactory::Create(opts); @@ -59,6 +57,7 @@ void CleanupTracer() int main(int argc, char *argv[]) { + otlp::OtlpFileExporterOptions opts; if (argc > 1) { opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions fs_backend; @@ -66,7 +65,7 @@ int main(int argc, char *argv[]) opts.backend_options = fs_backend; } // Removing this line will leave the default noop TracerProvider in place. - InitTracer(); + InitTracer(opts); foo_library(); diff --git a/examples/otlp/file_metric_main.cc b/examples/otlp/file_metric_main.cc index 1e484485b1..f02373e401 100644 --- a/examples/otlp/file_metric_main.cc +++ b/examples/otlp/file_metric_main.cc @@ -34,9 +34,7 @@ namespace otlp_exporter = opentelemetry::exporter::otlp; namespace { -otlp_exporter::OtlpFileMetricExporterOptions exporter_options; - -void InitMetrics() +void InitMetrics(const otlp_exporter::OtlpFileMetricExporterOptions &exporter_options) { auto exporter = otlp_exporter::OtlpFileMetricExporterFactory::Create(exporter_options); @@ -66,6 +64,7 @@ void CleanupMetrics() int main(int argc, char *argv[]) { + otlp_exporter::OtlpFileMetricExporterOptions exporter_options; std::string example_type; if (argc > 1) { @@ -78,7 +77,7 @@ int main(int argc, char *argv[]) } } // Removing this line will leave the default noop MetricProvider in place. - InitMetrics(); + InitMetrics(exporter_options); std::string name{"otlp_file_metric_example"}; if (example_type == "counter") diff --git a/examples/otlp/grpc_log_main.cc b/examples/otlp/grpc_log_main.cc index bd7d7fa894..5b8dd003c6 100644 --- a/examples/otlp/grpc_log_main.cc +++ b/examples/otlp/grpc_log_main.cc @@ -39,13 +39,11 @@ namespace trace_sdk = opentelemetry::sdk::trace; namespace { -opentelemetry::exporter::otlp::OtlpGrpcExporterOptions opts; -opentelemetry::exporter::otlp::OtlpGrpcLogRecordExporterOptions log_opts; - std::shared_ptr tracer_provider; std::shared_ptr logger_provider; -void InitTracer(const std::shared_ptr &shared_client) +void InitTracer(const otlp::OtlpGrpcExporterOptions &opts, + const std::shared_ptr &shared_client) { // Create OTLP exporter instance auto exporter = otlp::OtlpGrpcExporterFactory::Create(opts, shared_client); @@ -70,7 +68,8 @@ void CleanupTracer() trace_sdk::Provider::SetTracerProvider(none); } -void InitLogger(const std::shared_ptr &shared_client) +void InitLogger(const otlp::OtlpGrpcLogRecordExporterOptions &log_opts, + const std::shared_ptr &shared_client) { // Create OTLP exporter instance auto exporter = otlp::OtlpGrpcLogRecordExporterFactory::Create(log_opts, shared_client); @@ -98,6 +97,8 @@ void CleanupLogger() int main(int argc, char *argv[]) { + otlp::OtlpGrpcExporterOptions opts; + otlp::OtlpGrpcLogRecordExporterOptions log_opts; if (argc > 1) { opts.endpoint = argv[1]; @@ -113,8 +114,8 @@ int main(int argc, char *argv[]) std::shared_ptr shared_client = otlp::OtlpGrpcClientFactory::Create(opts); - InitLogger(shared_client); - InitTracer(shared_client); + InitLogger(log_opts, shared_client); + InitTracer(opts, shared_client); foo_library(); CleanupTracer(); CleanupLogger(); diff --git a/examples/otlp/grpc_main.cc b/examples/otlp/grpc_main.cc index 10fde09b76..fb5785c92f 100644 --- a/examples/otlp/grpc_main.cc +++ b/examples/otlp/grpc_main.cc @@ -26,11 +26,9 @@ namespace otlp = opentelemetry::exporter::otlp; namespace { -opentelemetry::exporter::otlp::OtlpGrpcExporterOptions opts; - std::shared_ptr provider; -void InitTracer() +void InitTracer(const otlp::OtlpGrpcExporterOptions &opts) { // Create OTLP exporter instance auto exporter = otlp::OtlpGrpcExporterFactory::Create(opts); @@ -58,6 +56,7 @@ void CleanupTracer() int main(int argc, char *argv[]) { + otlp::OtlpGrpcExporterOptions opts; if (argc > 1) { opts.endpoint = argv[1]; @@ -68,7 +67,7 @@ int main(int argc, char *argv[]) } } // Removing this line will leave the default noop TracerProvider in place. - InitTracer(); + InitTracer(opts); foo_library(); diff --git a/examples/otlp/grpc_metric_main.cc b/examples/otlp/grpc_metric_main.cc index 15bf9755eb..c7782ea842 100644 --- a/examples/otlp/grpc_metric_main.cc +++ b/examples/otlp/grpc_metric_main.cc @@ -42,9 +42,8 @@ namespace otlp_exporter = opentelemetry::exporter::otlp; namespace { -otlp_exporter::OtlpGrpcMetricExporterOptions exporter_options; - -void InitMetrics(std::string &name) +void InitMetrics(const otlp_exporter::OtlpGrpcMetricExporterOptions &exporter_options, + std::string &name) { auto exporter = otlp_exporter::OtlpGrpcMetricExporterFactory::Create(exporter_options); @@ -100,6 +99,7 @@ void CleanupMetrics() int main(int argc, char *argv[]) { + otlp_exporter::OtlpGrpcMetricExporterOptions exporter_options; std::string example_type; if (argc > 1) { @@ -123,7 +123,7 @@ int main(int argc, char *argv[]) std::string name{"otlp_grpc_metric_example"}; - InitMetrics(name); + InitMetrics(exporter_options, name); if (example_type == "counter") { diff --git a/examples/otlp/http_instrumented_main.cc b/examples/otlp/http_instrumented_main.cc index 7f45c44ec4..17d9da7ccc 100644 --- a/examples/otlp/http_instrumented_main.cc +++ b/examples/otlp/http_instrumented_main.cc @@ -152,15 +152,11 @@ class MyThreadInstrumentation : public opentelemetry::sdk::common::ThreadInstrum #endif /* ENABLE_THREAD_INSTRUMENTATION_PREVIEW */ -opentelemetry::exporter::otlp::OtlpHttpExporterOptions tracer_opts; -opentelemetry::exporter::otlp::OtlpHttpMetricExporterOptions meter_opts; -opentelemetry::exporter::otlp::OtlpHttpLogRecordExporterOptions logger_opts; - std::shared_ptr tracer_provider; std::shared_ptr meter_provider; std::shared_ptr logger_provider; -void InitTracer() +void InitTracer(const opentelemetry::exporter::otlp::OtlpHttpExporterOptions &tracer_opts) { // Create OTLP exporter instance opentelemetry::exporter::otlp::OtlpHttpExporterRuntimeOptions exp_rt_opts; @@ -205,7 +201,7 @@ void CleanupTracer() opentelemetry::sdk::trace::Provider::SetTracerProvider(none); } -void InitMetrics() +void InitMetrics(const opentelemetry::exporter::otlp::OtlpHttpMetricExporterOptions &meter_opts) { // Create OTLP exporter instance opentelemetry::exporter::otlp::OtlpHttpMetricExporterRuntimeOptions exp_rt_opts; @@ -254,7 +250,7 @@ void CleanupMetrics() opentelemetry::sdk::metrics::Provider::SetMeterProvider(none); } -void InitLogger() +void InitLogger(const opentelemetry::exporter::otlp::OtlpHttpLogRecordExporterOptions &logger_opts) { // Create OTLP exporter instance opentelemetry::exporter::otlp::OtlpHttpLogRecordExporterRuntimeOptions exp_rt_opts; @@ -306,6 +302,9 @@ void CleanupLogger() */ int main(int argc, char *argv[]) { + opentelemetry::exporter::otlp::OtlpHttpExporterOptions tracer_opts; + opentelemetry::exporter::otlp::OtlpHttpMetricExporterOptions meter_opts; + opentelemetry::exporter::otlp::OtlpHttpLogRecordExporterOptions logger_opts; if (argc > 1) { tracer_opts.url = argv[1]; @@ -335,9 +334,9 @@ int main(int argc, char *argv[]) std::cout << "Initializing opentelemetry-cpp\n" << std::flush; - InitTracer(); - InitMetrics(); - InitLogger(); + InitTracer(tracer_opts); + InitMetrics(meter_opts); + InitLogger(logger_opts); std::cout << "Application payload\n" << std::flush; diff --git a/examples/otlp/http_log_main.cc b/examples/otlp/http_log_main.cc index 7cc6a0bb4e..1c890cd1db 100644 --- a/examples/otlp/http_log_main.cc +++ b/examples/otlp/http_log_main.cc @@ -44,11 +44,9 @@ namespace internal_log = opentelemetry::sdk::common::internal_log; namespace { -opentelemetry::exporter::otlp::OtlpHttpExporterOptions trace_opts; - std::shared_ptr tracer_provider; -void InitTracer() +void InitTracer(otlp::OtlpHttpExporterOptions &trace_opts) { if (trace_opts.url.size() > 9) { @@ -90,11 +88,9 @@ void CleanupTracer() trace_sdk::Provider::SetTracerProvider(none); } -opentelemetry::exporter::otlp::OtlpHttpLogRecordExporterOptions logger_opts; - std::shared_ptr logger_provider; -void InitLogger() +void InitLogger(otlp::OtlpHttpLogRecordExporterOptions &logger_opts) { std::cout << "Using " << logger_opts.url << " to export log records." << '\n'; logger_opts.console_debug = true; @@ -132,6 +128,8 @@ void CleanupLogger() */ int main(int argc, char *argv[]) { + otlp::OtlpHttpExporterOptions trace_opts; + otlp::OtlpHttpLogRecordExporterOptions logger_opts; if (argc > 1) { trace_opts.url = argv[1]; @@ -158,8 +156,8 @@ int main(int argc, char *argv[]) internal_log::GlobalLogHandler::SetLogLevel(internal_log::LogLevel::Debug); } - InitLogger(); - InitTracer(); + InitLogger(logger_opts); + InitTracer(trace_opts); foo_library(); CleanupTracer(); CleanupLogger(); diff --git a/examples/otlp/http_main.cc b/examples/otlp/http_main.cc index 66b612e3e8..5922b86b6c 100644 --- a/examples/otlp/http_main.cc +++ b/examples/otlp/http_main.cc @@ -30,11 +30,9 @@ namespace internal_log = opentelemetry::sdk::common::internal_log; namespace { -opentelemetry::exporter::otlp::OtlpHttpExporterOptions opts; - std::shared_ptr provider; -void InitTracer() +void InitTracer(const otlp::OtlpHttpExporterOptions &opts) { // Create OTLP exporter instance auto exporter = otlp::OtlpHttpExporterFactory::Create(opts); @@ -70,6 +68,7 @@ void CleanupTracer() */ int main(int argc, char *argv[]) { + otlp::OtlpHttpExporterOptions opts; if (argc > 1) { opts.url = argv[1]; @@ -95,7 +94,7 @@ int main(int argc, char *argv[]) } // Removing this line will leave the default noop TracerProvider in place. - InitTracer(); + InitTracer(opts); foo_library(); diff --git a/examples/otlp/http_metric_main.cc b/examples/otlp/http_metric_main.cc index 0e1403debd..12e1076dc2 100644 --- a/examples/otlp/http_metric_main.cc +++ b/examples/otlp/http_metric_main.cc @@ -37,9 +37,7 @@ namespace internal_log = opentelemetry::sdk::common::internal_log; namespace { -otlp_exporter::OtlpHttpMetricExporterOptions exporter_options; - -void InitMetrics() +void InitMetrics(const otlp_exporter::OtlpHttpMetricExporterOptions &exporter_options) { auto exporter = otlp_exporter::OtlpHttpMetricExporterFactory::Create(exporter_options); @@ -80,6 +78,7 @@ void CleanupMetrics() */ int main(int argc, char *argv[]) { + otlp_exporter::OtlpHttpMetricExporterOptions exporter_options; std::string example_type; if (argc > 1) { @@ -112,7 +111,7 @@ int main(int argc, char *argv[]) } // Removing this line will leave the default noop MetricProvider in place. - InitMetrics(); + InitMetrics(exporter_options); std::string name{"otlp_http_metric_example"}; if (example_type == "counter") diff --git a/examples/zipkin/main.cc b/examples/zipkin/main.cc index 1f7f0060c0..d85098ffbb 100644 --- a/examples/zipkin/main.cc +++ b/examples/zipkin/main.cc @@ -28,8 +28,7 @@ namespace resource = opentelemetry::sdk::resource; namespace { -zipkin::ZipkinExporterOptions opts; -void InitTracer() +void InitTracer(const zipkin::ZipkinExporterOptions &opts) { // Create zipkin exporter instance resource::ResourceAttributes attributes = {{"service.name", "zipkin_demo_service"}}; @@ -51,12 +50,13 @@ void CleanupTracer() int main(int argc, char *argv[]) { + zipkin::ZipkinExporterOptions opts; if (argc == 2) { opts.endpoint = argv[1]; } // Removing this line will leave the default noop TracerProvider in place. - InitTracer(); + InitTracer(opts); foo_library(); diff --git a/exporters/prometheus/src/exporter_utils.cc b/exporters/prometheus/src/exporter_utils.cc index 694a30a079..bcca6bb73c 100644 --- a/exporters/prometheus/src/exporter_utils.cc +++ b/exporters/prometheus/src/exporter_utils.cc @@ -288,14 +288,6 @@ std::string PrometheusExporterUtils::SanitizeNames(std::string name) return name; } -#if OPENTELEMETRY_HAVE_WORKING_REGEX -const std::regex INVALID_CHARACTERS_PATTERN("[^a-zA-Z0-9]"); -const std::regex CHARACTERS_BETWEEN_BRACES_PATTERN("\\{(.*?)\\}"); -const std::regex SANITIZE_LEADING_UNDERSCORES("^_+"); -const std::regex SANITIZE_TRAILING_UNDERSCORES("_+$"); -const std::regex SANITIZE_CONSECUTIVE_UNDERSCORES("[_]{2,}"); -#endif - std::string PrometheusExporterUtils::GetEquivalentPrometheusUnit( const std::string &raw_metric_unit_name) { @@ -371,6 +363,7 @@ std::string PrometheusExporterUtils::GetPrometheusPerUnit(const std::string &per std::string PrometheusExporterUtils::RemoveUnitPortionInBraces(const std::string &unit) { #if OPENTELEMETRY_HAVE_WORKING_REGEX + static const std::regex CHARACTERS_BETWEEN_BRACES_PATTERN("\\{(.*?)\\}"); return std::regex_replace(unit, CHARACTERS_BETWEEN_BRACES_PATTERN, ""); #else bool in_braces = false; @@ -425,6 +418,10 @@ std::string PrometheusExporterUtils::ConvertRateExpressedToPrometheusUnit( std::string PrometheusExporterUtils::CleanUpString(const std::string &str) { #if OPENTELEMETRY_HAVE_WORKING_REGEX + static const std::regex INVALID_CHARACTERS_PATTERN("[^a-zA-Z0-9]"); + static const std::regex SANITIZE_LEADING_UNDERSCORES("^_+"); + static const std::regex SANITIZE_TRAILING_UNDERSCORES("_+$"); + static const std::regex SANITIZE_CONSECUTIVE_UNDERSCORES("[_]{2,}"); std::string cleaned_string = std::regex_replace(str, INVALID_CHARACTERS_PATTERN, "_"); cleaned_string = std::regex_replace(cleaned_string, SANITIZE_CONSECUTIVE_UNDERSCORES, "_"); cleaned_string = std::regex_replace(cleaned_string, SANITIZE_TRAILING_UNDERSCORES, ""); diff --git a/exporters/zipkin/src/recordable.cc b/exporters/zipkin/src/recordable.cc index 24325ffd24..75cf962c6f 100644 --- a/exporters/zipkin/src/recordable.cc +++ b/exporters/zipkin/src/recordable.cc @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include #include "nlohmann/json.hpp" @@ -39,14 +37,6 @@ namespace trace_api = opentelemetry::trace; namespace common = opentelemetry::common; namespace sdk = opentelemetry::sdk; -// constexpr needs keys to be constexpr, const is next best to use. -static const std::map kSpanKindMap = { - {trace_api::SpanKind::kClient, "CLIENT"}, - {trace_api::SpanKind::kServer, "SERVER"}, - {trace_api::SpanKind::kConsumer, "CONSUMER"}, - {trace_api::SpanKind::kProducer, "PRODUCER"}, -}; - // // See `attribute_value.h` for details. // @@ -262,10 +252,22 @@ void Recordable::SetDuration(std::chrono::nanoseconds duration) noexcept void Recordable::SetSpanKind(trace_api::SpanKind span_kind) noexcept { - auto span_iter = kSpanKindMap.find(span_kind); - if (span_iter != kSpanKindMap.end()) + switch (span_kind) { - span_["kind"] = span_iter->second; + case trace_api::SpanKind::kClient: + span_["kind"] = "CLIENT"; + break; + case trace_api::SpanKind::kServer: + span_["kind"] = "SERVER"; + break; + case trace_api::SpanKind::kConsumer: + span_["kind"] = "CONSUMER"; + break; + case trace_api::SpanKind::kProducer: + span_["kind"] = "PRODUCER"; + break; + default: + break; } } diff --git a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h index c4e2226f99..541b74a85c 100644 --- a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h @@ -39,9 +39,9 @@ namespace client { namespace curl { -const std::chrono::milliseconds kDefaultHttpConnTimeout(5000); // ms -const std::string kHttpStatusRegexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*"; -const std::string kHttpHeaderRegexp = "(.*)\\: (.*)\\n*"; +constexpr std::chrono::milliseconds kDefaultHttpConnTimeout{5000}; // ms +constexpr const char *kHttpStatusRegexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*"; +constexpr const char *kHttpHeaderRegexp = "(.*)\\: (.*)\\n*"; /** * Default max HTTP Response size. diff --git a/functional/otlp/func_grpc_main.cc b/functional/otlp/func_grpc_main.cc index 808bb227bc..f53cde3f1b 100644 --- a/functional/otlp/func_grpc_main.cc +++ b/functional/otlp/func_grpc_main.cc @@ -51,7 +51,8 @@ static bool opt_list = false; static bool opt_debug = false; static bool opt_secure = false; // HTTPS by default -static std::string opt_endpoint = "https://127.0.0.1:4317"; +constexpr char kDefaultOptEndpoint[] = "https://127.0.0.1:4317"; +static std::string opt_endpoint; static std::string opt_cert_dir; static std::string opt_test_name; static TestMode opt_mode = TestMode::kNone; @@ -318,7 +319,7 @@ typedef int (*test_func_t)(); struct test_case { - std::string m_name; + nostd::string_view m_name; test_func_t m_func; }; @@ -392,6 +393,8 @@ int main(int argc, char *argv[]) argc--; argv++; + opt_endpoint = kDefaultOptEndpoint; + int rc = parse_args(argc, argv); if (rc != 0) diff --git a/functional/otlp/func_http_main.cc b/functional/otlp/func_http_main.cc index 525580b18a..ae7399f46d 100644 --- a/functional/otlp/func_http_main.cc +++ b/functional/otlp/func_http_main.cc @@ -50,7 +50,8 @@ static bool opt_list = false; static bool opt_debug = false; static bool opt_secure = false; // HTTPS by default -static std::string opt_endpoint = "https://localhost:4318/v1/traces"; +constexpr char kDefaultOptEndpoint[] = "https://localhost:4318/v1/traces"; +static std::string opt_endpoint; static std::string opt_cert_dir; static std::string opt_test_name; static test_mode opt_mode = MODE_NONE; @@ -335,7 +336,7 @@ typedef int (*test_func_t)(); struct test_case { - std::string m_name; + nostd::string_view m_name; test_func_t m_func; }; @@ -462,6 +463,8 @@ int main(int argc, char *argv[]) argc--; argv++; + opt_endpoint = kDefaultOptEndpoint; + int rc = parse_args(argc, argv); if (rc != 0) diff --git a/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h b/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h index 19e10343ba..7812cdc38e 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h @@ -26,11 +26,17 @@ namespace metrics using opentelemetry::sdk::common::OrderedAttributeMap; constexpr size_t kAggregationCardinalityLimit = 2000; -const std::string kAttributesLimitOverflowKey = "otel.metric.overflow"; const bool kAttributesLimitOverflowValue = true; -const MetricAttributes kOverflowAttributes = { - {kAttributesLimitOverflowKey, - kAttributesLimitOverflowValue}}; // precalculated for optimization + +constexpr char kAttributesLimitOverflowKey[] = "otel.metric.overflow"; + +inline const MetricAttributes &GetOverflowAttributes() +{ + static const MetricAttributes value = { + {kAttributesLimitOverflowKey, + kAttributesLimitOverflowValue}}; // precalculated for optimization + return value; +} class AttributeHashGenerator { @@ -127,7 +133,7 @@ class AttributesHashMapWithCustomHash } else if (IsOverflowAttributes(attributes)) { - hash_map_[kOverflowAttributes] = std::move(aggr); + hash_map_[GetOverflowAttributes()] = std::move(aggr); } else { @@ -144,7 +150,7 @@ class AttributesHashMapWithCustomHash } else if (IsOverflowAttributes(attributes)) { - hash_map_[kOverflowAttributes] = std::move(aggr); + hash_map_[GetOverflowAttributes()] = std::move(aggr); } else { @@ -191,13 +197,13 @@ class AttributesHashMapWithCustomHash Aggregation *GetOrSetOveflowAttributes(std::unique_ptr agg) { - auto it = hash_map_.find(kOverflowAttributes); + auto it = hash_map_.find(GetOverflowAttributes()); if (it != hash_map_.end()) { return it->second.get(); } - auto result = hash_map_.emplace(kOverflowAttributes, std::move(agg)); + auto result = hash_map_.emplace(GetOverflowAttributes(), std::move(agg)); return result.first->second.get(); } @@ -205,12 +211,12 @@ class AttributesHashMapWithCustomHash { // If the incoming attributes are exactly the overflow sentinel, route // directly to the overflow entry. - if (attributes == kOverflowAttributes) + if (attributes == GetOverflowAttributes()) { return true; } // Determine if overflow entry already exists. - bool has_overflow = (hash_map_.find(kOverflowAttributes) != hash_map_.end()); + bool has_overflow = (hash_map_.find(GetOverflowAttributes()) != hash_map_.end()); // If overflow already present, total size already includes it; trigger overflow // when current size (including overflow) is >= limit. if (has_overflow) diff --git a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h index b3112a47f6..5bce0fa379 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h @@ -236,17 +236,17 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage // Must be called with attribute_hashmap_lock_ held. MetricAttributes ResolveCardinality(const MetricAttributes &filtered) noexcept { - if (filtered == kOverflowAttributes) + if (filtered == GetOverflowAttributes()) { - active_keys_.insert(kOverflowAttributes); - return kOverflowAttributes; + active_keys_.insert(GetOverflowAttributes()); + return GetOverflowAttributes(); } if (active_keys_.find(filtered) != active_keys_.end()) { return filtered; } const size_t limit = aggregation_config_->cardinality_limit_; - const bool has_overflow = active_keys_.find(kOverflowAttributes) != active_keys_.end(); + const bool has_overflow = active_keys_.find(GetOverflowAttributes()) != active_keys_.end(); // Mirror AttributesHashMap::IsOverflowAttributes() exactly. When the // overflow slot is already counted in active_keys_, only route to // overflow when total active keys reach the limit. Otherwise simulate @@ -255,8 +255,8 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage has_overflow ? (active_keys_.size() >= limit) : (active_keys_.size() + 1 >= limit); if (would_overflow) { - active_keys_.insert(kOverflowAttributes); - return kOverflowAttributes; + active_keys_.insert(GetOverflowAttributes()); + return GetOverflowAttributes(); } active_keys_.insert(filtered); return filtered; diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index 4c9969a89f..dcaf683395 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -137,7 +137,6 @@ class Tracer final : public opentelemetry::trace::Tracer, #if OPENTELEMETRY_ABI_VERSION_NO < 2 std::atomic is_enabled_{false}; #endif - static const std::shared_ptr kNoopTracer; }; } // namespace trace } // namespace sdk diff --git a/sdk/src/metrics/instrument_metadata_validator.cc b/sdk/src/metrics/instrument_metadata_validator.cc index 482330a682..1fe193b0df 100644 --- a/sdk/src/metrics/instrument_metadata_validator.cc +++ b/sdk/src/metrics/instrument_metadata_validator.cc @@ -1,10 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include - -#include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/metrics/instrument_metadata_validator.h" +#include "opentelemetry/nostd/string_view.h" #include "opentelemetry/version.h" #if OPENTELEMETRY_HAVE_WORKING_REGEX @@ -20,9 +18,9 @@ namespace metrics { #if OPENTELEMETRY_HAVE_WORKING_REGEX // instrument-name = ALPHA 0*254 ("_" / "." / "-" / "/" / ALPHA / DIGIT) -const std::string kInstrumentNamePattern = "[a-zA-Z][-_./a-zA-Z0-9]{0,254}"; +constexpr const char *kInstrumentNamePattern = "[a-zA-Z][-_./a-zA-Z0-9]{0,254}"; // -const std::string kInstrumentUnitPattern = "[\x01-\x7F]{0,63}"; +constexpr const char *kInstrumentUnitPattern = "[\x01-\x7F]{0,63}"; // instrument-unit = It can have a maximum length of 63 ASCII chars #endif diff --git a/sdk/src/metrics/state/sync_metric_storage.cc b/sdk/src/metrics/state/sync_metric_storage.cc index f4accbe124..80ec279d10 100644 --- a/sdk/src/metrics/state/sync_metric_storage.cc +++ b/sdk/src/metrics/state/sync_metric_storage.cc @@ -204,7 +204,7 @@ std::shared_ptr SyncMetricStorage::Bind( MetricAttributes key = ResolveCardinality(filtered); // If we ended up at overflow, dedupe against an existing overflow entry. - if (key == kOverflowAttributes) + if (key == GetOverflowAttributes()) { auto ov_it = bound_entries_.find(key); if (ov_it != bound_entries_.end()) diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 298873db31..b0f0e593e8 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -37,9 +37,6 @@ namespace sdk { namespace trace { -const std::shared_ptr Tracer::kNoopTracer = - std::make_shared(); - Tracer::Tracer(std::shared_ptr context, std::unique_ptr instrumentation_scope) noexcept : instrumentation_scope_{std::move(instrumentation_scope)}, @@ -58,6 +55,8 @@ nostd::shared_ptr Tracer::StartSpan( // Check if the tracer is enabled using the API Tracer::Enabled() accessor if available. if (!Enabled()) { + static const std::shared_ptr kNoopTracer = + std::make_shared(); return kNoopTracer->StartSpan(name, attributes, links, options); } diff --git a/sdk/test/common/circular_buffer_test.cc b/sdk/test/common/circular_buffer_test.cc index 6dc1b627cc..7617683fbb 100644 --- a/sdk/test/common/circular_buffer_test.cc +++ b/sdk/test/common/circular_buffer_test.cc @@ -23,7 +23,11 @@ using opentelemetry::sdk::common::AtomicUniquePtr; using opentelemetry::sdk::common::CircularBuffer; using opentelemetry::sdk::common::CircularBufferRange; -static thread_local std::mt19937 RandomNumberGenerator{std::random_device{}()}; +static std::mt19937 &RandomNumberGenerator() +{ + static thread_local std::mt19937 generator{std::random_device{}()}; + return generator; +} static void GenerateRandomNumbers(CircularBuffer &buffer, std::vector &numbers, @@ -31,7 +35,7 @@ static void GenerateRandomNumbers(CircularBuffer &buffer, { for (int i = 0; i < n; ++i) { - auto value = static_cast(RandomNumberGenerator()); + auto value = static_cast(RandomNumberGenerator()()); std::unique_ptr x{new uint32_t{value}}; if (buffer.Add(x)) { @@ -73,7 +77,8 @@ static void RunNumberConsumer(CircularBuffer &buffer, { return; } - auto n = std::uniform_int_distribution{0, buffer.Peek().size()}(RandomNumberGenerator); + auto n = + std::uniform_int_distribution{0, buffer.Peek().size()}(RandomNumberGenerator()); buffer.Consume(n, [&](CircularBufferRange> range) noexcept { assert(range.size() == n); range.ForEach([&](AtomicUniquePtr &ptr) noexcept { diff --git a/sdk/test/logs/logger_config_test.cc b/sdk/test/logs/logger_config_test.cc index f6bdedf944..b2d2925f61 100644 --- a/sdk/test/logs/logger_config_test.cc +++ b/sdk/test/logs/logger_config_test.cc @@ -54,40 +54,70 @@ TEST(LoggerConfig, CheckCreateWorksAsExpected) /** Tests to verify the behavior of logs_sdk::LoggerConfig::Default */ -static std::pair attr1 = { - "accept_single_attr", true}; -static std::pair attr2 = { - "accept_second_attr", "some other attr"}; -static std::pair attr3 = { - "accept_third_attr", 3}; - -static instrumentation_scope::InstrumentationScope test_scope_1 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); -static instrumentation_scope::InstrumentationScope test_scope_2 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); -static instrumentation_scope::InstrumentationScope test_scope_3 = - *instrumentation_scope::InstrumentationScope::Create( - "test_scope_3", - "0", - "https://opentelemetry.io/schemas/v1.18.0"); -static instrumentation_scope::InstrumentationScope test_scope_4 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_4", - "0", - "https://opentelemetry.io/schemas/v1.18.0", - {attr1}); -static instrumentation_scope::InstrumentationScope test_scope_5 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_5", - "0", - "https://opentelemetry.io/schemas/v1.18.0", - {attr1, attr2, attr3}); +static std::pair & +GetAttr1() +{ + static std::pair value{ + "accept_single_attr", true}; + return value; +} +static std::pair & +GetAttr2() +{ + static std::pair value{ + "accept_second_attr", "some other attr"}; + return value; +} +static std::pair & +GetAttr3() +{ + static std::pair value{ + "accept_third_attr", 3}; + return value; +} + +static instrumentation_scope::InstrumentationScope &GetTestScope1() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope2() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope3() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_3", "0", "https://opentelemetry.io/schemas/v1.18.0"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope4() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_4", "0", "https://opentelemetry.io/schemas/v1.18.0", {GetAttr1()}); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope5() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_5", "0", "https://opentelemetry.io/schemas/v1.18.0", + {GetAttr1(), GetAttr2(), GetAttr3()}); + return value; +} // This array could also directly contain the reference types, but that leads to 'uninitialized // value was created by heap allocation' errors in Valgrind memcheck. This is a bug in Googletest // library, see https://github.com/google/googletest/issues/3805#issuecomment-1397301790 for more // details. Using pointers is a workaround to prevent the Valgrind warnings. -const std::array instrumentation_scopes = { - &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, -}; +static const std::array & +GetInstrumentationScopes() +{ + static const std::array value = { + &GetTestScope1(), &GetTestScope2(), &GetTestScope3(), &GetTestScope4(), &GetTestScope5(), + }; + return value; +} namespace { @@ -121,12 +151,12 @@ TEST(LoggerConfig, ScopeConfiguratorPreservesCustomConfig) .AddConditionNameEquals("test_scope_1", matching_config) .Build(); - ASSERT_EQ(configurator.ComputeConfig(test_scope_1), matching_config); - ASSERT_EQ(configurator.ComputeConfig(test_scope_2), default_config); + ASSERT_EQ(configurator.ComputeConfig(GetTestScope1()), matching_config); + ASSERT_EQ(configurator.ComputeConfig(GetTestScope2()), default_config); } INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, DefaultLoggerConfiguratorTestFixture, - ::testing::ValuesIn(instrumentation_scopes)); + ::testing::ValuesIn(GetInstrumentationScopes())); } // namespace diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index 84cd131566..70d34004af 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -773,62 +773,113 @@ class CustomLogConfiguratorTestData // constants used in VerifyCustomConfiguratorBehavior test static auto noop_logger = logs_api::NoopLogger(); -const std::string schema{"https://opentelemetry.io/schemas/1.11.0"}; +constexpr char schema[] = "https://opentelemetry.io/schemas/1.11.0"; // Generate test case data // Test Case 1 -static auto instrumentation_scope_1 = - *InstrumentationScope::Create("opentelemetry_library", "1.0.0", schema); -static auto test_log_recordable_1 = - create_mock_log_recordable("Log Message", opentelemetry::logs::Severity::kWarn); -static auto expected_log_recordable_1 = - create_mock_log_recordable("Log Message", opentelemetry::logs::Severity::kWarn); -static auto custom_log_configurator_test_data_1 = - CustomLogConfiguratorTestData(instrumentation_scope_1, - *test_log_recordable_1, - *expected_log_recordable_1, - false); +static InstrumentationScope &GetInstrumentationScope1() +{ + static auto value = *InstrumentationScope::Create("opentelemetry_library", "1.0.0", schema); + return value; +} +static std::unique_ptr &GetTestLogRecordable1() +{ + static auto value = + create_mock_log_recordable("Log Message", opentelemetry::logs::Severity::kWarn); + return value; +} +static std::unique_ptr &GetExpectedLogRecordable1() +{ + static auto value = + create_mock_log_recordable("Log Message", opentelemetry::logs::Severity::kWarn); + return value; +} +static CustomLogConfiguratorTestData &GetCustomLogConfiguratorTestData1() +{ + static auto value = CustomLogConfiguratorTestData( + GetInstrumentationScope1(), *GetTestLogRecordable1(), *GetExpectedLogRecordable1(), false); + return value; +} // Test Case 2 -static auto instrumentation_scope_2 = *InstrumentationScope::Create("bar_library", "1.0.0", schema); -static auto test_log_recordable_2 = - create_mock_log_recordable("", opentelemetry::logs::Severity::kDebug); -static auto expected_log_recordable_2 = - create_mock_log_recordable("", opentelemetry::logs::Severity::kDebug); -static auto custom_log_configurator_test_data_2 = - CustomLogConfiguratorTestData(instrumentation_scope_2, - *test_log_recordable_2, - *expected_log_recordable_2, - false); +static InstrumentationScope &GetInstrumentationScope2() +{ + static auto value = *InstrumentationScope::Create("bar_library", "1.0.0", schema); + return value; +} +static std::unique_ptr &GetTestLogRecordable2() +{ + static auto value = create_mock_log_recordable("", opentelemetry::logs::Severity::kDebug); + return value; +} +static std::unique_ptr &GetExpectedLogRecordable2() +{ + static auto value = create_mock_log_recordable("", opentelemetry::logs::Severity::kDebug); + return value; +} +static CustomLogConfiguratorTestData &GetCustomLogConfiguratorTestData2() +{ + static auto value = CustomLogConfiguratorTestData( + GetInstrumentationScope2(), *GetTestLogRecordable2(), *GetExpectedLogRecordable2(), false); + return value; +} // Test Case 3 -static auto instrumentation_scope_3 = *InstrumentationScope::Create("foo_library", "", schema); -static auto test_log_recordable_3 = - create_mock_log_recordable("Info message", opentelemetry::logs::Severity::kInfo); -static auto expected_log_recordable_3 = - create_mock_log_recordable("", opentelemetry::logs::Severity::kInvalid); -static auto custom_log_configurator_test_data_3 = - CustomLogConfiguratorTestData(instrumentation_scope_3, - *test_log_recordable_3, - *expected_log_recordable_3, - true); +static InstrumentationScope &GetInstrumentationScope3() +{ + static auto value = *InstrumentationScope::Create("foo_library", "", schema); + return value; +} +static std::unique_ptr &GetTestLogRecordable3() +{ + static auto value = + create_mock_log_recordable("Info message", opentelemetry::logs::Severity::kInfo); + return value; +} +static std::unique_ptr &GetExpectedLogRecordable3() +{ + static auto value = create_mock_log_recordable("", opentelemetry::logs::Severity::kInvalid); + return value; +} +static CustomLogConfiguratorTestData &GetCustomLogConfiguratorTestData3() +{ + static auto value = CustomLogConfiguratorTestData( + GetInstrumentationScope3(), *GetTestLogRecordable3(), *GetExpectedLogRecordable3(), true); + return value; +} // Test Case 4 -static auto instrumentation_scope_4 = *InstrumentationScope::Create("allowed_library", "", schema); -static auto test_log_recordable_4 = - create_mock_log_recordable("Scope version missing", opentelemetry::logs::Severity::kInfo); -static auto expected_log_recordable_4 = - create_mock_log_recordable("", opentelemetry::logs::Severity::kInvalid); -static auto custom_log_configurator_test_data_4 = - CustomLogConfiguratorTestData(instrumentation_scope_4, - *test_log_recordable_4, - *expected_log_recordable_4, - true); +static InstrumentationScope &GetInstrumentationScope4() +{ + static auto value = *InstrumentationScope::Create("allowed_library", "", schema); + return value; +} +static std::unique_ptr &GetTestLogRecordable4() +{ + static auto value = + create_mock_log_recordable("Scope version missing", opentelemetry::logs::Severity::kInfo); + return value; +} +static std::unique_ptr &GetExpectedLogRecordable4() +{ + static auto value = create_mock_log_recordable("", opentelemetry::logs::Severity::kInvalid); + return value; +} +static CustomLogConfiguratorTestData &GetCustomLogConfiguratorTestData4() +{ + static auto value = CustomLogConfiguratorTestData( + GetInstrumentationScope4(), *GetTestLogRecordable4(), *GetExpectedLogRecordable4(), true); + return value; +} // This array could also directly contain the reference types, but that leads to 'uninitialized // value was created by heap allocation' errors in Valgrind memcheck. This is a bug in Googletest // library, see https://github.com/google/googletest/issues/3805#issuecomment-1397301790 for more // details. Using pointers is a workaround to prevent the Valgrind warnings. -constexpr std::array log_configurator_test_cases = { - &custom_log_configurator_test_data_1, &custom_log_configurator_test_data_2, - &custom_log_configurator_test_data_3, &custom_log_configurator_test_data_4}; +static const std::array &GetLogConfiguratorTestCases() +{ + static const std::array value = { + &GetCustomLogConfiguratorTestData1(), &GetCustomLogConfiguratorTestData2(), + &GetCustomLogConfiguratorTestData3(), &GetCustomLogConfiguratorTestData4()}; + return value; +} // Test fixture for VerifyCustomConfiguratorBehavior class CustomLoggerConfiguratorTestFixture @@ -899,7 +950,7 @@ TEST_P(CustomLoggerConfiguratorTestFixture, VerifyCustomConfiguratorBehavior) INSTANTIATE_TEST_SUITE_P(CustomLogConfiguratorTestData, CustomLoggerConfiguratorTestFixture, - ::testing::ValuesIn(log_configurator_test_cases)); + ::testing::ValuesIn(GetLogConfiguratorTestCases())); #if OPENTELEMETRY_ABI_VERSION_NO < 2 TEST(LoggerSDK, EventLog) diff --git a/sdk/test/metrics/attributes_hashmap_test.cc b/sdk/test/metrics/attributes_hashmap_test.cc index 04ccd4fe48..157f3f814b 100644 --- a/sdk/test/metrics/attributes_hashmap_test.cc +++ b/sdk/test/metrics/attributes_hashmap_test.cc @@ -203,7 +203,7 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) EXPECT_EQ(map.Size(), limit); // Ensure overflow key was actually created and accessible via Get - EXPECT_NE(map.Get(kOverflowAttributes), nullptr); + EXPECT_NE(map.Get(GetOverflowAttributes()), nullptr); // Ensure original real attributes still present for (size_t i = 0; i < limit - 1; ++i) @@ -219,7 +219,7 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) return true; }); EXPECT_EQ(map_copy.Size(), map.Size()); - EXPECT_NE(map_copy.Get(kOverflowAttributes), nullptr); + EXPECT_NE(map_copy.Get(GetOverflowAttributes()), nullptr); for (size_t i = 0; i < limit - 1; ++i) { MetricAttributes attr = {{"k", std::to_string(i)}}; diff --git a/sdk/test/metrics/cardinality_limit_test.cc b/sdk/test/metrics/cardinality_limit_test.cc index bb6fda78e4..b28f23a847 100644 --- a/sdk/test/metrics/cardinality_limit_test.cc +++ b/sdk/test/metrics/cardinality_limit_test.cc @@ -75,7 +75,7 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) EXPECT_EQ(hash_map.Size(), 10); // no new metric point added // get the overflow metric point - auto agg1 = hash_map.GetOrSetDefault(kOverflowAttributes, aggregation_callback); + auto agg1 = hash_map.GetOrSetDefault(GetOverflowAttributes(), aggregation_callback); EXPECT_NE(agg1, nullptr); auto sum_agg1 = static_cast(agg1); EXPECT_EQ(nostd::get(nostd::get(sum_agg1->ToPoint()).value_), @@ -175,12 +175,12 @@ INSTANTIATE_TEST_SUITE_P(All, // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#cardinality-limits TEST(CardinalityLimitOverflowAttribute, MatchesSpecLiteral) { - EXPECT_EQ(opentelemetry::sdk::metrics::kAttributesLimitOverflowKey, "otel.metric.overflow"); + EXPECT_STREQ(opentelemetry::sdk::metrics::kAttributesLimitOverflowKey, "otel.metric.overflow"); EXPECT_EQ(opentelemetry::sdk::metrics::kAttributesLimitOverflowValue, true); // The precomputed overflow attribute set MUST contain exactly the spec key // mapped to the boolean value `true`. - ASSERT_EQ(opentelemetry::sdk::metrics::kOverflowAttributes.size(), 1u); - const auto &entry = *opentelemetry::sdk::metrics::kOverflowAttributes.begin(); + ASSERT_EQ(opentelemetry::sdk::metrics::GetOverflowAttributes().size(), 1u); + const auto &entry = *opentelemetry::sdk::metrics::GetOverflowAttributes().begin(); EXPECT_EQ(entry.first, "otel.metric.overflow"); EXPECT_EQ(nostd::get(entry.second), true); } diff --git a/sdk/test/metrics/meter_config_test.cc b/sdk/test/metrics/meter_config_test.cc index bdc737063d..cd41277a88 100644 --- a/sdk/test/metrics/meter_config_test.cc +++ b/sdk/test/metrics/meter_config_test.cc @@ -36,40 +36,70 @@ TEST(MeterConfig, CheckDefaultConfigWorksAccToSpec) /** Tests to verify the behavior of metrics_sdk::MeterConfig::Default */ -static std::pair attr1 = { - "accept_single_attr", true}; -static std::pair attr2 = { - "accept_second_attr", "some other attr"}; -static std::pair attr3 = { - "accept_third_attr", 3}; - -static instrumentation_scope::InstrumentationScope test_scope_1 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); -static instrumentation_scope::InstrumentationScope test_scope_2 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); -static instrumentation_scope::InstrumentationScope test_scope_3 = - *instrumentation_scope::InstrumentationScope::Create( - "test_scope_3", - "0", - "https://opentelemetry.io/schemas/v1.18.0"); -static instrumentation_scope::InstrumentationScope test_scope_4 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_4", - "0", - "https://opentelemetry.io/schemas/v1.18.0", - {attr1}); -static instrumentation_scope::InstrumentationScope test_scope_5 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_5", - "0", - "https://opentelemetry.io/schemas/v1.18.0", - {attr1, attr2, attr3}); +static std::pair & +GetAttr1() +{ + static std::pair value{ + "accept_single_attr", true}; + return value; +} +static std::pair & +GetAttr2() +{ + static std::pair value{ + "accept_second_attr", "some other attr"}; + return value; +} +static std::pair & +GetAttr3() +{ + static std::pair value{ + "accept_third_attr", 3}; + return value; +} + +static instrumentation_scope::InstrumentationScope &GetTestScope1() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope2() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope3() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_3", "0", "https://opentelemetry.io/schemas/v1.18.0"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope4() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_4", "0", "https://opentelemetry.io/schemas/v1.18.0", {GetAttr1()}); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope5() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_5", "0", "https://opentelemetry.io/schemas/v1.18.0", + {GetAttr1(), GetAttr2(), GetAttr3()}); + return value; +} // This array could also directly contain the reference types, but that leads to 'uninitialized // value was created by heap allocation' errors in Valgrind memcheck. This is a bug in Googletest // library, see https://github.com/google/googletest/issues/3805#issuecomment-1397301790 for more // details. Using pointers is a workaround to prevent the Valgrind warnings. -const std::array instrumentation_scopes = { - &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, -}; +static const std::array & +GetInstrumentationScopes() +{ + static const std::array value = { + &GetTestScope1(), &GetTestScope2(), &GetTestScope3(), &GetTestScope4(), &GetTestScope5(), + }; + return value; +} namespace { @@ -93,6 +123,6 @@ TEST_P(DefaultMeterConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, DefaultMeterConfiguratorTestFixture, - ::testing::ValuesIn(instrumentation_scopes)); + ::testing::ValuesIn(GetInstrumentationScopes())); } // namespace diff --git a/sdk/test/metrics/sync_instruments_test.cc b/sdk/test/metrics/sync_instruments_test.cc index a6402c5d66..5128a43d80 100644 --- a/sdk/test/metrics/sync_instruments_test.cc +++ b/sdk/test/metrics/sync_instruments_test.cc @@ -11,18 +11,14 @@ #include "opentelemetry/common/key_value_iterable_view.h" #include "opentelemetry/context/context.h" #include "opentelemetry/nostd/utility.h" -#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/metrics/instruments.h" #include "opentelemetry/sdk/metrics/state/metric_storage.h" #include "opentelemetry/sdk/metrics/state/multi_metric_storage.h" #include "opentelemetry/sdk/metrics/sync_instruments.h" using namespace opentelemetry; -using namespace opentelemetry::sdk::instrumentationscope; using namespace opentelemetry::sdk::metrics; -static auto instrumentation_scope = InstrumentationScope::Create("opentelemetry-cpp", "0.1.0"); - using M = std::map; TEST(SyncInstruments, LongCounter) diff --git a/sdk/test/trace/tracer_config_test.cc b/sdk/test/trace/tracer_config_test.cc index 6f849bc94b..f890d8ecb6 100644 --- a/sdk/test/trace/tracer_config_test.cc +++ b/sdk/test/trace/tracer_config_test.cc @@ -36,40 +36,70 @@ TEST(TracerConfig, CheckDefaultConfigWorksAccToSpec) /** Tests to verify the behavior of trace_sdk::TracerConfig::DefaultConfigurator */ -static std::pair attr1 = { - "accept_single_attr", true}; -static std::pair attr2 = { - "accept_second_attr", "some other attr"}; -static std::pair attr3 = { - "accept_third_attr", 3}; - -static instrumentation_scope::InstrumentationScope test_scope_1 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); -static instrumentation_scope::InstrumentationScope test_scope_2 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); -static instrumentation_scope::InstrumentationScope test_scope_3 = - *instrumentation_scope::InstrumentationScope::Create( - "test_scope_3", - "0", - "https://opentelemetry.io/schemas/v1.18.0"); -static instrumentation_scope::InstrumentationScope test_scope_4 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_4", - "0", - "https://opentelemetry.io/schemas/v1.18.0", - {attr1}); -static instrumentation_scope::InstrumentationScope test_scope_5 = - *instrumentation_scope::InstrumentationScope::Create("test_scope_5", - "0", - "https://opentelemetry.io/schemas/v1.18.0", - {attr1, attr2, attr3}); +static std::pair & +GetAttr1() +{ + static std::pair value{ + "accept_single_attr", true}; + return value; +} +static std::pair & +GetAttr2() +{ + static std::pair value{ + "accept_second_attr", "some other attr"}; + return value; +} +static std::pair & +GetAttr3() +{ + static std::pair value{ + "accept_third_attr", 3}; + return value; +} + +static instrumentation_scope::InstrumentationScope &GetTestScope1() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope2() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope3() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_3", "0", "https://opentelemetry.io/schemas/v1.18.0"); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope4() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_4", "0", "https://opentelemetry.io/schemas/v1.18.0", {GetAttr1()}); + return value; +} +static instrumentation_scope::InstrumentationScope &GetTestScope5() +{ + static auto value = *instrumentation_scope::InstrumentationScope::Create( + "test_scope_5", "0", "https://opentelemetry.io/schemas/v1.18.0", + {GetAttr1(), GetAttr2(), GetAttr3()}); + return value; +} // This array could also directly contain the reference types, but that leads to 'uninitialized // value was created by heap allocation' errors in Valgrind memcheck. This is a bug in Googletest // library, see https://github.com/google/googletest/issues/3805#issuecomment-1397301790 for more // details. Using pointers is a workaround to prevent the Valgrind warnings. -const std::array instrumentation_scopes = { - &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, -}; +static const std::array & +GetInstrumentationScopes() +{ + static const std::array value = { + &GetTestScope1(), &GetTestScope2(), &GetTestScope3(), &GetTestScope4(), &GetTestScope5(), + }; + return value; +} namespace { @@ -93,6 +123,6 @@ TEST_P(DefaultTracerConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, DefaultTracerConfiguratorTestFixture, - ::testing::ValuesIn(instrumentation_scopes)); + ::testing::ValuesIn(GetInstrumentationScopes())); } // namespace From 072353e519fe95b48a95be81b7f59eb85eb23f8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 22:34:19 +0200 Subject: [PATCH 24/49] Bump rules_cc from 0.2.21 to 0.2.22 (#4223) Bumps [rules_cc](https://github.com/bazelbuild/rules_cc) from 0.2.21 to 0.2.22. - [Release notes](https://github.com/bazelbuild/rules_cc/releases) - [Commits](https://github.com/bazelbuild/rules_cc/compare/0.2.21...0.2.22) --- updated-dependencies: - dependency-name: rules_cc dependency-version: 0.2.22 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index c4d9574873..31994c3e00 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -19,7 +19,7 @@ bazel_dep(name = "platforms", version = "1.1.0") bazel_dep(name = "prometheus-cpp", version = "1.3.0.bcr.2", repo_name = "com_github_jupp0r_prometheus_cpp") bazel_dep(name = "protobuf", version = "32.0", repo_name = "com_google_protobuf") bazel_dep(name = "rapidyaml", version = "0.12.1") -bazel_dep(name = "rules_cc", version = "0.2.21") +bazel_dep(name = "rules_cc", version = "0.2.22") bazel_dep(name = "rules_foreign_cc", version = "0.15.1") bazel_dep(name = "rules_proto", version = "7.1.0") bazel_dep(name = "zlib", version = "1.3.2") From ed73e5437b6a5196dbb58e2f2eeb65fbe3d83f2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 02:08:14 +0200 Subject: [PATCH 25/49] Bump step-security/harden-runner from 2.19.4 to 2.20.0 (#4222) Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.19.4 to 2.20.0. - [Release notes](https://github.com/step-security/harden-runner/releases) - [Commits](https://github.com/step-security/harden-runner/compare/9af89fc71515a100421586dfdb3dc9c984fbf411...bf7454d06d71f1098171f2acdf0cd4708d7b5920) --- updated-dependencies: - dependency-name: step-security/harden-runner dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yml | 4 +- .github/workflows/ci.yml | 102 +++++++++--------- .github/workflows/clang-tidy.yaml | 2 +- .github/workflows/cmake_install.yml | 20 ++-- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/cppcheck.yml | 2 +- .github/workflows/dependencies_image.yml | 2 +- .github/workflows/fossa.yml | 2 +- .github/workflows/iwyu.yml | 2 +- .github/workflows/ossf-scorecard.yml | 2 +- .../workflows/project_management_comment.yml | 2 +- .../project_management_issue_open.yml | 2 +- 12 files changed, 72 insertions(+), 72 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 55ce42a59b..61ba188891 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9098076489..1983f747fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -60,7 +60,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -84,7 +84,7 @@ jobs: BUILD_TYPE: 'Release' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -114,7 +114,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -152,7 +152,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -190,7 +190,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -228,7 +228,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -259,7 +259,7 @@ jobs: CXX_STANDARD: '14' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -290,7 +290,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -326,7 +326,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -357,7 +357,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -384,7 +384,7 @@ jobs: runs-on: windows-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -402,7 +402,7 @@ jobs: runs-on: windows-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -422,7 +422,7 @@ jobs: runs-on: windows-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -442,7 +442,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -468,7 +468,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -486,7 +486,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -513,7 +513,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -537,7 +537,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -561,7 +561,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -595,7 +595,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -619,7 +619,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -653,7 +653,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -683,7 +683,7 @@ jobs: CXX_STANDARD: '14' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -704,7 +704,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -726,7 +726,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -750,7 +750,7 @@ jobs: if: false steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -774,7 +774,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -800,7 +800,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -826,7 +826,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -852,7 +852,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -878,7 +878,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -904,7 +904,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -930,7 +930,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -956,7 +956,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -982,7 +982,7 @@ jobs: runs-on: macos-15 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1009,7 +1009,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1042,7 +1042,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1057,7 +1057,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1070,7 +1070,7 @@ jobs: runs-on: windows-2022 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1090,7 +1090,7 @@ jobs: runs-on: windows-2022 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1112,7 +1112,7 @@ jobs: runs-on: windows-2022 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1132,7 +1132,7 @@ jobs: runs-on: windows-2022 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1152,7 +1152,7 @@ jobs: if: false steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1170,7 +1170,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1199,7 +1199,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1216,7 +1216,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1231,7 +1231,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1249,7 +1249,7 @@ jobs: runs-on: windows-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1265,7 +1265,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -1309,7 +1309,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 96d4c50cfd..738452084c 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -25,7 +25,7 @@ jobs: CXX: /usr/bin/clang++-22 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/cmake_install.yml b/.github/workflows/cmake_install.yml index a8481ecc58..c8c5ccbe40 100644 --- a/.github/workflows/cmake_install.yml +++ b/.github/workflows/cmake_install.yml @@ -19,7 +19,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -41,7 +41,7 @@ jobs: CXX_STANDARD: '20' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -64,7 +64,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -96,7 +96,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -126,7 +126,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -159,7 +159,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -191,7 +191,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -230,7 +230,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -270,7 +270,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit @@ -302,7 +302,7 @@ jobs: BUILD_TYPE: 'Debug' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 41622c46df..517a7a20cc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: CXX_STANDARD: '17' steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml index 335f61d89d..93a857bfea 100644 --- a/.github/workflows/cppcheck.yml +++ b/.github/workflows/cppcheck.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/dependencies_image.yml b/.github/workflows/dependencies_image.yml index d7388a24b8..db6e9837f6 100644 --- a/.github/workflows/dependencies_image.yml +++ b/.github/workflows/dependencies_image.yml @@ -13,7 +13,7 @@ jobs: timeout-minutes: 300 steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index f41b0e1a20..870f1e0f1e 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index e7e832f0bb..11946dc0a5 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 7bcdce7067..add463e933 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -20,7 +20,7 @@ jobs: id-token: write steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/project_management_comment.yml b/.github/workflows/project_management_comment.yml index c74a3e5c72..1c7b1065a0 100644 --- a/.github/workflows/project_management_comment.yml +++ b/.github/workflows/project_management_comment.yml @@ -15,7 +15,7 @@ jobs: issues: write steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit diff --git a/.github/workflows/project_management_issue_open.yml b/.github/workflows/project_management_issue_open.yml index ddd405fff0..793be64e18 100644 --- a/.github/workflows/project_management_issue_open.yml +++ b/.github/workflows/project_management_issue_open.yml @@ -14,7 +14,7 @@ jobs: issues: write steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit From ef420fd1af4d3e5c20014496a6f8e9fff64eee76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:16:42 +0200 Subject: [PATCH 26/49] Bump bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml (#4221) Bumps [bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml](https://github.com/bazel-contrib/publish-to-bcr) from 1.4.1 to 1.4.2. - [Release notes](https://github.com/bazel-contrib/publish-to-bcr/releases) - [Commits](https://github.com/bazel-contrib/publish-to-bcr/compare/c316f1611511a40423572303f66c80bb30bfe2f8...ad6879fd015a01c3e3b1b318fe3c061f928ac2fb) --- updated-dependencies: - dependency-name: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml dependency-version: 1.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish-to-bcr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-to-bcr.yml b/.github/workflows/publish-to-bcr.yml index 0850c7e9b9..e68fe88bdd 100644 --- a/.github/workflows/publish-to-bcr.yml +++ b/.github/workflows/publish-to-bcr.yml @@ -25,7 +25,7 @@ jobs: contents: write id-token: write attestations: write - uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@c316f1611511a40423572303f66c80bb30bfe2f8 # v1.4.1 + uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@ad6879fd015a01c3e3b1b318fe3c061f928ac2fb # v1.4.2 with: tag_name: ${{ inputs.tag_name || github.event.release.tag_name }} registry_fork: ${{ inputs.registry_fork || 'open-telemetry/bazel-central-registry' }} From 2cf96c742a81d6038782aa960d5418ed47df4043 Mon Sep 17 00:00:00 2001 From: Mateen Anjum Date: Thu, 9 Jul 2026 06:05:45 -0400 Subject: [PATCH 27/49] [CODE HEALTH] Move ext and exporters test classes into anonymous namespace (#4225) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ exporters/memory/test/in_memory_metric_exporter_test.cc | 5 +++++ exporters/ostream/test/ostream_span_test.cc | 5 +++++ ext/test/http/url_parser_test.cc | 5 +++++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 738452084c..03dd195aac 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 240 + warning_limit: 235 - cmake_options: all-options-abiv2-preview - warning_limit: 250 + warning_limit: 245 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index a7c886bc47..0b8eb7ef9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [CODE HEALTH] Move ext and exporters test classes into anonymous namespace + [#4225](https://github.com/open-telemetry/opentelemetry-cpp/pull/4225) + * [CODE HEALTH] Move api/test and sdk/test classes into anonymous namespace [#4217](https://github.com/open-telemetry/opentelemetry-cpp/pull/4217) diff --git a/exporters/memory/test/in_memory_metric_exporter_test.cc b/exporters/memory/test/in_memory_metric_exporter_test.cc index e0342d33a5..402f48cf35 100644 --- a/exporters/memory/test/in_memory_metric_exporter_test.cc +++ b/exporters/memory/test/in_memory_metric_exporter_test.cc @@ -24,6 +24,9 @@ using opentelemetry::sdk::metrics::ResourceMetrics; using opentelemetry::sdk::metrics::ScopeMetrics; using opentelemetry::sdk::resource::Resource; +namespace +{ + class InMemoryMetricExporterTest : public ::testing::Test { protected: @@ -62,3 +65,5 @@ TEST_F(InMemoryMetricExporterTest, TemporalitySelector) EXPECT_EQ(exporter_->GetAggregationTemporality(InstrumentType::kCounter), AggregationTemporality::kCumulative); } + +} // namespace diff --git a/exporters/ostream/test/ostream_span_test.cc b/exporters/ostream/test/ostream_span_test.cc index 10c8a55cd3..a734b0894f 100644 --- a/exporters/ostream/test/ostream_span_test.cc +++ b/exporters/ostream/test/ostream_span_test.cc @@ -43,6 +43,9 @@ namespace exportertrace = opentelemetry::exporter::trace; using Attributes = std::initializer_list>; +namespace +{ + class TestResource : public resource::Resource { public: @@ -411,3 +414,5 @@ TEST(OStreamSpanExporter, PrintSpanToClog) EXPECT_EQ(captured, kDefaultSpanPrinted); } + +} // namespace diff --git a/ext/test/http/url_parser_test.cc b/ext/test/http/url_parser_test.cc index 7c23e7dd2c..b4cd095ce0 100644 --- a/ext/test/http/url_parser_test.cc +++ b/ext/test/http/url_parser_test.cc @@ -12,6 +12,9 @@ namespace http_common = opentelemetry::ext::http::common; +namespace +{ + struct ParsedUrl { std::string scheme; @@ -138,3 +141,5 @@ TEST_P(UrlDecoderTests, BasicTests) EXPECT_EQ(actual, expected); } + +} // namespace From dc6135cafdd84af0ec3247d10688cd50e36ff331 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:23:53 +0200 Subject: [PATCH 28/49] Bump github/codeql-action/upload-sarif from 4.36.3 to 4.37.0 (#4227) Bumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...99df26d4f13ea111d4ec1a7dddef6063f76b97e9) --- updated-dependencies: - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index add463e933..dd42cd542e 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -47,6 +47,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 + uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 with: sarif_file: results.sarif From 2465d65768a2b5700b24f4d9343df9df7073520e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:19:20 +0200 Subject: [PATCH 29/49] Bump github/codeql-action/init from 4.36.3 to 4.37.0 (#4229) * Bump github/codeql-action/init from 4.36.3 to 4.37.0 Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...99df26d4f13ea111d4ec1a7dddef6063f76b97e9) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update .github/workflows/codeql-analysis.yml --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marc Alff --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 517a7a20cc..c7bae0d749 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: run: | sudo -E ./ci/install_thirdparty.sh --install-dir /usr/local --tags-file third_party_release --packages "ryml" - name: Initialize CodeQL - uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 + uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 with: languages: cpp config: | @@ -71,4 +71,4 @@ jobs: "${GITHUB_WORKSPACE}" cmake --build . --parallel - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 + uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 From 2aba50942b47118b64c8edacbfea64a59bd37e34 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Thu, 9 Jul 2026 23:38:42 +0200 Subject: [PATCH 30/49] [SEMANTIC CONVENTIONS] Upgrade to semconv 1.43.0 (#4230) --- .../opentelemetry/semconv/db_attributes.h | 27 ++++++- .../semconv/incubating/azure_attributes.h | 7 ++ .../semconv/incubating/cicd_metrics.h | 2 +- .../semconv/incubating/cloud_attributes.h | 2 + .../semconv/incubating/db_attributes.h | 27 ++++++- .../semconv/incubating/k8s_attributes.h | 70 +++++++++---------- .../semconv/incubating/network_attributes.h | 4 +- .../semconv/incubating/otel_metrics.h | 4 +- .../semconv/incubating/telemetry_attributes.h | 2 + .../opentelemetry/semconv/k8s_attributes.h | 70 +++++++++---------- .../opentelemetry/semconv/schema_url.h | 2 +- .../semconv/telemetry_attributes.h | 2 + buildscripts/semantic-convention/generate.sh | 4 +- 13 files changed, 141 insertions(+), 82 deletions(-) diff --git a/api/include/opentelemetry/semconv/db_attributes.h b/api/include/opentelemetry/semconv/db_attributes.h index f9fc8ed8ff..ceacc11b73 100644 --- a/api/include/opentelemetry/semconv/db_attributes.h +++ b/api/include/opentelemetry/semconv/db_attributes.h @@ -47,10 +47,31 @@ static constexpr const char *kDbCollectionName = "db.collection.name"; static constexpr const char *kDbNamespace = "db.namespace"; /** - The number of queries included in a batch operation. + The number of database operations included in a batch operation.

- Operations are only considered batches when they contain two or more operations, and so @code - db.operation.batch.size @endcode SHOULD never be @code 1 @endcode. + Except for empty batch requests described below, a batch operation contains two + or more database operations explicitly submitted as separate operations in a single + client call, protocol message, or database command. +

+ Requests to batch APIs that contain only one operation SHOULD be modeled as single + operations, not as batch operations. +

+ A database call is not a batch operation solely because one operation accepts + multiple operands, such as keys, rows, documents, points, or other data elements, + including Redis @code MGET @endcode with + multiple keys. +

+ In batch APIs that execute the same parameterized operation with parameter sets, + each parameter set represents one database operation for determining whether the + request is a batch operation. Requests with only one parameter set SHOULD be modeled + as single operations, not as batch operations. +

+ @code db.operation.batch.size @endcode SHOULD be set to the number of operations in the batch. + It SHOULD NOT be set for non-batch operations. +

+ A request to execute a batch operation with no operations SHOULD also be treated + as a batch operation, and @code db.operation.batch.size @endcode SHOULD be set to @code 0 + @endcode. */ static constexpr const char *kDbOperationBatchSize = "db.operation.batch.size"; diff --git a/api/include/opentelemetry/semconv/incubating/azure_attributes.h b/api/include/opentelemetry/semconv/incubating/azure_attributes.h index b8fa13674f..f42a2faf22 100644 --- a/api/include/opentelemetry/semconv/incubating/azure_attributes.h +++ b/api/include/opentelemetry/semconv/incubating/azure_attributes.h @@ -62,6 +62,13 @@ static constexpr const char *kAzureCosmosdbRequestBodySize = "azure.cosmosdb.req static constexpr const char *kAzureCosmosdbResponseSubStatusCode = "azure.cosmosdb.response.sub_status_code"; +/** + The name of the Azure resource + group the resource belongs to. + */ +static constexpr const char *kAzureResourceGroupName = "azure.resource_group.name"; + /** Azure diff --git a/api/include/opentelemetry/semconv/incubating/cicd_metrics.h b/api/include/opentelemetry/semconv/incubating/cicd_metrics.h index 86a9c09bbe..11ffef5f73 100644 --- a/api/include/opentelemetry/semconv/incubating/cicd_metrics.h +++ b/api/include/opentelemetry/semconv/incubating/cicd_metrics.h @@ -178,7 +178,7 @@ CreateAsyncDoubleMetricCicdSystemErrors(metrics::Meter *meter) static constexpr const char *kMetricCicdWorkerCount = "cicd.worker.count"; static constexpr const char *descrMetricCicdWorkerCount = "The number of workers on the CI/CD system by state."; -static constexpr const char *unitMetricCicdWorkerCount = "{count}"; +static constexpr const char *unitMetricCicdWorkerCount = "{worker}"; static inline nostd::unique_ptr> CreateSyncInt64MetricCicdWorkerCount(metrics::Meter *meter) diff --git a/api/include/opentelemetry/semconv/incubating/cloud_attributes.h b/api/include/opentelemetry/semconv/incubating/cloud_attributes.h index 80f22a3d8d..7c7bdfcb5c 100644 --- a/api/include/opentelemetry/semconv/incubating/cloud_attributes.h +++ b/api/include/opentelemetry/semconv/incubating/cloud_attributes.h @@ -21,6 +21,8 @@ namespace cloud /** The cloud account ID the resource is assigned to. +

+ For Azure, this is the subscription ID. */ static constexpr const char *kCloudAccountId = "cloud.account.id"; diff --git a/api/include/opentelemetry/semconv/incubating/db_attributes.h b/api/include/opentelemetry/semconv/incubating/db_attributes.h index 8be4d501e9..41f4b43cb5 100644 --- a/api/include/opentelemetry/semconv/incubating/db_attributes.h +++ b/api/include/opentelemetry/semconv/incubating/db_attributes.h @@ -344,10 +344,31 @@ static constexpr const char *kDbNamespace = "db.namespace"; OPENTELEMETRY_DEPRECATED static constexpr const char *kDbOperation = "db.operation"; /** - The number of queries included in a batch operation. + The number of database operations included in a batch operation.

- Operations are only considered batches when they contain two or more operations, and so @code - db.operation.batch.size @endcode SHOULD never be @code 1 @endcode. + Except for empty batch requests described below, a batch operation contains two + or more database operations explicitly submitted as separate operations in a single + client call, protocol message, or database command. +

+ Requests to batch APIs that contain only one operation SHOULD be modeled as single + operations, not as batch operations. +

+ A database call is not a batch operation solely because one operation accepts + multiple operands, such as keys, rows, documents, points, or other data elements, + including Redis @code MGET @endcode with + multiple keys. +

+ In batch APIs that execute the same parameterized operation with parameter sets, + each parameter set represents one database operation for determining whether the + request is a batch operation. Requests with only one parameter set SHOULD be modeled + as single operations, not as batch operations. +

+ @code db.operation.batch.size @endcode SHOULD be set to the number of operations in the batch. + It SHOULD NOT be set for non-batch operations. +

+ A request to execute a batch operation with no operations SHOULD also be treated + as a batch operation, and @code db.operation.batch.size @endcode SHOULD be set to @code 0 + @endcode. */ static constexpr const char *kDbOperationBatchSize = "db.operation.batch.size"; diff --git a/api/include/opentelemetry/semconv/incubating/k8s_attributes.h b/api/include/opentelemetry/semconv/incubating/k8s_attributes.h index 43dcdf61b9..98a0dca0b3 100644 --- a/api/include/opentelemetry/semconv/incubating/k8s_attributes.h +++ b/api/include/opentelemetry/semconv/incubating/k8s_attributes.h @@ -129,10 +129,10 @@ static constexpr const char *kK8sCronjobUid = "k8s.cronjob.uid"; /** The annotation placed on the DaemonSet, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

Examples:

  • A label @code - replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.daemonset.annotation.replicas @endcode attribute with value @code "1" @endcode.
  • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

    Examples:

    • An annotation + @code replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code + k8s.daemonset.annotation.replicas @endcode attribute with value @code "1" @endcode.
    • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.daemonset.annotation.data @endcode attribute with value @code "" @endcode.
    */ @@ -142,7 +142,7 @@ static constexpr const char *kK8sDaemonsetAnnotation = "k8s.daemonset.annotation The label placed on the DaemonSet, the @code @endcode being the label name, the value being the label value, even if the value is empty.

    Examples:

    • A label @code app @endcode with value @code guestbook @endcode SHOULD be recorded as the @code k8s.daemonset.label.app - @endcode attribute with value @code "guestbook" @endcode.
    • A label @code data @endcode + @endcode attribute with value @code "guestbook" @endcode.
    • A label @code injected @endcode with empty string value SHOULD be recorded as the @code k8s.daemonset.label.injected @endcode attribute with value @code "" @endcode.
    @@ -161,10 +161,10 @@ static constexpr const char *kK8sDaemonsetUid = "k8s.daemonset.uid"; /** The annotation placed on the Deployment, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

    Examples:

    • A label @code - replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.deployment.annotation.replicas @endcode attribute with value @code "1" @endcode.
    • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

      Examples:

      • An annotation + @code replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code + k8s.deployment.annotation.replicas @endcode attribute with value @code "1" @endcode.
      • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.deployment.annotation.data @endcode attribute with value @code "" @endcode.
      */ @@ -172,8 +172,8 @@ static constexpr const char *kK8sDeploymentAnnotation = "k8s.deployment.annotati /** The label placed on the Deployment, the @code @endcode being the label name, the value being - the label value, even if the value is empty.

      Examples:

      • A label @code replicas - @endcode with value @code 0 @endcode SHOULD be recorded as the @code k8s.deployment.label.app + the label value, even if the value is empty.

        Examples:

        • A label @code app @endcode + with value @code guestbook @endcode SHOULD be recorded as the @code k8s.deployment.label.app @endcode attribute with value @code "guestbook" @endcode.
        • A label @code injected @endcode with empty string value SHOULD be recorded as the @code k8s.deployment.label.injected @endcode attribute with value @code "" @endcode.
        • @@ -237,11 +237,11 @@ static constexpr const char *kK8sHugepageSize = "k8s.hugepage.size"; /** The annotation placed on the Job, the @code @endcode being the annotation name, the value - being the annotation value, even if the value is empty.

          Examples:

          • A label @code + being the annotation value, even if the value is empty.

            Examples:

            • An annotation @code number @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.job.annotation.number @endcode attribute with value @code "1" @endcode.
            • A label @code - data @endcode with empty string value SHOULD be recorded as the @code k8s.job.annotation.data - @endcode attribute with value @code "" @endcode.
            • + k8s.job.annotation.number @endcode attribute with value @code "1" @endcode.
            • An annotation + @code data @endcode with empty string value SHOULD be recorded as the @code + k8s.job.annotation.data @endcode attribute with value @code "" @endcode.
            */ static constexpr const char *kK8sJobAnnotation = "k8s.job.annotation"; @@ -250,9 +250,9 @@ static constexpr const char *kK8sJobAnnotation = "k8s.job.annotation"; The label placed on the Job, the @code @endcode being the label name, the value being the label value, even if the value is empty.

            Examples:

            • A label @code jobtype @endcode with value @code ci @endcode SHOULD be recorded as the @code k8s.job.label.jobtype @endcode - attribute with value @code "ci" @endcode.
            • A label @code data @endcode with empty string - value SHOULD be recorded as the @code k8s.job.label.automated @endcode attribute with value @code - "" @endcode.
            • + attribute with value @code "ci" @endcode.
            • A label @code automated @endcode with empty + string value SHOULD be recorded as the @code k8s.job.label.automated @endcode attribute with value + @code "" @endcode.
            */ static constexpr const char *kK8sJobLabel = "k8s.job.label"; @@ -269,10 +269,10 @@ static constexpr const char *kK8sJobUid = "k8s.job.uid"; /** The annotation placed on the Namespace, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

            Examples:

            • A label @code - ttl @endcode with value @code 0 @endcode SHOULD be recorded as the @code - k8s.namespace.annotation.ttl @endcode attribute with value @code "0" @endcode.
            • A label - @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

              Examples:

              • An annotation + @code ttl @endcode with value @code 0 @endcode SHOULD be recorded as the @code + k8s.namespace.annotation.ttl @endcode attribute with value @code "0" @endcode.
              • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.namespace.annotation.data @endcode attribute with value @code "" @endcode.
              */ @@ -564,10 +564,10 @@ static constexpr const char *kK8sPodUid = "k8s.pod.uid"; /** The annotation placed on the ReplicaSet, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

              Examples:

              • A label @code - replicas @endcode with value @code 0 @endcode SHOULD be recorded as the @code - k8s.replicaset.annotation.replicas @endcode attribute with value @code "0" @endcode.
              • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                Examples:

                • An annotation + @code replicas @endcode with value @code 0 @endcode SHOULD be recorded as the @code + k8s.replicaset.annotation.replicas @endcode attribute with value @code "0" @endcode.
                • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.replicaset.annotation.data @endcode attribute with value @code "" @endcode.
                */ @@ -748,10 +748,10 @@ static constexpr const char *kK8sServiceUid = "k8s.service.uid"; /** The annotation placed on the StatefulSet, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

                Examples:

                • A label @code - replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.statefulset.annotation.replicas @endcode attribute with value @code "1" @endcode.
                • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                  Examples:

                  • An annotation + @code replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code + k8s.statefulset.annotation.replicas @endcode attribute with value @code "1" @endcode.
                  • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.statefulset.annotation.data @endcode attribute with value @code "" @endcode.
                  */ @@ -759,11 +759,11 @@ static constexpr const char *kK8sStatefulsetAnnotation = "k8s.statefulset.annota /** The label placed on the StatefulSet, the @code @endcode being the label name, the value - being the label value, even if the value is empty.

                  Examples:

                  • A label @code replicas - @endcode with value @code 0 @endcode SHOULD be recorded as the @code k8s.statefulset.label.app - @endcode attribute with value @code "guestbook" @endcode.
                  • A label @code injected @endcode - with empty string value SHOULD be recorded as the @code k8s.statefulset.label.injected @endcode - attribute with value @code "" @endcode.
                  • + being the label value, even if the value is empty.

                    Examples:

                    • A label @code app + @endcode with value @code guestbook @endcode SHOULD be recorded as the @code + k8s.statefulset.label.app @endcode attribute with value @code "guestbook" @endcode.
                    • A + label @code injected @endcode with empty string value SHOULD be recorded as the @code + k8s.statefulset.label.injected @endcode attribute with value @code "" @endcode.
                    */ static constexpr const char *kK8sStatefulsetLabel = "k8s.statefulset.label"; diff --git a/api/include/opentelemetry/semconv/incubating/network_attributes.h b/api/include/opentelemetry/semconv/incubating/network_attributes.h index 34c6a880e1..0ec6e6f624 100644 --- a/api/include/opentelemetry/semconv/incubating/network_attributes.h +++ b/api/include/opentelemetry/semconv/incubating/network_attributes.h @@ -64,7 +64,9 @@ static constexpr const char *kNetworkConnectionType = "network.connection.type"; static constexpr const char *kNetworkInterfaceName = "network.interface.name"; /** - The network IO operation direction. + The direction of traffic from the perspective of the observing host's physical or virtual network + interface. It should not be used to represent the logical direction of a stateful connection or + network flow. */ static constexpr const char *kNetworkIoDirection = "network.io.direction"; diff --git a/api/include/opentelemetry/semconv/incubating/otel_metrics.h b/api/include/opentelemetry/semconv/incubating/otel_metrics.h index 093af10f53..fe0ef1591e 100644 --- a/api/include/opentelemetry/semconv/incubating/otel_metrics.h +++ b/api/include/opentelemetry/semconv/incubating/otel_metrics.h @@ -963,7 +963,9 @@ CreateAsyncDoubleMetricOtelSdkSpanEndedCount(metrics::Meter *meter) /** The number of created spans with @code recording=true @endcode for which the end operation has not - been called yet.

                    updowncounter + been called yet.

                    Non-recording spans are not counted, hence @code otel.span.sampling_result + @endcode can only take values @code RECORD_ONLY @endcode and @code RECORD_AND_SAMPLE @endcode, not + @code DROP @endcode.

                    updowncounter */ static constexpr const char *kMetricOtelSdkSpanLive = "otel.sdk.span.live"; static constexpr const char *descrMetricOtelSdkSpanLive = diff --git a/api/include/opentelemetry/semconv/incubating/telemetry_attributes.h b/api/include/opentelemetry/semconv/incubating/telemetry_attributes.h index 05ef72558a..87ca0ec518 100644 --- a/api/include/opentelemetry/semconv/incubating/telemetry_attributes.h +++ b/api/include/opentelemetry/semconv/incubating/telemetry_attributes.h @@ -69,6 +69,8 @@ static constexpr const char *kGo = "go"; static constexpr const char *kJava = "java"; +static constexpr const char *kKotlin = "kotlin"; + static constexpr const char *kNodejs = "nodejs"; static constexpr const char *kPhp = "php"; diff --git a/api/include/opentelemetry/semconv/k8s_attributes.h b/api/include/opentelemetry/semconv/k8s_attributes.h index 934e668e3e..9ec73f0621 100644 --- a/api/include/opentelemetry/semconv/k8s_attributes.h +++ b/api/include/opentelemetry/semconv/k8s_attributes.h @@ -98,10 +98,10 @@ static constexpr const char *kK8sCronjobUid = "k8s.cronjob.uid"; /** The annotation placed on the DaemonSet, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

                    Examples:

                    • A label @code - replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.daemonset.annotation.replicas @endcode attribute with value @code "1" @endcode.
                    • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                      Examples:

                      • An annotation + @code replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code + k8s.daemonset.annotation.replicas @endcode attribute with value @code "1" @endcode.
                      • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.daemonset.annotation.data @endcode attribute with value @code "" @endcode.
                      */ @@ -111,7 +111,7 @@ static constexpr const char *kK8sDaemonsetAnnotation = "k8s.daemonset.annotation The label placed on the DaemonSet, the @code @endcode being the label name, the value being the label value, even if the value is empty.

                      Examples:

                      • A label @code app @endcode with value @code guestbook @endcode SHOULD be recorded as the @code k8s.daemonset.label.app - @endcode attribute with value @code "guestbook" @endcode.
                      • A label @code data @endcode + @endcode attribute with value @code "guestbook" @endcode.
                      • A label @code injected @endcode with empty string value SHOULD be recorded as the @code k8s.daemonset.label.injected @endcode attribute with value @code "" @endcode.
                      @@ -130,10 +130,10 @@ static constexpr const char *kK8sDaemonsetUid = "k8s.daemonset.uid"; /** The annotation placed on the Deployment, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

                      Examples:

                      • A label @code - replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.deployment.annotation.replicas @endcode attribute with value @code "1" @endcode.
                      • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                        Examples:

                        • An annotation + @code replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code + k8s.deployment.annotation.replicas @endcode attribute with value @code "1" @endcode.
                        • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.deployment.annotation.data @endcode attribute with value @code "" @endcode.
                        */ @@ -141,8 +141,8 @@ static constexpr const char *kK8sDeploymentAnnotation = "k8s.deployment.annotati /** The label placed on the Deployment, the @code @endcode being the label name, the value being - the label value, even if the value is empty.

                        Examples:

                        • A label @code replicas - @endcode with value @code 0 @endcode SHOULD be recorded as the @code k8s.deployment.label.app + the label value, even if the value is empty.

                          Examples:

                          • A label @code app @endcode + with value @code guestbook @endcode SHOULD be recorded as the @code k8s.deployment.label.app @endcode attribute with value @code "guestbook" @endcode.
                          • A label @code injected @endcode with empty string value SHOULD be recorded as the @code k8s.deployment.label.injected @endcode attribute with value @code "" @endcode.
                          • @@ -162,11 +162,11 @@ static constexpr const char *kK8sDeploymentUid = "k8s.deployment.uid"; /** The annotation placed on the Job, the @code @endcode being the annotation name, the value - being the annotation value, even if the value is empty.

                            Examples:

                            • A label @code + being the annotation value, even if the value is empty.

                              Examples:

                              • An annotation @code number @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.job.annotation.number @endcode attribute with value @code "1" @endcode.
                              • A label @code - data @endcode with empty string value SHOULD be recorded as the @code k8s.job.annotation.data - @endcode attribute with value @code "" @endcode.
                              • + k8s.job.annotation.number @endcode attribute with value @code "1" @endcode.
                              • An annotation + @code data @endcode with empty string value SHOULD be recorded as the @code + k8s.job.annotation.data @endcode attribute with value @code "" @endcode.
                              */ static constexpr const char *kK8sJobAnnotation = "k8s.job.annotation"; @@ -175,9 +175,9 @@ static constexpr const char *kK8sJobAnnotation = "k8s.job.annotation"; The label placed on the Job, the @code @endcode being the label name, the value being the label value, even if the value is empty.

                              Examples:

                              • A label @code jobtype @endcode with value @code ci @endcode SHOULD be recorded as the @code k8s.job.label.jobtype @endcode - attribute with value @code "ci" @endcode.
                              • A label @code data @endcode with empty string - value SHOULD be recorded as the @code k8s.job.label.automated @endcode attribute with value @code - "" @endcode.
                              • + attribute with value @code "ci" @endcode.
                              • A label @code automated @endcode with empty + string value SHOULD be recorded as the @code k8s.job.label.automated @endcode attribute with value + @code "" @endcode.
                              */ static constexpr const char *kK8sJobLabel = "k8s.job.label"; @@ -194,10 +194,10 @@ static constexpr const char *kK8sJobUid = "k8s.job.uid"; /** The annotation placed on the Namespace, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

                              Examples:

                              • A label @code - ttl @endcode with value @code 0 @endcode SHOULD be recorded as the @code - k8s.namespace.annotation.ttl @endcode attribute with value @code "0" @endcode.
                              • A label - @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                                Examples:

                                • An annotation + @code ttl @endcode with value @code 0 @endcode SHOULD be recorded as the @code + k8s.namespace.annotation.ttl @endcode attribute with value @code "0" @endcode.
                                • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.namespace.annotation.data @endcode attribute with value @code "" @endcode.
                                */ @@ -326,10 +326,10 @@ static constexpr const char *kK8sPodUid = "k8s.pod.uid"; /** The annotation placed on the ReplicaSet, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

                                Examples:

                                • A label @code - replicas @endcode with value @code 0 @endcode SHOULD be recorded as the @code - k8s.replicaset.annotation.replicas @endcode attribute with value @code "0" @endcode.
                                • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                                  Examples:

                                  • An annotation + @code replicas @endcode with value @code 0 @endcode SHOULD be recorded as the @code + k8s.replicaset.annotation.replicas @endcode attribute with value @code "0" @endcode.
                                  • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.replicaset.annotation.data @endcode attribute with value @code "" @endcode.
                                  */ @@ -358,10 +358,10 @@ static constexpr const char *kK8sReplicasetUid = "k8s.replicaset.uid"; /** The annotation placed on the StatefulSet, the @code @endcode being the annotation name, the - value being the annotation value, even if the value is empty.

                                  Examples:

                                  • A label @code - replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code - k8s.statefulset.annotation.replicas @endcode attribute with value @code "1" @endcode.
                                  • A - label @code data @endcode with empty string value SHOULD be recorded as the @code + value being the annotation value, even if the value is empty.

                                    Examples:

                                    • An annotation + @code replicas @endcode with value @code 1 @endcode SHOULD be recorded as the @code + k8s.statefulset.annotation.replicas @endcode attribute with value @code "1" @endcode.
                                    • An + annotation @code data @endcode with empty string value SHOULD be recorded as the @code k8s.statefulset.annotation.data @endcode attribute with value @code "" @endcode.
                                    */ @@ -369,11 +369,11 @@ static constexpr const char *kK8sStatefulsetAnnotation = "k8s.statefulset.annota /** The label placed on the StatefulSet, the @code @endcode being the label name, the value - being the label value, even if the value is empty.

                                    Examples:

                                    • A label @code replicas - @endcode with value @code 0 @endcode SHOULD be recorded as the @code k8s.statefulset.label.app - @endcode attribute with value @code "guestbook" @endcode.
                                    • A label @code injected @endcode - with empty string value SHOULD be recorded as the @code k8s.statefulset.label.injected @endcode - attribute with value @code "" @endcode.
                                    • + being the label value, even if the value is empty.

                                      Examples:

                                      • A label @code app + @endcode with value @code guestbook @endcode SHOULD be recorded as the @code + k8s.statefulset.label.app @endcode attribute with value @code "guestbook" @endcode.
                                      • A + label @code injected @endcode with empty string value SHOULD be recorded as the @code + k8s.statefulset.label.injected @endcode attribute with value @code "" @endcode.
                                      */ static constexpr const char *kK8sStatefulsetLabel = "k8s.statefulset.label"; diff --git a/api/include/opentelemetry/semconv/schema_url.h b/api/include/opentelemetry/semconv/schema_url.h index f0285a5057..d173828de5 100644 --- a/api/include/opentelemetry/semconv/schema_url.h +++ b/api/include/opentelemetry/semconv/schema_url.h @@ -19,6 +19,6 @@ namespace semconv /** * The URL of the OpenTelemetry schema for these keys and values. */ -static constexpr const char *kSchemaUrl = "https://opentelemetry.io/schemas/1.42.0"; +static constexpr const char *kSchemaUrl = "https://opentelemetry.io/schemas/1.43.0"; } // namespace semconv OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/semconv/telemetry_attributes.h b/api/include/opentelemetry/semconv/telemetry_attributes.h index 05ef72558a..87ca0ec518 100644 --- a/api/include/opentelemetry/semconv/telemetry_attributes.h +++ b/api/include/opentelemetry/semconv/telemetry_attributes.h @@ -69,6 +69,8 @@ static constexpr const char *kGo = "go"; static constexpr const char *kJava = "java"; +static constexpr const char *kKotlin = "kotlin"; + static constexpr const char *kNodejs = "nodejs"; static constexpr const char *kPhp = "php"; diff --git a/buildscripts/semantic-convention/generate.sh b/buildscripts/semantic-convention/generate.sh index 4c93eb08a3..7caa66af92 100755 --- a/buildscripts/semantic-convention/generate.sh +++ b/buildscripts/semantic-convention/generate.sh @@ -16,10 +16,10 @@ ROOT_DIR="${SCRIPT_DIR}/../../" # freeze the spec & generator tools versions to make the generation reproducible # repository: https://github.com/open-telemetry/semantic-conventions -SEMCONV_VERSION=1.42.0 +SEMCONV_VERSION=1.43.0 # repository: https://github.com/open-telemetry/weaver -WEAVER_VERSION=0.23.0 +WEAVER_VERSION=0.24.2 SEMCONV_VERSION_TAG=v$SEMCONV_VERSION WEAVER_VERSION_TAG=v$WEAVER_VERSION From 32f6f3afdb058a9a9bf828fe4cc40a6eb1db85f9 Mon Sep 17 00:00:00 2001 From: Mateen Anjum Date: Thu, 9 Jul 2026 20:13:33 -0400 Subject: [PATCH 31/49] [CODE HEALTH] Move otlp, zipkin and prometheus test classes into anonymous namespace (#4231) --- .github/workflows/clang-tidy.yaml | 4 ++-- CHANGELOG.md | 3 +++ exporters/otlp/test/otlp_file_client_test.cc | 3 +++ exporters/otlp/test/otlp_file_exporter_test.cc | 6 ++++++ exporters/otlp/test/otlp_file_log_record_exporter_test.cc | 6 ++++++ exporters/otlp/test/otlp_file_metric_exporter_test.cc | 3 +++ exporters/otlp/test/otlp_grpc_exporter_test.cc | 6 ++++++ exporters/otlp/test/otlp_http_exporter_test.cc | 3 +++ exporters/otlp/test/otlp_log_recordable_test.cc | 3 +++ exporters/otlp/test/otlp_recordable_test.cc | 6 ++++++ exporters/prometheus/test/collector_test.cc | 6 ++++++ exporters/prometheus/test/exporter_utils_test.cc | 6 ++++++ exporters/zipkin/test/zipkin_exporter_test.cc | 6 ++++++ exporters/zipkin/test/zipkin_recordable_test.cc | 3 +++ 14 files changed, 62 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 03dd195aac..84300776f8 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 235 + warning_limit: 137 - cmake_options: all-options-abiv2-preview - warning_limit: 245 + warning_limit: 145 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8eb7ef9e..456c9439e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [CODE HEALTH] Move otlp, zipkin and prometheus test classes into anonymous namespace + [#4231](https://github.com/open-telemetry/opentelemetry-cpp/pull/4231) + * [CODE HEALTH] Move ext and exporters test classes into anonymous namespace [#4225](https://github.com/open-telemetry/opentelemetry-cpp/pull/4225) diff --git a/exporters/otlp/test/otlp_file_client_test.cc b/exporters/otlp/test/otlp_file_client_test.cc index 6260559720..0a7e1e84dc 100644 --- a/exporters/otlp/test/otlp_file_client_test.cc +++ b/exporters/otlp/test/otlp_file_client_test.cc @@ -57,6 +57,8 @@ namespace otlp namespace resource = opentelemetry::sdk::resource; +namespace +{ class ProtobufGlobalSymbolGuard { public: @@ -67,6 +69,7 @@ class ProtobufGlobalSymbolGuard ProtobufGlobalSymbolGuard(ProtobufGlobalSymbolGuard &&) = delete; ProtobufGlobalSymbolGuard &operator=(ProtobufGlobalSymbolGuard &&) = delete; }; +} // namespace static std::tm GetLocalTime(std::chrono::system_clock::time_point tp) { diff --git a/exporters/otlp/test/otlp_file_exporter_test.cc b/exporters/otlp/test/otlp_file_exporter_test.cc index 64d4c4d708..fe38517aed 100644 --- a/exporters/otlp/test/otlp_file_exporter_test.cc +++ b/exporters/otlp/test/otlp_file_exporter_test.cc @@ -59,6 +59,8 @@ namespace otlp namespace trace_api = opentelemetry::trace; namespace resource = opentelemetry::sdk::resource; +namespace +{ class ProtobufGlobalSymbolGuard { public: @@ -69,6 +71,7 @@ class ProtobufGlobalSymbolGuard ProtobufGlobalSymbolGuard(ProtobufGlobalSymbolGuard &&) = delete; ProtobufGlobalSymbolGuard &operator=(ProtobufGlobalSymbolGuard &&) = delete; }; +} // namespace template static nostd::span MakeSpan(T (&array)[N]) @@ -76,6 +79,8 @@ static nostd::span MakeSpan(T (&array)[N]) return nostd::span(array); } +namespace +{ class OtlpFileExporterTestPeer : public ::testing::Test { public: @@ -183,6 +188,7 @@ class OtlpFileExporterTestPeer : public ::testing::Test } } }; +} // namespace TEST(OtlpFileExporterTest, Shutdown) { diff --git a/exporters/otlp/test/otlp_file_log_record_exporter_test.cc b/exporters/otlp/test/otlp_file_log_record_exporter_test.cc index 2d96328d79..75a6697f82 100644 --- a/exporters/otlp/test/otlp_file_log_record_exporter_test.cc +++ b/exporters/otlp/test/otlp_file_log_record_exporter_test.cc @@ -45,6 +45,8 @@ namespace exporter namespace otlp { +namespace +{ class ProtobufGlobalSymbolGuard { public: @@ -55,6 +57,7 @@ class ProtobufGlobalSymbolGuard ProtobufGlobalSymbolGuard(ProtobufGlobalSymbolGuard &&) = delete; ProtobufGlobalSymbolGuard &operator=(ProtobufGlobalSymbolGuard &&) = delete; }; +} // namespace template static nostd::span MakeSpan(T (&array)[N]) @@ -62,6 +65,8 @@ static nostd::span MakeSpan(T (&array)[N]) return nostd::span(array); } +namespace +{ class OtlpFileLogRecordExporterTestPeer : public ::testing::Test { public: @@ -168,6 +173,7 @@ class OtlpFileLogRecordExporterTestPeer : public ::testing::Test } } }; +} // namespace TEST(OtlpFileLogRecordExporterTest, Shutdown) { diff --git a/exporters/otlp/test/otlp_file_metric_exporter_test.cc b/exporters/otlp/test/otlp_file_metric_exporter_test.cc index 98b819b08b..bc6138c9c1 100644 --- a/exporters/otlp/test/otlp_file_metric_exporter_test.cc +++ b/exporters/otlp/test/otlp_file_metric_exporter_test.cc @@ -43,6 +43,8 @@ namespace exporter namespace otlp { +namespace +{ class ProtobufGlobalSymbolGuard { public: @@ -53,6 +55,7 @@ class ProtobufGlobalSymbolGuard ProtobufGlobalSymbolGuard(ProtobufGlobalSymbolGuard &&) = delete; ProtobufGlobalSymbolGuard &operator=(ProtobufGlobalSymbolGuard &&) = delete; }; +} // namespace template static IntegerType JsonToInteger(const nlohmann::json &value) diff --git a/exporters/otlp/test/otlp_grpc_exporter_test.cc b/exporters/otlp/test/otlp_grpc_exporter_test.cc index 7525fc2109..239f788c49 100644 --- a/exporters/otlp/test/otlp_grpc_exporter_test.cc +++ b/exporters/otlp/test/otlp_grpc_exporter_test.cc @@ -453,6 +453,8 @@ TEST_F(OtlpGrpcExporterTestPeer, ConfigRetryGenericValuesFromEnv) # endif // NO_GETENV # ifdef ENABLE_OTLP_RETRY_PREVIEW +namespace +{ struct TestTraceService : public opentelemetry::proto::collector::trace::v1::TraceService::Service { TestTraceService(const std::vector &status_codes) : status_codes_(status_codes) @@ -471,12 +473,16 @@ struct TestTraceService : public opentelemetry::proto::collector::trace::v1::Tra size_t index_ = 0UL; std::vector status_codes_; }; +} // namespace using StatusCodeVector = std::vector; +namespace +{ class OtlpGrpcExporterRetryIntegrationTests : public ::testing::TestWithParam> {}; +} // namespace INSTANTIATE_TEST_SUITE_P( StatusCodes, diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index b7a346c866..a77a5fe590 100644 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -777,9 +777,12 @@ TEST_F(OtlpHttpExporterTestPeer, ConfigRetryGenericValuesFromEnv) # ifdef ENABLE_OTLP_RETRY_PREVIEW using StatusCodeVector = std::vector; +namespace +{ class OtlpHttpExporterRetryIntegrationTests : public ::testing::TestWithParam> {}; +} // namespace INSTANTIATE_TEST_SUITE_P(StatusCodes, OtlpHttpExporterRetryIntegrationTests, diff --git a/exporters/otlp/test/otlp_log_recordable_test.cc b/exporters/otlp/test/otlp_log_recordable_test.cc index d471c86414..3801afe043 100644 --- a/exporters/otlp/test/otlp_log_recordable_test.cc +++ b/exporters/otlp/test/otlp_log_recordable_test.cc @@ -254,11 +254,14 @@ TEST(OtlpLogRecordable, SetEventName) * unsigned int, and uint64_t. To avoid writing test cases for each, we can * use a template approach to test all int types. */ +namespace +{ template struct OtlpLogRecordableIntAttributeTest : public testing::Test { using IntParamType = T; }; +} // namespace using IntTypes = testing::Types; TYPED_TEST_SUITE(OtlpLogRecordableIntAttributeTest, IntTypes); diff --git a/exporters/otlp/test/otlp_recordable_test.cc b/exporters/otlp/test/otlp_recordable_test.cc index 56c4015fe2..1645b61b5f 100644 --- a/exporters/otlp/test/otlp_recordable_test.cc +++ b/exporters/otlp/test/otlp_recordable_test.cc @@ -548,11 +548,14 @@ TEST(OtlpRecordable, PopulateRequestMissing) } } +namespace +{ template struct EmptyArrayAttributeTest : public testing::Test { using ElementType = T; }; +} // namespace using ArrayElementTypes = testing::Types; @@ -576,11 +579,14 @@ TYPED_TEST(EmptyArrayAttributeTest, SetEmptyArrayAttribute) * unsigned int, and uint64_t. To avoid writing test cases for each, we can * use a template approach to test all int types. */ +namespace +{ template struct IntAttributeTest : public testing::Test { using IntParamType = T; }; +} // namespace using IntTypes = testing::Types; TYPED_TEST_SUITE(IntAttributeTest, IntTypes); diff --git a/exporters/prometheus/test/collector_test.cc b/exporters/prometheus/test/collector_test.cc index 20c3070cec..e333c22ff9 100644 --- a/exporters/prometheus/test/collector_test.cc +++ b/exporters/prometheus/test/collector_test.cc @@ -20,6 +20,8 @@ using opentelemetry::exporter::metrics::PrometheusCollector; using opentelemetry::sdk::metrics::MetricProducer; using opentelemetry::sdk::metrics::ResourceMetrics; +namespace +{ class MockMetricProducer : public MetricProducer { TestDataPoints test_data_points_; @@ -43,7 +45,10 @@ class MockMetricProducer : public MetricProducer std::chrono::microseconds sleep_ms_; size_t data_sent_size_{0}; }; +} // namespace +namespace +{ class MockMetricReader : public opentelemetry::sdk::metrics::MetricReader { public: @@ -61,6 +66,7 @@ class MockMetricReader : public opentelemetry::sdk::metrics::MetricReader void OnInitialized() noexcept override {} }; +} // namespace // ==================== Test for addMetricsData() function ====================== diff --git a/exporters/prometheus/test/exporter_utils_test.cc b/exporters/prometheus/test/exporter_utils_test.cc index e43b930331..92ba79eba9 100644 --- a/exporters/prometheus/test/exporter_utils_test.cc +++ b/exporters/prometheus/test/exporter_utils_test.cc @@ -286,6 +286,8 @@ TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramNormal) ASSERT_EQ(checked_label_num, 3); } +namespace +{ class SanitizeTest : public ::testing::Test { Resource resource_ = Resource::Create({}); @@ -308,6 +310,7 @@ class SanitizeTest : public ::testing::Test EXPECT_EQ(result.begin()->metric.begin()->label.begin()->name, sanitized); } }; +} // namespace TEST_F(SanitizeTest, Label) { @@ -321,6 +324,8 @@ TEST_F(SanitizeTest, Label) TEST_F(SanitizeTest, Name) {} +namespace +{ class AttributeCollisionTest : public ::testing::Test { Resource resource_ = Resource::Create(ResourceAttributes{}); @@ -356,6 +361,7 @@ class AttributeCollisionTest : public ::testing::Test } } }; +} // namespace TEST_F(AttributeCollisionTest, SeparatesDistinctKeys) { diff --git a/exporters/zipkin/test/zipkin_exporter_test.cc b/exporters/zipkin/test/zipkin_exporter_test.cc index 086357c07f..a15871cc89 100644 --- a/exporters/zipkin/test/zipkin_exporter_test.cc +++ b/exporters/zipkin/test/zipkin_exporter_test.cc @@ -60,6 +60,8 @@ class ZipkinExporterTestPeer : public ::testing::Test } }; +namespace +{ class MockHttpClient : public opentelemetry::ext::http::client::HttpClientSync { public: @@ -80,7 +82,10 @@ class MockHttpClient : public opentelemetry::ext::http::client::HttpClientSync const ext::http::client::Compression &), (noexcept, override)); }; +} // namespace +namespace +{ class IsValidMessageMatcher { public: @@ -102,6 +107,7 @@ class IsValidMessageMatcher private: std::string trace_id_; }; +} // namespace static PolymorphicMatcher IsValidMessage(const std::string &trace_id) { diff --git a/exporters/zipkin/test/zipkin_recordable_test.cc b/exporters/zipkin/test/zipkin_recordable_test.cc index d4b3cd4b53..df448d9e01 100644 --- a/exporters/zipkin/test/zipkin_recordable_test.cc +++ b/exporters/zipkin/test/zipkin_recordable_test.cc @@ -268,11 +268,14 @@ TEST(ZipkinSpanRecordable, SetResource) * unsigned int, and uint64_t. To avoid writing test cases for each, we can * use a template approach to test all int types. */ +namespace +{ template struct ZipkinIntAttributeTest : public testing::Test { using IntParamType = T; }; +} // namespace using IntTypes = testing::Types; TYPED_TEST_SUITE(ZipkinIntAttributeTest, IntTypes); From 2d65583ad287d6a2b46447f01a3ba2a01eaf99a8 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Fri, 10 Jul 2026 05:57:39 +0200 Subject: [PATCH 32/49] [BAZEL] Upgrade to rapidyaml 1.15.2 (#4234) --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 31994c3e00..f02a516052 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -18,7 +18,7 @@ bazel_dep(name = "opentracing-cpp", version = "1.6.0", repo_name = "com_github_o bazel_dep(name = "platforms", version = "1.1.0") bazel_dep(name = "prometheus-cpp", version = "1.3.0.bcr.2", repo_name = "com_github_jupp0r_prometheus_cpp") bazel_dep(name = "protobuf", version = "32.0", repo_name = "com_google_protobuf") -bazel_dep(name = "rapidyaml", version = "0.12.1") +bazel_dep(name = "rapidyaml", version = "0.15.2") bazel_dep(name = "rules_cc", version = "0.2.22") bazel_dep(name = "rules_foreign_cc", version = "0.15.1") bazel_dep(name = "rules_proto", version = "7.1.0") From 4601455187b1a86aff2ce8bcd829dba068e18271 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:34:34 -0400 Subject: [PATCH 33/49] [BENCHMARK] add SpanData recordable benchmark and unify common utils (#4203) --- exporters/otlp/BUILD | 1 + exporters/otlp/CMakeLists.txt | 8 +- .../otlp/test/otlp_recordable_benchmark.cc | 323 +++++++++--------- sdk/src/trace/span_data.cc | 6 +- sdk/test/trace/BUILD | 16 + sdk/test/trace/CMakeLists.txt | 10 + sdk/test/trace/span_data_benchmark.cc | 213 ++++++++++++ .../test_common/sdk/trace/test_utils.h | 228 +++++++++++++ 8 files changed, 630 insertions(+), 175 deletions(-) create mode 100644 sdk/test/trace/span_data_benchmark.cc create mode 100644 test_common/include/opentelemetry/test_common/sdk/trace/test_utils.h diff --git a/exporters/otlp/BUILD b/exporters/otlp/BUILD index b01fac5674..f756afa698 100644 --- a/exporters/otlp/BUILD +++ b/exporters/otlp/BUILD @@ -1041,6 +1041,7 @@ otel_cc_benchmark( "//sdk/src/metrics", "//sdk/src/resource", "//sdk/src/trace", + "//test_common:headers", "@com_github_opentelemetry_proto//:trace_service_proto_cc", ], ) diff --git a/exporters/otlp/CMakeLists.txt b/exporters/otlp/CMakeLists.txt index 89e229ca4a..eef251a1de 100644 --- a/exporters/otlp/CMakeLists.txt +++ b/exporters/otlp/CMakeLists.txt @@ -1225,7 +1225,11 @@ if(WITH_BENCHMARK) target_link_libraries( otlp_recordable_benchmark - PRIVATE opentelemetry_otlp_recordable opentelemetry_trace - opentelemetry_metrics opentelemetry_resources benchmark::benchmark + PRIVATE opentelemetry_otlp_recordable + opentelemetry_trace + opentelemetry_metrics + opentelemetry_resources + benchmark::benchmark + opentelemetry_test_common ${CMAKE_THREAD_LIBS_INIT}) endif() diff --git a/exporters/otlp/test/otlp_recordable_benchmark.cc b/exporters/otlp/test/otlp_recordable_benchmark.cc index 6c672c9217..2d5202d71d 100644 --- a/exporters/otlp/test/otlp_recordable_benchmark.cc +++ b/exporters/otlp/test/otlp_recordable_benchmark.cc @@ -3,38 +3,72 @@ // clang-format off // -// 2026-06-18T21:05:11+00:00 +// ~/build/exporters/otlp/otlp_recordable_benchmark --benchmark_repetitions=5 --benchmark_display_aggregates_only=true +// 2026-07-01T15:57:22+00:00 // Running /home/devuser/build/exporters/otlp/otlp_recordable_benchmark -// Run on (32 X 5175.97 MHz CPU s) +// Run on (32 X 5700 MHz CPU s) // CPU Caches: // L1 Data 48 KiB (x16) // L1 Instruction 32 KiB (x16) // L2 Unified 2048 KiB (x16) // L3 Unified 36864 KiB (x1) -// Load Average: 0.61, 1.30, 1.33 -// ----------------------------------------------------------------------------------- -// Benchmark Time CPU Iterations -// ----------------------------------------------------------------------------------- -// OtlpSpanFixture/Minimal_mean 355 ns 357 ns 5 -// OtlpSpanFixture/Minimal_median 355 ns 358 ns 5 -// OtlpSpanFixture/Minimal_stddev 0.489 ns 0.790 ns 5 -// OtlpSpanFixture/Minimal_cv 0.14 % 0.22 % 5 -// OtlpSpanFixture/Nominal_mean 617 ns 619 ns 5 -// OtlpSpanFixture/Nominal_median 617 ns 620 ns 5 -// OtlpSpanFixture/Nominal_stddev 0.960 ns 1.25 ns 5 -// OtlpSpanFixture/Nominal_cv 0.16 % 0.20 % 5 -// OtlpSpanFixture/MaxAttributes_mean 8014 ns 8016 ns 5 items_per_second=16.0018M/s -// OtlpSpanFixture/MaxAttributes_median 7737 ns 7739 ns 5 items_per_second=16.5389M/s -// OtlpSpanFixture/MaxAttributes_stddev 419 ns 418 ns 5 items_per_second=819.876k/s -// OtlpSpanFixture/MaxAttributes_cv 5.23 % 5.22 % 5 items_per_second=5.12% -// BM_PopulateRequest/span_count:1_mean 0.398 us 0.398 us 5 items_per_second=2.51517M/s -// BM_PopulateRequest/span_count:1_median 0.397 us 0.397 us 5 items_per_second=2.52167M/s -// BM_PopulateRequest/span_count:1_stddev 0.002 us 0.002 us 5 items_per_second=12.1068k/s -// BM_PopulateRequest/span_count:1_cv 0.48 % 0.48 % 5 items_per_second=0.48% -// BM_PopulateRequest/span_count:512_mean 26.3 us 26.3 us 5 items_per_second=19.4714M/s -// BM_PopulateRequest/span_count:512_median 26.3 us 26.3 us 5 items_per_second=19.4746M/s -// BM_PopulateRequest/span_count:512_stddev 0.078 us 0.077 us 5 items_per_second=56.7323k/s -// BM_PopulateRequest/span_count:512_cv 0.30 % 0.29 % 5 items_per_second=0.29% +// Load Average: 2.05, 1.65, 1.67 +// ***WARNING*** ASLR is enabled, the results may have unreproducible noise in them. +// -------------------------------------------------------------------------------------------------------------------- +// Benchmark Time CPU Iterations +// -------------------------------------------------------------------------------------------------------------------- +// OtlpRecordableFixture/RecordMinimalSpan_mean 362 ns 364 ns 5 +// OtlpRecordableFixture/RecordMinimalSpan_median 363 ns 365 ns 5 +// OtlpRecordableFixture/RecordMinimalSpan_stddev 1.77 ns 1.97 ns 5 +// OtlpRecordableFixture/RecordMinimalSpan_cv 0.49 % 0.54 % 5 +// OtlpRecordableFixture/RecordNominalSpan_mean 638 ns 639 ns 5 +// OtlpRecordableFixture/RecordNominalSpan_median 636 ns 637 ns 5 +// OtlpRecordableFixture/RecordNominalSpan_stddev 8.01 ns 8.04 ns 5 +// OtlpRecordableFixture/RecordNominalSpan_cv 1.26 % 1.26 % 5 +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_mean 414 ns 416 ns 5 items_per_second=2.40287M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_median 414 ns 416 ns 5 items_per_second=2.40294M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_stddev 1.94 ns 2.14 ns 5 items_per_second=12.3138k/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:1_cv 0.47 % 0.51 % 5 items_per_second=0.51% +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_mean 880 ns 881 ns 5 items_per_second=11.3516M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_median 883 ns 883 ns 5 items_per_second=11.3193M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_stddev 12.8 ns 13.0 ns 5 items_per_second=167.444k/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:10_cv 1.46 % 1.47 % 5 items_per_second=1.48% +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_mean 7293 ns 7297 ns 5 items_per_second=17.7189M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_median 6834 ns 6839 ns 5 items_per_second=18.7175M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_stddev 842 ns 841 ns 5 items_per_second=1.922M/s +// OtlpRecordableFixture/RecordSpanWithAttributes/attribute_count:128_cv 11.55 % 11.53 % 5 items_per_second=10.85% +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_mean 502 ns 503 ns 5 items_per_second=1.98937M/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_median 501 ns 503 ns 5 items_per_second=1.99001M/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_stddev 1.04 ns 0.917 ns 5 items_per_second=3.63084k/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:1_cv 0.21 % 0.18 % 5 items_per_second=0.18% +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_mean 1705 ns 1707 ns 5 items_per_second=5.86103M/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_median 1699 ns 1700 ns 5 items_per_second=5.88169M/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_stddev 29.0 ns 29.0 ns 5 items_per_second=99.4244k/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:10_cv 1.70 % 1.70 % 5 items_per_second=1.70% +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_mean 16750 ns 16750 ns 5 items_per_second=7.64354M/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_median 16757 ns 16756 ns 5 items_per_second=7.63925M/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_stddev 287 ns 287 ns 5 items_per_second=130.651k/s +// OtlpRecordableFixture/RecordSpanWithEvents/event_count:128_cv 1.71 % 1.71 % 5 items_per_second=1.71% +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_mean 511 ns 513 ns 5 items_per_second=1.94877M/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_median 510 ns 512 ns 5 items_per_second=1.95458M/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_stddev 5.76 ns 6.17 ns 5 items_per_second=23.304k/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:1_cv 1.13 % 1.20 % 5 items_per_second=1.20% +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_mean 1784 ns 1786 ns 5 items_per_second=5.60068M/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_median 1785 ns 1787 ns 5 items_per_second=5.59477M/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_stddev 6.08 ns 6.23 ns 5 items_per_second=19.5651k/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:10_cv 0.34 % 0.35 % 5 items_per_second=0.35% +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_mean 19515 ns 19517 ns 5 items_per_second=6.62769M/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_median 18663 ns 18666 ns 5 items_per_second=6.85722M/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_stddev 2403 ns 2403 ns 5 items_per_second=703.536k/s +// OtlpRecordableFixture/RecordSpanWithLinks/link_count:128_cv 12.31 % 12.31 % 5 items_per_second=10.62% +// BM_OtlpPopulateRequest/span_count:1_mean 0.387 us 0.387 us 5 items_per_second=2.58274M/s +// BM_OtlpPopulateRequest/span_count:1_median 0.387 us 0.387 us 5 items_per_second=2.58201M/s +// BM_OtlpPopulateRequest/span_count:1_stddev 0.004 us 0.004 us 5 items_per_second=23.6738k/s +// BM_OtlpPopulateRequest/span_count:1_cv 0.92 % 0.92 % 5 items_per_second=0.92% +// BM_OtlpPopulateRequest/span_count:512_mean 23.6 us 23.6 us 5 items_per_second=21.7343M/s +// BM_OtlpPopulateRequest/span_count:512_median 23.5 us 23.5 us 5 items_per_second=21.7479M/s +// BM_OtlpPopulateRequest/span_count:512_stddev 0.260 us 0.259 us 5 items_per_second=238.588k/s +// BM_OtlpPopulateRequest/span_count:512_cv 1.10 % 1.10 % 5 items_per_second=1.10% // // clang-format on @@ -47,7 +81,6 @@ #include #include -#include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/timestamp.h" #include "opentelemetry/exporters/otlp/otlp_recordable.h" #include "opentelemetry/exporters/otlp/otlp_recordable_utils.h" @@ -55,21 +88,15 @@ #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/nostd/utility.h" #include "opentelemetry/sdk/common/exporter_utils.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" -#include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/exporter.h" -#include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/recordable.h" #include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/test_common/sdk/trace/test_utils.h" #include "opentelemetry/trace/span.h" -#include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" -#include "opentelemetry/trace/trace_flags.h" -#include "opentelemetry/trace/trace_id.h" #include "opentelemetry/trace/tracer.h" // clang-format off @@ -79,9 +106,10 @@ #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" // IWYU pragma: keep // clang-format on -namespace otlp = opentelemetry::exporter::otlp; -namespace trace_sdk = opentelemetry::sdk::trace; -namespace trace_api = opentelemetry::trace; +namespace otlp = opentelemetry::exporter::otlp; +namespace trace_sdk = opentelemetry::sdk::trace; +namespace trace_api = opentelemetry::trace; +namespace test_utils = opentelemetry::test_common; namespace { @@ -107,69 +135,22 @@ class NullSpanExporter final : public trace_sdk::SpanExporter bool Shutdown(std::chrono::microseconds) noexcept override { return true; } }; -// A span processor that queues completed recordables into an internal buffer -// without serialising or destroying them mirroring the batch processor. -class BufferingProcessor final : public trace_sdk::SpanProcessor -{ -public: - using Buffer = std::vector>; - - explicit BufferingProcessor(std::unique_ptr exporter) - : exporter_(std::move(exporter)) - {} - - std::unique_ptr MakeRecordable() noexcept override - { - return exporter_->MakeRecordable(); - } - - void OnStart(trace_sdk::Recordable &, const opentelemetry::trace::SpanContext &) noexcept override - {} - - void OnEnd(std::unique_ptr &&span) noexcept override - { - buffer_.push_back(std::move(span)); - } - - bool ForceFlush(std::chrono::microseconds) noexcept override - { - buffer_.clear(); - return true; - } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } - -private: - std::unique_ptr exporter_; - Buffer buffer_; -}; - -const opentelemetry::sdk::resource::Resource &TestResource() -{ - static const auto resource = opentelemetry::sdk::resource::Resource::Create( - {{"service.name", opentelemetry::nostd::string_view{"benchmark_service"}}, - {"service.version", opentelemetry::nostd::string_view{"1.0.0"}}}); - return resource; -} - -const opentelemetry::sdk::instrumentationscope::InstrumentationScope &TestScope() -{ - static auto scope = opentelemetry::sdk::instrumentationscope::InstrumentationScope::Create( - "benchmark_scope", "1.0.0", "https://opentelemetry.io/schemas/1.24.0", - {{"scope.source", "benchmark"}}); - return *scope; -} - // Benchmark fixture: each benchmark function gets its own TracerProvider backed -// by a BufferingProcessor with a NullSpanExporter. -class OtlpSpanFixture : public benchmark::Fixture +// by a BufferingSpanProcessor with a NullSpanExporter. +class OtlpRecordableFixture : public benchmark::Fixture { public: + using benchmark::Fixture::SetUp; + void SetUp(benchmark::State &) override { - auto processor = std::make_unique(std::make_unique()); - provider_ = std::make_shared(std::move(processor)); - tracer_ = provider_->GetTracer(TestScope().GetName(), TestScope().GetVersion(), - TestScope().GetSchemaURL()); + auto processor = + std::make_unique(std::make_unique()); + provider_ = std::make_shared(std::move(processor)); + tracer_ = provider_->GetTracer(test_utils::TestScope().GetName(), + test_utils::TestScope().GetVersion(), + test_utils::TestScope().GetSchemaURL()); + test_utils::InitializeSpanTestData(); } protected: @@ -178,39 +159,13 @@ class OtlpSpanFixture : public benchmark::Fixture }; // Benchmark argument values. -constexpr int kMaxAttributes = 128; // default attribute count limit -constexpr int kBatchSmall = 1; // single span -constexpr int kBatchLarge = 512; // default batch size - -// Pre-built keys for the max-attributes benchmark (matches SDK default attribute limit). -const std::vector &MaxAttributeKeys() -{ - static const std::vector keys = []() { - std::vector result; - result.reserve(kMaxAttributes); - for (int key_idx = 0; key_idx < kMaxAttributes; ++key_idx) - { - result.push_back("attribute." + std::to_string(key_idx)); - } - return result; - }(); - return keys; -} +constexpr std::size_t kBatchSmall = 1; // single span +constexpr std::size_t kBatchLarge = 512; // default batch size // Build a batch of N fully-populated OtlpRecordable spans for PopulateRequest benchmarks. std::vector> MakeSpanBatch( std::size_t span_count) { - constexpr uint8_t kTraceIdBytes[opentelemetry::trace::TraceId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8}; - constexpr uint8_t kSpanIdBytes[opentelemetry::trace::SpanId::kSize] = {1, 2, 3, 4, 5, 6, 7, 8}; - constexpr uint8_t kParentIdBytes[opentelemetry::trace::SpanId::kSize] = {8, 7, 6, 5, 4, 3, 2, 1}; - const opentelemetry::trace::TraceId trace_id{kTraceIdBytes}; - const opentelemetry::trace::SpanId span_id{kSpanIdBytes}; - const opentelemetry::trace::SpanId parent_id{kParentIdBytes}; - const opentelemetry::trace::SpanContext ctx{ - trace_id, span_id, - opentelemetry::trace::TraceFlags{opentelemetry::trace::TraceFlags::kIsSampled}, true}; const auto now = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); std::vector> batch; @@ -218,13 +173,13 @@ std::vector> MakeSpanBatc for (std::size_t span_idx = 0; span_idx < span_count; ++span_idx) { auto recordable = std::make_unique(); - recordable->SetIdentity(ctx, parent_id); + recordable->SetIdentity(test_utils::TestSpanContext(), opentelemetry::trace::SpanId{}); recordable->SetName("benchmark_span"); recordable->SetSpanKind(trace_api::SpanKind::kServer); recordable->SetStartTime(now); recordable->SetDuration(std::chrono::milliseconds(5)); - recordable->SetResource(TestResource()); - recordable->SetInstrumentationScope(TestScope()); + recordable->SetResource(test_utils::TestResource()); + recordable->SetInstrumentationScope(test_utils::TestScope()); batch.push_back(std::move(recordable)); } return batch; @@ -240,80 +195,110 @@ std::unique_ptr CreateArena() } // namespace -// Baseline: start a span and end it immediately with no attributes. -BENCHMARK_DEFINE_F(OtlpSpanFixture, Minimal)(benchmark::State &state) +// Baseline: start a span and end it immediately with no attributes, links, or events. +BENCHMARK_DEFINE_F(OtlpRecordableFixture, RecordMinimalSpan)(benchmark::State &state) +{ + for (auto _ : state) + { + auto span = test_utils::StartMinimalSpan(*tracer_); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } +} +BENCHMARK_REGISTER_F(OtlpRecordableFixture, RecordMinimalSpan)->Unit(benchmark::kNanosecond); + +// RecordNominalSpan span: starts a span with a representative mix of attribute types then ends the +// span. +BENCHMARK_DEFINE_F(OtlpRecordableFixture, RecordNominalSpan)(benchmark::State &state) { for (auto _ : state) { - auto span = tracer_->StartSpan("benchmark_span"); + auto span = test_utils::StartNominalSpan(*tracer_); span->End(); state.PauseTiming(); provider_->ForceFlush(); state.ResumeTiming(); } } -BENCHMARK_REGISTER_F(OtlpSpanFixture, Minimal)->Unit(benchmark::kNanosecond); +BENCHMARK_REGISTER_F(OtlpRecordableFixture, RecordNominalSpan)->Unit(benchmark::kNanosecond); -// Nominal span: representative mix of attribute types passed at creation. -BENCHMARK_DEFINE_F(OtlpSpanFixture, Nominal)(benchmark::State &state) +// RecordSpanWithAttributes: starts a span with N attributes (all string values) then ends the span. +BENCHMARK_DEFINE_F(OtlpRecordableFixture, RecordSpanWithAttributes)(benchmark::State &state) { - using SpanAttribute = - std::pair; - static const std::vector kAttributes = { - {"benchmark.string.short", opentelemetry::nostd::string_view{"abcdefgh"}}, - {"benchmark.string.medium", - opentelemetry::nostd::string_view{"abcdefghijklmnopqrstuvwxyz012345"}}, - {"benchmark.string.long", - opentelemetry::nostd::string_view{ - "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"}}, - {"benchmark.int", opentelemetry::common::AttributeValue{static_cast(1)}}, - {"benchmark.double", opentelemetry::common::AttributeValue{3.14159265358979}}, - {"benchmark.bool", opentelemetry::common::AttributeValue{true}}, - }; + const int64_t attribute_count = state.range(0); + const std::vector attributes = + test_utils::MakeAttributes(static_cast(attribute_count)); for (auto _ : state) { - auto span = tracer_->StartSpan("benchmark_span", kAttributes); + auto span = test_utils::StartSpanWithAttributes(*tracer_, attributes); span->End(); state.PauseTiming(); provider_->ForceFlush(); state.ResumeTiming(); } + state.SetItemsProcessed(state.iterations() * attribute_count); } -BENCHMARK_REGISTER_F(OtlpSpanFixture, Nominal)->Unit(benchmark::kNanosecond); +BENCHMARK_REGISTER_F(OtlpRecordableFixture, RecordSpanWithAttributes) + ->ArgName("attribute_count") + ->Arg(1) + ->Arg(10) + ->Arg(test_utils::kSpanAttributeLimit) + ->Unit(benchmark::kNanosecond); + +// RecordSpanWithEvents: starts a span via the tracer API with N events, then ends the span. +BENCHMARK_DEFINE_F(OtlpRecordableFixture, RecordSpanWithEvents)(benchmark::State &state) +{ + const int64_t event_count = state.range(0); -// Max-attributes span: fills the recordable to its attribute limit (128) with string values. -BENCHMARK_DEFINE_F(OtlpSpanFixture, MaxAttributes)(benchmark::State &state) + for (auto _ : state) + { + auto span = test_utils::StartSpanWithEvents(*tracer_, static_cast(event_count)); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } + state.SetItemsProcessed(state.iterations() * event_count); +} +BENCHMARK_REGISTER_F(OtlpRecordableFixture, RecordSpanWithEvents) + ->ArgName("event_count") + ->Arg(1) + ->Arg(10) + ->Arg(test_utils::kSpanEventLimit) + ->Unit(benchmark::kNanosecond); + +// RecordSpanWithLinks: starts a span via the tracer API with N links, then ends the span. +BENCHMARK_DEFINE_F(OtlpRecordableFixture, RecordSpanWithLinks)(benchmark::State &state) { - using SpanAttribute = - std::pair; - static const std::vector kAttributes = []() { - const auto &keys = MaxAttributeKeys(); - std::vector attributes; - attributes.reserve(kMaxAttributes); - for (const auto &key : keys) - { - attributes.emplace_back(key, opentelemetry::nostd::string_view{"value-string-attribute"}); - } - return attributes; - }(); + const int64_t link_count = state.range(0); + const std::vector link_entries = + test_utils::MakeLinkEntries(static_cast(link_count)); + for (auto _ : state) { - auto span = tracer_->StartSpan("benchmark_span", kAttributes); + auto span = test_utils::StartSpanWithLinks(*tracer_, link_entries); span->End(); state.PauseTiming(); provider_->ForceFlush(); state.ResumeTiming(); } - state.SetItemsProcessed(state.iterations() * kMaxAttributes); + state.SetItemsProcessed(state.iterations() * link_count); } -BENCHMARK_REGISTER_F(OtlpSpanFixture, MaxAttributes)->Unit(benchmark::kNanosecond); +BENCHMARK_REGISTER_F(OtlpRecordableFixture, RecordSpanWithLinks) + ->ArgName("link_count") + ->Arg(1) + ->Arg(10) + ->Arg(test_utils::kSpanLinkLimit) + ->Unit(benchmark::kNanosecond); // Benchmark the arena allocation + OtlpRecordableUtils::PopulateRequest for a // batch of N spans. This mirrors the path in OtlpGrpcExporter::Export. -static void BM_PopulateRequest(benchmark::State &state) +static void BM_OtlpPopulateRequest(benchmark::State &state) { - const std::size_t span_count = static_cast(state.range(0)); - auto batch = MakeSpanBatch(span_count); + const int64_t span_count = state.range(0); + auto batch = MakeSpanBatch(static_cast(span_count)); for (auto _ : state) { auto arena = CreateArena(); @@ -326,9 +311,9 @@ static void BM_PopulateRequest(benchmark::State &state) request); benchmark::DoNotOptimize(request); } - state.SetItemsProcessed(state.iterations() * state.range(0)); + state.SetItemsProcessed(state.iterations() * span_count); } -BENCHMARK(BM_PopulateRequest) +BENCHMARK(BM_OtlpPopulateRequest) ->ArgName("span_count") ->Arg(kBatchSmall) ->Arg(kBatchLarge) diff --git a/sdk/src/trace/span_data.cc b/sdk/src/trace/span_data.cc index 9969b44c30..8b1503ee5d 100644 --- a/sdk/src/trace/span_data.cc +++ b/sdk/src/trace/span_data.cc @@ -100,15 +100,13 @@ void SpanData::AddEvent(nostd::string_view name, opentelemetry::common::SystemTimestamp timestamp, const opentelemetry::common::KeyValueIterable &attributes) noexcept { - SpanDataEvent event(std::string(name), timestamp, attributes); - events_.push_back(event); + events_.emplace_back(std::string(name), timestamp, attributes); } void SpanData::AddLink(const opentelemetry::trace::SpanContext &span_context, const opentelemetry::common::KeyValueIterable &attributes) noexcept { - SpanDataLink link(span_context, attributes); - links_.push_back(link); + links_.emplace_back(span_context, attributes); } void SpanData::SetStatus(opentelemetry::trace::StatusCode code, diff --git a/sdk/test/trace/BUILD b/sdk/test/trace/BUILD index 7f536dc57e..3e4393345a 100644 --- a/sdk/test/trace/BUILD +++ b/sdk/test/trace/BUILD @@ -205,3 +205,19 @@ otel_cc_benchmark( "//sdk/src/trace", ], ) + +otel_cc_benchmark( + name = "span_data_benchmark", + srcs = ["span_data_benchmark.cc"], + tags = [ + "benchmark", + "test", + "trace", + ], + deps = [ + "//exporters/memory:in_memory_span_exporter", + "//sdk/src/resource", + "//sdk/src/trace", + "//test_common:headers", + ], +) diff --git a/sdk/test/trace/CMakeLists.txt b/sdk/test/trace/CMakeLists.txt index ca0ae383bc..e299deb502 100644 --- a/sdk/test/trace/CMakeLists.txt +++ b/sdk/test/trace/CMakeLists.txt @@ -36,4 +36,14 @@ if(WITH_BENCHMARK) sampler_benchmark benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT} opentelemetry_trace opentelemetry_resources opentelemetry_exporter_in_memory) + + add_executable(span_data_benchmark span_data_benchmark.cc) + target_link_libraries( + span_data_benchmark + benchmark::benchmark + ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_trace + opentelemetry_resources + opentelemetry_exporter_in_memory + opentelemetry_test_common) endif() diff --git a/sdk/test/trace/span_data_benchmark.cc b/sdk/test/trace/span_data_benchmark.cc new file mode 100644 index 0000000000..6bf0418a01 --- /dev/null +++ b/sdk/test/trace/span_data_benchmark.cc @@ -0,0 +1,213 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// clang-format off +// +// ~/build/sdk/test/trace/span_data_benchmark --benchmark_repetitions=5 --benchmark_display_aggregates_only=true +// 2026-07-01T15:59:10+00:00 +// Running /home/devuser/build/sdk/test/trace/span_data_benchmark +// Run on (32 X 5700 MHz CPU s) +// CPU Caches: +// L1 Data 48 KiB (x16) +// L1 Instruction 32 KiB (x16) +// L2 Unified 2048 KiB (x16) +// L3 Unified 36864 KiB (x1) +// Load Average: 1.35, 1.52, 1.62 +// ***WARNING*** ASLR is enabled, the results may have unreproducible noise in them. +// -------------------------------------------------------------------------------------------------------------- +// Benchmark Time CPU Iterations +// -------------------------------------------------------------------------------------------------------------- +// SpanDataFixture/RecordMinimalSpan_mean 341 ns 343 ns 5 +// SpanDataFixture/RecordMinimalSpan_median 341 ns 343 ns 5 +// SpanDataFixture/RecordMinimalSpan_stddev 0.950 ns 0.988 ns 5 +// SpanDataFixture/RecordMinimalSpan_cv 0.28 % 0.29 % 5 +// SpanDataFixture/RecordNominalSpan_mean 581 ns 584 ns 5 +// SpanDataFixture/RecordNominalSpan_median 588 ns 590 ns 5 +// SpanDataFixture/RecordNominalSpan_stddev 15.3 ns 15.1 ns 5 +// SpanDataFixture/RecordNominalSpan_cv 2.63 % 2.59 % 5 +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:1_mean 390 ns 394 ns 5 items_per_second=2.5415M/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:1_median 391 ns 395 ns 5 items_per_second=2.53367M/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:1_stddev 4.17 ns 4.40 ns 5 items_per_second=28.4487k/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:1_cv 1.07 % 1.12 % 5 items_per_second=1.12% +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:10_mean 705 ns 708 ns 5 items_per_second=14.1257M/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:10_median 707 ns 711 ns 5 items_per_second=14.0739M/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:10_stddev 6.35 ns 6.36 ns 5 items_per_second=127.942k/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:10_cv 0.90 % 0.90 % 5 items_per_second=0.91% +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:128_mean 7479 ns 7484 ns 5 items_per_second=17.1122M/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:128_median 7539 ns 7543 ns 5 items_per_second=16.969M/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:128_stddev 189 ns 189 ns 5 items_per_second=438.424k/s +// SpanDataFixture/RecordSpanWithAttributes/attribute_count:128_cv 2.53 % 2.53 % 5 items_per_second=2.56% +// SpanDataFixture/RecordSpanWithEvents/event_count:1_mean 456 ns 459 ns 5 items_per_second=2.17751M/s +// SpanDataFixture/RecordSpanWithEvents/event_count:1_median 456 ns 459 ns 5 items_per_second=2.178M/s +// SpanDataFixture/RecordSpanWithEvents/event_count:1_stddev 2.89 ns 3.02 ns 5 items_per_second=14.3182k/s +// SpanDataFixture/RecordSpanWithEvents/event_count:1_cv 0.63 % 0.66 % 5 items_per_second=0.66% +// SpanDataFixture/RecordSpanWithEvents/event_count:10_mean 1584 ns 1587 ns 5 items_per_second=6.30345M/s +// SpanDataFixture/RecordSpanWithEvents/event_count:10_median 1580 ns 1583 ns 5 items_per_second=6.318M/s +// SpanDataFixture/RecordSpanWithEvents/event_count:10_stddev 24.3 ns 24.4 ns 5 items_per_second=95.4207k/s +// SpanDataFixture/RecordSpanWithEvents/event_count:10_cv 1.54 % 1.54 % 5 items_per_second=1.51% +// SpanDataFixture/RecordSpanWithEvents/event_count:128_mean 16434 ns 16438 ns 5 items_per_second=7.78791M/s +// SpanDataFixture/RecordSpanWithEvents/event_count:128_median 16429 ns 16432 ns 5 items_per_second=7.78954M/s +// SpanDataFixture/RecordSpanWithEvents/event_count:128_stddev 211 ns 211 ns 5 items_per_second=100.089k/s +// SpanDataFixture/RecordSpanWithEvents/event_count:128_cv 1.28 % 1.28 % 5 items_per_second=1.29% +// SpanDataFixture/RecordSpanWithLinks/link_count:1_mean 435 ns 437 ns 5 items_per_second=2.29077M/s +// SpanDataFixture/RecordSpanWithLinks/link_count:1_median 435 ns 436 ns 5 items_per_second=2.29368M/s +// SpanDataFixture/RecordSpanWithLinks/link_count:1_stddev 2.12 ns 2.39 ns 5 items_per_second=12.5108k/s +// SpanDataFixture/RecordSpanWithLinks/link_count:1_cv 0.49 % 0.55 % 5 items_per_second=0.55% +// SpanDataFixture/RecordSpanWithLinks/link_count:10_mean 1368 ns 1371 ns 5 items_per_second=7.2967M/s +// SpanDataFixture/RecordSpanWithLinks/link_count:10_median 1375 ns 1378 ns 5 items_per_second=7.25823M/s +// SpanDataFixture/RecordSpanWithLinks/link_count:10_stddev 22.3 ns 22.4 ns 5 items_per_second=121.346k/s +// SpanDataFixture/RecordSpanWithLinks/link_count:10_cv 1.63 % 1.63 % 5 items_per_second=1.66% +// SpanDataFixture/RecordSpanWithLinks/link_count:128_mean 15304 ns 15308 ns 5 items_per_second=8.38477M/s +// SpanDataFixture/RecordSpanWithLinks/link_count:128_median 14933 ns 14937 ns 5 items_per_second=8.56932M/s +// SpanDataFixture/RecordSpanWithLinks/link_count:128_stddev 930 ns 930 ns 5 items_per_second=474.386k/s +// SpanDataFixture/RecordSpanWithLinks/link_count:128_cv 6.08 % 6.08 % 5 items_per_second=5.66% +// +// clang-format on + +#include + +#include +#include +#include +#include +#include + +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/test_common/sdk/trace/test_utils.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/tracer.h" + +namespace trace_sdk = opentelemetry::sdk::trace; +namespace test_utils = opentelemetry::test_common; + +namespace +{ + +class SpanDataFixture : public benchmark::Fixture +{ +public: + using benchmark::Fixture::SetUp; + + void SetUp(benchmark::State &) override + { + auto exporter = std::make_unique(); + auto processor = std::make_unique(std::move(exporter)); + provider_ = std::make_shared(std::move(processor)); + tracer_ = provider_->GetTracer(test_utils::TestScope().GetName(), + test_utils::TestScope().GetVersion(), + test_utils::TestScope().GetSchemaURL()); + test_utils::InitializeSpanTestData(); + } + +protected: + std::shared_ptr provider_; + opentelemetry::nostd::shared_ptr tracer_; +}; + +} // namespace + +// Baseline: start a span and end it immediately with no attributes, links, or events. +BENCHMARK_DEFINE_F(SpanDataFixture, RecordMinimalSpan)(benchmark::State &state) +{ + for (auto _ : state) + { + auto span = test_utils::StartMinimalSpan(*tracer_); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } +} +BENCHMARK_REGISTER_F(SpanDataFixture, RecordMinimalSpan)->Unit(benchmark::kNanosecond); + +// RecordNominalSpan span: representative mix of attribute types passed at creation. +BENCHMARK_DEFINE_F(SpanDataFixture, RecordNominalSpan)(benchmark::State &state) +{ + for (auto _ : state) + { + auto span = test_utils::StartNominalSpan(*tracer_); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } +} +BENCHMARK_REGISTER_F(SpanDataFixture, RecordNominalSpan)->Unit(benchmark::kNanosecond); + +BENCHMARK_DEFINE_F(SpanDataFixture, RecordSpanWithAttributes)(benchmark::State &state) +{ + const int64_t attribute_count = state.range(0); + const std::vector attributes = + test_utils::MakeAttributes(attribute_count); + for (auto _ : state) + { + auto span = test_utils::StartSpanWithAttributes(*tracer_, attributes); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } + state.SetItemsProcessed(state.iterations() * attribute_count); +} +BENCHMARK_REGISTER_F(SpanDataFixture, RecordSpanWithAttributes) + ->ArgName("attribute_count") + ->Arg(1) + ->Arg(10) + ->Arg(test_utils::kSpanAttributeLimit) + ->Unit(benchmark::kNanosecond); + +BENCHMARK_DEFINE_F(SpanDataFixture, RecordSpanWithEvents)(benchmark::State &state) +{ + const int64_t event_count = state.range(0); + + for (auto _ : state) + { + auto span = test_utils::StartSpanWithEvents(*tracer_, static_cast(event_count)); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } + state.SetItemsProcessed(state.iterations() * event_count); +} +BENCHMARK_REGISTER_F(SpanDataFixture, RecordSpanWithEvents) + ->ArgName("event_count") + ->Arg(1) + ->Arg(10) + ->Arg(test_utils::kSpanEventLimit) + ->Unit(benchmark::kNanosecond); + +BENCHMARK_DEFINE_F(SpanDataFixture, RecordSpanWithLinks)(benchmark::State &state) +{ + const int64_t link_count = state.range(0); + const std::vector link_entries = + test_utils::MakeLinkEntries(static_cast(link_count)); + + for (auto _ : state) + { + auto span = test_utils::StartSpanWithLinks(*tracer_, link_entries); + span->End(); + state.PauseTiming(); + provider_->ForceFlush(); + state.ResumeTiming(); + } + state.SetItemsProcessed(state.iterations() * link_count); +} +BENCHMARK_REGISTER_F(SpanDataFixture, RecordSpanWithLinks) + ->ArgName("link_count") + ->Arg(1) + ->Arg(10) + ->Arg(test_utils::kSpanLinkLimit) + ->Unit(benchmark::kNanosecond); + +int main(int argc, char **argv) +{ + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/test_common/include/opentelemetry/test_common/sdk/trace/test_utils.h b/test_common/include/opentelemetry/test_common/sdk/trace/test_utils.h new file mode 100644 index 0000000000..78f0305fbb --- /dev/null +++ b/test_common/include/opentelemetry/test_common/sdk/trace/test_utils.h @@ -0,0 +1,228 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/trace/tracer.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace test_common +{ + +// A span processor that queues completed recordables into an internal buffer +// without exporting or destroying them, mirroring the batch processor's +// recording path. ForceFlush clears the buffer so benchmarks can drop recorded +// spans between iterations without measuring deallocation. +class BufferingSpanProcessor final : public sdk::trace::SpanProcessor +{ +public: + using Buffer = std::vector>; + + explicit BufferingSpanProcessor(std::unique_ptr exporter) + : exporter_(std::move(exporter)) + {} + + std::unique_ptr MakeRecordable() noexcept override + { + return exporter_->MakeRecordable(); + } + + void OnStart(sdk::trace::Recordable &, const trace::SpanContext &) noexcept override {} + + void OnEnd(std::unique_ptr &&span) noexcept override + { + buffer_.push_back(std::move(span)); + } + + bool ForceFlush(std::chrono::microseconds) noexcept override + { + buffer_.clear(); + return true; + } + + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } + +private: + std::unique_ptr exporter_; + Buffer buffer_; +}; + +inline const sdk::resource::Resource &TestResource() +{ + static const auto kResource = + sdk::resource::Resource::Create({{"service.name", nostd::string_view{"benchmark_service"}}, + {"service.version", nostd::string_view{"1.0.0"}}}); + return kResource; +} + +inline const sdk::instrumentationscope::InstrumentationScope &TestScope() +{ + static const auto kScope = sdk::instrumentationscope::InstrumentationScope::Create( + "benchmark_scope", "1.0.0", "https://opentelemetry.io/schemas/1.24.0", + {{"scope.source", "benchmark"}}); + return *kScope; +} + +constexpr std::size_t kSpanAttributeLimit = 128; +constexpr std::size_t kSpanEventLimit = 128; +constexpr std::size_t kSpanLinkLimit = 128; +constexpr std::size_t kSpanEventAttributesCount = 2; +constexpr std::size_t kSpanLinkAttributesCount = 2; + +// Pre-built unique attribute keys (up to the default limit) for attribute sweeps. +inline const std::vector &AttributeKeys() +{ + static const std::vector kAttributeKeys = []() { + std::vector result; + result.reserve(kSpanAttributeLimit); + for (std::size_t key_idx = 0; key_idx < kSpanAttributeLimit; ++key_idx) + { + result.push_back("attribute." + std::to_string(key_idx)); + } + return result; + }(); + return kAttributeKeys; +} + +// A representative mix of attribute types used by the nominal span benchmark. +using SpanAttribute = std::pair; +inline const std::vector &NominalAttributes() +{ + static const std::vector kNominalAttributes = { + {"benchmark.string.short", nostd::string_view{"abcdefgh"}}, + {"benchmark.string.medium", nostd::string_view{"abcdefghijklmnopqrstuvwxyz012345"}}, + {"benchmark.string.long", + nostd::string_view{"abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"}}, + {"benchmark.int", common::AttributeValue{static_cast(1)}}, + {"benchmark.double", common::AttributeValue{3.14159265358979}}, + {"benchmark.bool", common::AttributeValue{true}}, + }; + return kNominalAttributes; +} + +inline std::vector MakeAttributes(std::size_t count) +{ + const auto &keys = AttributeKeys(); + std::vector attributes; + attributes.reserve(count); + for (std::size_t i = 0; i < count; ++i) + { + attributes.emplace_back(keys.at(i), nostd::string_view{"value-string-attribute"}); + } + return attributes; +} + +inline const std::vector &SpanEventAttributes() +{ + static const std::vector kSpanEventAttributes = + MakeAttributes(kSpanEventAttributesCount); + return kSpanEventAttributes; +} + +inline const trace::SpanContext &TestSpanContext() +{ + static constexpr uint8_t kTraceId[trace::TraceId::kSize] = {2, 3, 4, 5, 6, 7, 8, 9, + 2, 3, 4, 5, 6, 7, 8, 9}; + static constexpr uint8_t kSpanId[trace::SpanId::kSize] = {9, 8, 7, 6, 5, 4, 3, 2}; + static const trace::SpanContext kTestSpanContext{trace::TraceId{kTraceId}, trace::SpanId{kSpanId}, + trace::TraceFlags{trace::TraceFlags::kIsSampled}, + true}; + return kTestSpanContext; +} + +inline const std::vector &SpanLinkAttributes() +{ + static const std::vector kSpanLinkAttributes = + MakeAttributes(kSpanLinkAttributesCount); + return kSpanLinkAttributes; +} + +using LinkEntry = std::pair>; + +inline std::vector MakeLinkEntries(std::size_t count) +{ + std::vector entries; + entries.reserve(count); + for (std::size_t i = 0; i < count; ++i) + { + entries.emplace_back(TestSpanContext(), SpanLinkAttributes()); + } + return entries; +} + +// Starts a span with no attributes, events, or links +inline nostd::shared_ptr StartMinimalSpan(trace::Tracer &tracer) +{ + return tracer.StartSpan("benchmark_span"); +} + +// Starts a span with a representative mix of attributes +inline nostd::shared_ptr StartNominalSpan(trace::Tracer &tracer) +{ + return tracer.StartSpan("benchmark_span", NominalAttributes()); +} + +// Starts a span with pre-built attributes +inline nostd::shared_ptr StartSpanWithAttributes( + trace::Tracer &tracer, + const std::vector &attributes) +{ + return tracer.StartSpan("benchmark_span", attributes); +} + +// Starts a span with pre-built links +inline nostd::shared_ptr StartSpanWithLinks(trace::Tracer &tracer, + const std::vector &link_entries) +{ + return tracer.StartSpan("benchmark_span", nostd::span{}, link_entries); +} + +// Starts a span and adds `num_events` +inline nostd::shared_ptr StartSpanWithEvents(trace::Tracer &tracer, + std::size_t num_events) +{ + auto span = tracer.StartSpan("benchmark_span"); + for (std::size_t i = 0; i < num_events; ++i) + { + span->AddEvent("benchmark_event", SpanEventAttributes()); + } + return span; +} + +// Initialize all the static test data in singletons above. To be called in test fixture SetUp() +inline void InitializeSpanTestData() +{ + TestResource(); + TestScope(); + AttributeKeys(); + NominalAttributes(); + SpanEventAttributes(); + SpanLinkAttributes(); + TestSpanContext(); +} + +} // namespace test_common +OPENTELEMETRY_END_NAMESPACE From aa8388fbbb3c81da0d6ae2c8d2a31e733d77397f Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Sat, 11 Jul 2026 03:36:26 -0400 Subject: [PATCH 34/49] [CI] Add `modernize-` and `cppcoreguidelines-pro-` checks to clang-tidy (#4238) --- .clang-tidy | 29 ++++++++++++++++++++++++++++- .github/workflows/clang-tidy.yaml | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 9611954a59..0492066a96 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,6 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 +# Checks: > -*, @@ -38,4 +39,30 @@ Checks: > -cppcoreguidelines-macro-usage, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-avoid-non-const-global-variables, - -cppcoreguidelines-pro-* + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-pro-type-static-cast-downcast, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-vararg, + modernize-*, + -modernize-avoid-c-arrays, + -modernize-concat-nested-namespaces, + -modernize-macro-to-enum, + -modernize-raw-string-literal, + -modernize-return-braced-init-list, + -modernize-type-traits, + -modernize-use-auto, + -modernize-use-nodiscard, + -modernize-use-scoped-lock, + -modernize-use-trailing-return-type, + -modernize-deprecated-headers, + -modernize-loop-convert, + -modernize-pass-by-value, + -modernize-use-equals-default, + -modernize-use-equals-delete, + -modernize-make-unique, + -modernize-make-shared, + diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 84300776f8..08b66a0699 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,9 +17,9 @@ jobs: matrix: include: - cmake_options: all-options-abiv1-preview - warning_limit: 137 + warning_limit: 216 - cmake_options: all-options-abiv2-preview - warning_limit: 145 + warning_limit: 226 env: CC: /usr/bin/clang-22 CXX: /usr/bin/clang++-22 From 06514e8dcbc6412faaccc2a20c3cfa13a7f7020b Mon Sep 17 00:00:00 2001 From: Tom Tan Date: Sun, 12 Jul 2026 02:39:08 -0700 Subject: [PATCH 35/49] [METRICS SDK] Apply cardinality limits only to non-overflow attributesets (#4236) --- CHANGELOG.md | 4 ++ .../sdk/metrics/state/attributes_hashmap.h | 17 ++--- .../sdk/metrics/state/sync_metric_storage.h | 10 ++- sdk/test/metrics/attributes_hashmap_test.cc | 11 ++-- .../metrics/bound_sync_instruments_test.cc | 64 +++++++++---------- sdk/test/metrics/cardinality_limit_test.cc | 19 +++--- sdk/test/metrics/sum_aggregation_test.cc | 9 +-- 7 files changed, 62 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 456c9439e3..3c57ff6451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Increment the: ## [Unreleased] +* [SDK] Apply metric cardinality limits to non-overflow attribute sets and + reserve the overflow point separately. + [#4236](https://github.com/open-telemetry/opentelemetry-cpp/pull/4236) + * [CODE HEALTH] Move otlp, zipkin and prometheus test classes into anonymous namespace [#4231](https://github.com/open-telemetry/opentelemetry-cpp/pull/4231) diff --git a/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h b/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h index 7812cdc38e..38f62005e1 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h @@ -215,18 +215,11 @@ class AttributesHashMapWithCustomHash { return true; } - // Determine if overflow entry already exists. - bool has_overflow = (hash_map_.find(GetOverflowAttributes()) != hash_map_.end()); - // If overflow already present, total size already includes it; trigger overflow - // when current size (including overflow) is >= limit. - if (has_overflow) - { - return hash_map_.size() >= attributes_limit_; - } - // If overflow not present yet, simulate adding a new distinct key. If that - // would exceed the limit, we redirect to overflow instead of creating a - // new real attribute entry. - return (hash_map_.size() + 1) >= attributes_limit_; + // The configured limit applies to distinct non-overflow attribute sets. + // The overflow point is an additional reserved entry. + const bool has_overflow = (hash_map_.find(GetOverflowAttributes()) != hash_map_.end()); + const size_t non_overflow_size = hash_map_.size() - (has_overflow ? 1 : 0); + return non_overflow_size >= attributes_limit_; } }; diff --git a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h index 5bce0fa379..a3aa7b8d13 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h @@ -247,12 +247,10 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage } const size_t limit = aggregation_config_->cardinality_limit_; const bool has_overflow = active_keys_.find(GetOverflowAttributes()) != active_keys_.end(); - // Mirror AttributesHashMap::IsOverflowAttributes() exactly. When the - // overflow slot is already counted in active_keys_, only route to - // overflow when total active keys reach the limit. Otherwise simulate - // adding the overflow slot too, matching the (size + 1) >= limit branch. - const bool would_overflow = - has_overflow ? (active_keys_.size() >= limit) : (active_keys_.size() + 1 >= limit); + // Mirror AttributesHashMap::IsOverflowAttributes() exactly. The configured + // limit applies to non-overflow attribute sets, while overflow is reserved. + const size_t non_overflow_size = active_keys_.size() - (has_overflow ? 1 : 0); + const bool would_overflow = non_overflow_size >= limit; if (would_overflow) { active_keys_.insert(GetOverflowAttributes()); diff --git a/sdk/test/metrics/attributes_hashmap_test.cc b/sdk/test/metrics/attributes_hashmap_test.cc index 157f3f814b..9aedf0aef2 100644 --- a/sdk/test/metrics/attributes_hashmap_test.cc +++ b/sdk/test/metrics/attributes_hashmap_test.cc @@ -183,6 +183,7 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) // Size should be exactly 'limit' (no overflow yet) EXPECT_EQ(map.Size(), limit); + EXPECT_EQ(map.Get(GetOverflowAttributes()), nullptr); // Insert one more distinct attribute; this should not increase the real attributes count MetricAttributes overflow_trigger = {{"k", "overflow"}}; @@ -190,9 +191,9 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) []() { return std::unique_ptr(new DropAggregation()); }) ->Aggregate(static_cast(1)); - EXPECT_EQ(map.Size(), limit); + EXPECT_EQ(map.Size(), limit + 1); - // Insert several more unique attributes - size must remain constant (limit) + // Insert several more unique attributes - size must remain constant (limit + overflow) for (size_t i = 0; i < limit - 1; ++i) { MetricAttributes extra_attr = {{"k", std::string("extra") + std::to_string(i)}}; @@ -200,13 +201,13 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) []() { return std::unique_ptr(new DropAggregation()); }) ->Aggregate(static_cast(1)); } - EXPECT_EQ(map.Size(), limit); + EXPECT_EQ(map.Size(), limit + 1); // Ensure overflow key was actually created and accessible via Get EXPECT_NE(map.Get(GetOverflowAttributes()), nullptr); // Ensure original real attributes still present - for (size_t i = 0; i < limit - 1; ++i) + for (size_t i = 0; i < limit; ++i) { MetricAttributes attr = {{"k", std::to_string(i)}}; EXPECT_NE(map.Get(attr), nullptr); @@ -220,7 +221,7 @@ TEST(AttributesHashMap, OverflowCardinalityLimitBehavior) }); EXPECT_EQ(map_copy.Size(), map.Size()); EXPECT_NE(map_copy.Get(GetOverflowAttributes()), nullptr); - for (size_t i = 0; i < limit - 1; ++i) + for (size_t i = 0; i < limit; ++i) { MetricAttributes attr = {{"k", std::to_string(i)}}; EXPECT_NE(map_copy.Get(attr), nullptr); diff --git a/sdk/test/metrics/bound_sync_instruments_test.cc b/sdk/test/metrics/bound_sync_instruments_test.cc index a3194e54a1..6b289c6aa4 100644 --- a/sdk/test/metrics/bound_sync_instruments_test.cc +++ b/sdk/test/metrics/bound_sync_instruments_test.cc @@ -633,8 +633,7 @@ TEST(BoundSyncInstruments, BoundAndUnboundShareDatapoint) // multiple overflow-bound handles aggregate into the same overflow datapoint. TEST(BoundSyncInstruments, BindingAtOverflowGoesToOverflowBucket) { - // limit = 3: two real distinct keys are admitted; the third new distinct - // key overflows because the overflow attribute set itself consumes a slot. + // limit = 3: three real distinct keys are admitted; the fourth overflows. StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); M a1 = {{"k", "1"}}; @@ -653,7 +652,7 @@ TEST(BoundSyncInstruments, BindingAtOverflowGoesToOverflowBucket) auto b4 = holder->Bind(KeyValueIterableView(a4)); b4->RecordLong(50); - // Overflow datapoint must equal sum of the overflow-bound writes (100 + 50). + // Overflow datapoint contains only the fourth bound write. std::shared_ptr collector( new MockCollectorHandle(AggregationTemporality::kDelta)); std::vector> collectors{collector}; @@ -673,7 +672,7 @@ TEST(BoundSyncInstruments, BindingAtOverflowGoesToOverflowBucket) return true; }); EXPECT_TRUE(seen); - EXPECT_EQ(ov_value, 150); + EXPECT_EQ(ov_value, 50); } // 7) Noop bound instruments compile and no-op. @@ -756,7 +755,7 @@ TEST(BoundSyncInstruments, DroppedBoundEntriesAreGarbageCollected) h->RecordLong(1); handles.push_back(h); } - // Expect no overflow yet (3 + 1 < 5). + // Expect no overflow yet (3 < 5). EXPECT_FALSE(HasOverflowPoint(*holder, AggregationTemporality::kDelta)); } @@ -764,14 +763,12 @@ TEST(BoundSyncInstruments, DroppedBoundEntriesAreGarbageCollected) // Bind() to reuse it without consuming new cardinality. TEST(BoundSyncInstruments, BindOnExistingUnboundKeyDoesNotOverflow) { - // limit = 3: two real keys are admitted; a third new distinct key would - // overflow (overflow itself consumes a slot). + // limit = 3: three real keys are admitted; a fourth new distinct key would overflow. StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); M a1 = {{"k", "1"}}; M a2 = {{"k", "2"}}; - // Two distinct unbound keys consume the two admitted slots (limit=3 with - // overflow consuming a slot leaves room for two real keys). + // Two distinct unbound keys consume two of the three admitted slots. holder->RecordLong(10, KeyValueIterableView(a1), opentelemetry::context::Context{}); holder->RecordLong(20, KeyValueIterableView(a2), opentelemetry::context::Context{}); @@ -858,9 +855,9 @@ TEST(BoundSyncInstruments, UnboundOnExistingBoundKeyDoesNotOverflow) // unbound keys correctly overflow. TEST(BoundSyncInstruments, RetainedBoundEntriesCountAfterDeltaCollect) { - StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); + StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 2); - // Two bound keys consume the two real admitted slots (overflow takes the 3rd). + // Two bound keys consume the two admitted slots. M a1 = {{"bound", "1"}}; M a2 = {{"bound", "2"}}; auto b1 = holder->Bind(KeyValueIterableView(a1)); @@ -881,7 +878,7 @@ TEST(BoundSyncInstruments, RetainedBoundEntriesCountAfterDeltaCollect) // Strengthened: assert the overflow datapoint VALUE, not just its presence. TEST(BoundSyncInstruments, RetainedBoundEntriesOverflowValue) { - StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); + StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 2); M a1 = {{"bound", "1"}}; M a2 = {{"bound", "2"}}; auto b1 = holder->Bind(KeyValueIterableView(a1)); @@ -912,12 +909,11 @@ TEST(BoundSyncInstruments, RetainedBoundEntriesOverflowValue) } // No-attribute unbound RecordLong must follow the unified cardinality policy. -// With limit=3, two real distinct bound keys are admitted; the empty-attr -// unbound write would be a third new key and routes to overflow (overflow -// itself consumes a slot). +// With limit=2, two real distinct bound keys are admitted; the empty-attr +// unbound write is a third new key and routes to overflow. TEST(BoundSyncInstruments, NoAttributeUnboundFollowsUnifiedPolicyLong) { - StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); + StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 2); M a1 = {{"k", "1"}}; M a2 = {{"k", "2"}}; auto b1 = holder->Bind(KeyValueIterableView(a1)); @@ -949,7 +945,7 @@ TEST(BoundSyncInstruments, NoAttributeUnboundFollowsUnifiedPolicyLong) // Same coverage for double counter no-attribute path. TEST(BoundSyncInstruments, NoAttributeUnboundFollowsUnifiedPolicyDouble) { - StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kDouble, 3); + StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kDouble, 2); M a1 = {{"k", "1"}}; M a2 = {{"k", "2"}}; auto b1 = holder->Bind(KeyValueIterableView(a1)); @@ -1037,7 +1033,7 @@ TEST(BoundSyncInstruments, DirtyDroppedBoundEntriesReleaseCardinality) { StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); - // Bind two distinct keys (consume the two real admitted slots), record so + // Bind two distinct keys, record so // they're dirty, then drop the handles BEFORE Collect(). The pre-rotation // GC cannot remove them (they're dirty); rotation flushes their data; the // post-rotation cleanup must release their slots. @@ -1071,36 +1067,36 @@ TEST(BoundSyncInstruments, OverflowParityAllowsFillingRemainingSlot) { StorageHolder holder(InstrumentType::kCounter, InstrumentValueType::kLong, 3); - // Trigger overflow first: bind k1, k2, then k3 (which routes to overflow, - // so bound_entries_ ends up with k1, k2, and the overflow entry). + // Trigger overflow first: bind k1, k2, k3, then k4, which routes to overflow. M a1 = {{"k", "1"}}; M a2 = {{"k", "2"}}; M a3 = {{"k", "3"}}; + M a4 = {{"k", "4"}}; auto b1 = holder->Bind(KeyValueIterableView(a1)); auto b2 = holder->Bind(KeyValueIterableView(a2)); - auto bov = holder->Bind(KeyValueIterableView(a3)); // routes to overflow + auto b3 = holder->Bind(KeyValueIterableView(a3)); + auto bov = holder->Bind(KeyValueIterableView(a4)); // routes to overflow b1->RecordLong(1); b2->RecordLong(1); + b3->RecordLong(1); bov->RecordLong(100); // Drop k1 only. After Collect(), the M1 cleanup releases its slot, leaving - // active_keys_ = { k2, overflow }. With limit=3, has_overflow=true and - // active_keys_.size()==2, the existing AttributesHashMap semantics admit - // one more real key. The pre-fix preview path would have routed it to - // overflow (off-by-one). After M2, it must be admitted. + // active_keys_ = { k2, k3, overflow }. With limit=3, the existing + // AttributesHashMap semantics admit one more real key. b1.reset(); - EXPECT_EQ(CollectAndCountPoints(*holder, AggregationTemporality::kDelta), 3u); + EXPECT_EQ(CollectAndCountPoints(*holder, AggregationTemporality::kDelta), 4u); // Fresh real key in the next interval must be admitted, not routed to overflow. - M a4 = {{"fresh", "key"}}; - holder->RecordLong(42, KeyValueIterableView(a4), opentelemetry::context::Context{}); + M a5 = {{"fresh", "key"}}; + holder->RecordLong(42, KeyValueIterableView(a5), opentelemetry::context::Context{}); std::shared_ptr collector( new MockCollectorHandle(AggregationTemporality::kDelta)); std::vector> collectors{collector}; bool overflow_seen = false; - bool a4_seen = false; - int64_t a4_value = 0; + bool a5_seen = false; + int64_t a5_value = 0; holder->Collect(collector.get(), collectors, std::chrono::system_clock::now(), std::chrono::system_clock::now(), [&](const MetricData &md) { for (const auto &p : md.point_data_attr_) @@ -1113,15 +1109,15 @@ TEST(BoundSyncInstruments, OverflowParityAllowsFillingRemainingSlot) if (it != p.attributes.end() && opentelemetry::nostd::get(it->second) == "key") { - a4_seen = true; + a5_seen = true; const auto &sp = opentelemetry::nostd::get(p.point_data); - a4_value = opentelemetry::nostd::get(sp.value_); + a5_value = opentelemetry::nostd::get(sp.value_); } } return true; }); - EXPECT_TRUE(a4_seen); - EXPECT_EQ(a4_value, 42); + EXPECT_TRUE(a5_seen); + EXPECT_EQ(a5_value, 42); EXPECT_FALSE(overflow_seen); } diff --git a/sdk/test/metrics/cardinality_limit_test.cc b/sdk/test/metrics/cardinality_limit_test.cc index b28f23a847..cd38641a79 100644 --- a/sdk/test/metrics/cardinality_limit_test.cc +++ b/sdk/test/metrics/cardinality_limit_test.cc @@ -46,7 +46,7 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) []() -> std::unique_ptr { return std::unique_ptr(new LongSumAggregation(true)); }; - // add 10 unique metric points. 9 should be added to hashmap, 10th should be overflow. + // Add 10 unique metric points. All should be independently aggregated without overflow. int64_t record_value = 100; for (auto i = 0; i < 10; i++) { @@ -55,15 +55,16 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) ->Aggregate(record_value); } EXPECT_EQ(hash_map.Size(), 10); + EXPECT_EQ(hash_map.Get(GetOverflowAttributes()), nullptr); // add 5 unique metric points above limit, they all should get consolidated as single - // overflowmetric point. + // overflow metric point. for (auto i = 10; i < 15; i++) { FilteredOrderedAttributeMap attributes = {{"key", std::to_string(i)}}; static_cast(hash_map.GetOrSetDefault(attributes, aggregation_callback)) ->Aggregate(record_value); } - EXPECT_EQ(hash_map.Size(), 10); // only one more metric point should be added as overflow. + EXPECT_EQ(hash_map.Size(), 11); // one additional metric point should be added as overflow. // record 5 more measurements to already existing (and not-overflow) metric points. They // should get aggregated to these existing metric points. for (auto i = 0; i < 5; i++) @@ -72,16 +73,16 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) static_cast(hash_map.GetOrSetDefault(attributes, aggregation_callback)) ->Aggregate(record_value); } - EXPECT_EQ(hash_map.Size(), 10); // no new metric point added + EXPECT_EQ(hash_map.Size(), 11); // no new metric point added // get the overflow metric point auto agg1 = hash_map.GetOrSetDefault(GetOverflowAttributes(), aggregation_callback); EXPECT_NE(agg1, nullptr); auto sum_agg1 = static_cast(agg1); EXPECT_EQ(nostd::get(nostd::get(sum_agg1->ToPoint()).value_), - record_value * 6); // 1 from previous 10, 5 from current 5. + record_value * 5); // get remaining metric points - for (auto i = 0; i < 9; i++) + for (auto i = 0; i < 10; i++) { FilteredOrderedAttributeMap attributes = {{"key", std::to_string(i)}}; auto agg2 = hash_map.GetOrSetDefault(attributes, aggregation_callback); @@ -123,7 +124,7 @@ TEST_P(WritableMetricStorageCardinalityLimitTestFixture, LongCounterSumAggregati &aggConfig); int64_t record_value = 100; - // add 9 unique metric points, and 6 more above limit. + // Add 10 unique metric points, and 5 more above limit. for (auto i = 0; i < 15; i++) { std::map attributes = {{"key", std::to_string(i)}}; @@ -152,13 +153,13 @@ TEST_P(WritableMetricStorageCardinalityLimitTestFixture, LongCounterSumAggregati // value `true`. EXPECT_EQ(data_attr.attributes.size(), 1u); EXPECT_EQ(nostd::get(data_attr.attributes.begin()->second), true); - EXPECT_EQ(nostd::get(data.value_), record_value * 6); + EXPECT_EQ(nostd::get(data.value_), record_value * 5); overflow_present = true; } } return true; }); - EXPECT_EQ(count_attributes, attributes_limit); + EXPECT_EQ(count_attributes, attributes_limit + 1); EXPECT_EQ(overflow_present, true); } INSTANTIATE_TEST_SUITE_P(All, diff --git a/sdk/test/metrics/sum_aggregation_test.cc b/sdk/test/metrics/sum_aggregation_test.cc index 1a4e9c2545..7aaed33c9d 100644 --- a/sdk/test/metrics/sum_aggregation_test.cc +++ b/sdk/test/metrics/sum_aggregation_test.cc @@ -194,7 +194,7 @@ TEST(HistogramToSumFilterAttributesWithCardinalityLimit, Double) { for (const MetricData &md : smd.metric_data_) { - EXPECT_EQ(cardinality_limit, md.point_data_attr_.size()); + EXPECT_EQ(cardinality_limit + 1, md.point_data_attr_.size()); for (size_t i = 0; i < md.point_data_attr_.size(); i++) { EXPECT_EQ(1, md.point_data_attr_[i].attributes.size()); @@ -378,11 +378,8 @@ TEST(CounterToSumFilterAttributesWithCardinalityLimit, Double) for (const MetricData &md : smd.metric_data_) { // When the number of unique attribute sets exceeds the cardinality limit, the - // implementation emits up to (cardinality_limit - 1) unique sets and one overflow set, - // resulting in a total of cardinality_limit sets. This test checks that the number of - // emitted attribute sets is within the expected range, accounting for the overflow - // behavior. - EXPECT_EQ(cardinality_limit, md.point_data_attr_.size()); + // implementation emits cardinality_limit unique sets and one overflow set. + EXPECT_EQ(cardinality_limit + 1, md.point_data_attr_.size()); for (size_t i = 0; i < md.point_data_attr_.size(); i++) { EXPECT_EQ(1, md.point_data_attr_[i].attributes.size()); From 4e87286881b5b68fc45b8b377340362d8d3e6981 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:18:22 -0400 Subject: [PATCH 36/49] [CONFIGURATION] fix yaml configuration of the logger configurator (#4241) --- sdk/src/configuration/sdk_builder.cc | 71 ++++++++++++++++++++- sdk/test/configuration/BUILD | 20 ++++++ sdk/test/configuration/CMakeLists.txt | 1 + sdk/test/configuration/sdk_builder_test.cc | 74 ++++++++++++++++++++++ 4 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 sdk/test/configuration/sdk_builder_test.cc diff --git a/sdk/src/configuration/sdk_builder.cc b/sdk/src/configuration/sdk_builder.cc index 98f5fde13b..c077761859 100644 --- a/sdk/src/configuration/sdk_builder.cc +++ b/sdk/src/configuration/sdk_builder.cc @@ -16,6 +16,7 @@ #include "opentelemetry/common/kv_properties.h" #include "opentelemetry/context/propagation/composite_propagator.h" #include "opentelemetry/context/propagation/text_map_propagator.h" +#include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/global_log_handler.h" @@ -193,6 +194,68 @@ using common::WildcardMatch; namespace { +static opentelemetry::logs::Severity ToLogSeverity( + opentelemetry::sdk::configuration::SeverityNumber severity_number) +{ + // Convert configuration::SeverityNumber to opentelemetry::logs::Severity + // The configuration::SeverityNumber enum values do not match the opentelemetry::logs::Severity + // enum values use a switch statement for conversion. + switch (severity_number) + { + case SeverityNumber::trace: + return opentelemetry::logs::Severity::kTrace; + case SeverityNumber::trace2: + return opentelemetry::logs::Severity::kTrace2; + case SeverityNumber::trace3: + return opentelemetry::logs::Severity::kTrace3; + case SeverityNumber::trace4: + return opentelemetry::logs::Severity::kTrace4; + case SeverityNumber::debug: + return opentelemetry::logs::Severity::kDebug; + case SeverityNumber::debug2: + return opentelemetry::logs::Severity::kDebug2; + case SeverityNumber::debug3: + return opentelemetry::logs::Severity::kDebug3; + case SeverityNumber::debug4: + return opentelemetry::logs::Severity::kDebug4; + case SeverityNumber::info: + return opentelemetry::logs::Severity::kInfo; + case SeverityNumber::info2: + return opentelemetry::logs::Severity::kInfo2; + case SeverityNumber::info3: + return opentelemetry::logs::Severity::kInfo3; + case SeverityNumber::info4: + return opentelemetry::logs::Severity::kInfo4; + case SeverityNumber::warn: + return opentelemetry::logs::Severity::kWarn; + case SeverityNumber::warn2: + return opentelemetry::logs::Severity::kWarn2; + case SeverityNumber::warn3: + return opentelemetry::logs::Severity::kWarn3; + case SeverityNumber::warn4: + return opentelemetry::logs::Severity::kWarn4; + case SeverityNumber::error: + return opentelemetry::logs::Severity::kError; + case SeverityNumber::error2: + return opentelemetry::logs::Severity::kError2; + case SeverityNumber::error3: + return opentelemetry::logs::Severity::kError3; + case SeverityNumber::error4: + return opentelemetry::logs::Severity::kError4; + case SeverityNumber::fatal: + return opentelemetry::logs::Severity::kFatal; + case SeverityNumber::fatal2: + return opentelemetry::logs::Severity::kFatal2; + case SeverityNumber::fatal3: + return opentelemetry::logs::Severity::kFatal3; + case SeverityNumber::fatal4: + return opentelemetry::logs::Severity::kFatal4; + default: + break; + } + return opentelemetry::logs::Severity::kInvalid; +} + class ResourceAttributeValueSetter : public opentelemetry::sdk::configuration::AttributeValueConfigurationVisitor { @@ -1876,15 +1939,17 @@ SdkBuilder::CreateLoggerConfigurator( using opentelemetry::sdk::instrumentationscope::ScopeConfigurator; using opentelemetry::sdk::logs::LoggerConfig; - LoggerConfig default_config = - model->default_config.enabled ? LoggerConfig::Enabled() : LoggerConfig::Disabled(); + LoggerConfig default_config = LoggerConfig::Create( + model->default_config.enabled, ToLogSeverity(model->default_config.minimum_severity), + model->default_config.trace_based); auto builder = ScopeConfigurator::Builder(default_config); for (const auto &entry : model->loggers) { LoggerConfig entry_config = - entry.config.enabled ? LoggerConfig::Enabled() : LoggerConfig::Disabled(); + LoggerConfig::Create(entry.config.enabled, ToLogSeverity(entry.config.minimum_severity), + entry.config.trace_based); std::string pattern = entry.name; builder.AddCondition( [pattern](const InstrumentationScope &scope) { diff --git a/sdk/test/configuration/BUILD b/sdk/test/configuration/BUILD index f9cd843105..f16cc267cc 100644 --- a/sdk/test/configuration/BUILD +++ b/sdk/test/configuration/BUILD @@ -3,6 +3,26 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test") +cc_test( + name = "sdk_builder_test", + srcs = [ + "sdk_builder_test.cc", + ], + tags = [ + "test", + "yaml", + ], + deps = [ + "//sdk:headers", + "//sdk/src/configuration", + "//sdk/src/logs", + "//sdk/src/metrics", + "//sdk/src/resource", + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "yaml_logs_test", srcs = [ diff --git a/sdk/test/configuration/CMakeLists.txt b/sdk/test/configuration/CMakeLists.txt index 38ae21b19d..5385ddf0de 100644 --- a/sdk/test/configuration/CMakeLists.txt +++ b/sdk/test/configuration/CMakeLists.txt @@ -3,6 +3,7 @@ foreach( testname + sdk_builder_test yaml_logs_test yaml_metrics_test yaml_propagator_test diff --git a/sdk/test/configuration/sdk_builder_test.cc b/sdk/test/configuration/sdk_builder_test.cc new file mode 100644 index 0000000000..cccb6d37ee --- /dev/null +++ b/sdk/test/configuration/sdk_builder_test.cc @@ -0,0 +1,74 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +#include "opentelemetry/logs/severity.h" +#include "opentelemetry/sdk/configuration/logger_config_configuration.h" +#include "opentelemetry/sdk/configuration/logger_configurator_configuration.h" +#include "opentelemetry/sdk/configuration/logger_matcher_and_config_configuration.h" +#include "opentelemetry/sdk/configuration/registry.h" +#include "opentelemetry/sdk/configuration/sdk_builder.h" +#include "opentelemetry/sdk/configuration/severity_number.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/logger_config.h" + +namespace config_sdk = opentelemetry::sdk::configuration; +namespace scope_sdk = opentelemetry::sdk::instrumentationscope; +namespace logs_sdk = opentelemetry::sdk::logs; + +TEST(SdkBuilder, CreateLoggerConfigurator) +{ + config_sdk::LoggerConfigConfiguration default_config; + default_config.enabled = true; + default_config.minimum_severity = config_sdk::SeverityNumber::warn; + default_config.trace_based = false; + + config_sdk::LoggerMatcherAndConfigConfiguration matcher1; + matcher1.name = "enabled_minsev_error_not_trace_based"; + matcher1.config.enabled = true; + matcher1.config.minimum_severity = config_sdk::SeverityNumber::error3; + matcher1.config.trace_based = false; + + config_sdk::LoggerMatcherAndConfigConfiguration matcher2; + matcher2.name = "disabled_minsev_info_trace_based"; + matcher2.config.enabled = false; + matcher2.config.minimum_severity = config_sdk::SeverityNumber::debug; + matcher2.config.trace_based = true; + + auto model = std::make_unique(); + model->default_config = default_config; + model->loggers.push_back(matcher1); + model->loggers.push_back(matcher2); + + config_sdk::SdkBuilder builder(std::make_shared()); + + auto logger_configurator = builder.CreateLoggerConfigurator(model); + ASSERT_NE(logger_configurator, nullptr); + + auto default_scope = scope_sdk::InstrumentationScope::Create("default_scope"); + logs_sdk::LoggerConfig sdk_logger_config_default = + logger_configurator->ComputeConfig(*default_scope); + + auto scope_1 = scope_sdk::InstrumentationScope::Create(matcher1.name); + logs_sdk::LoggerConfig sdk_logger_config_1 = logger_configurator->ComputeConfig(*scope_1); + + auto scope_2 = scope_sdk::InstrumentationScope::Create(matcher2.name); + logs_sdk::LoggerConfig sdk_logger_config_2 = logger_configurator->ComputeConfig(*scope_2); + + EXPECT_TRUE(sdk_logger_config_default.IsEnabled()); + EXPECT_EQ(sdk_logger_config_default.GetMinimumSeverity(), opentelemetry::logs::Severity::kWarn); + EXPECT_FALSE(sdk_logger_config_default.IsTraceBased()); + + EXPECT_TRUE(sdk_logger_config_1.IsEnabled()); + EXPECT_EQ(sdk_logger_config_1.GetMinimumSeverity(), opentelemetry::logs::Severity::kError3); + EXPECT_FALSE(sdk_logger_config_1.IsTraceBased()); + + EXPECT_FALSE(sdk_logger_config_2.IsEnabled()); + EXPECT_EQ(sdk_logger_config_2.GetMinimumSeverity(), opentelemetry::logs::Severity::kDebug); + EXPECT_TRUE(sdk_logger_config_2.IsTraceBased()); +} From e12542e57b4a59753e79557da709653312209923 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Sun, 12 Jul 2026 19:24:05 -0400 Subject: [PATCH 37/49] [SDK] Support LoggerConfigurator updates and example (#4224) --- examples/CMakeLists.txt | 1 + examples/logger_configurator/CMakeLists.txt | 13 + examples/logger_configurator/README.md | 420 ++++++++++++++++++ examples/logger_configurator/main.cc | 294 ++++++++++++ sdk/include/opentelemetry/sdk/logs/logger.h | 17 +- .../opentelemetry/sdk/logs/logger_context.h | 7 + .../opentelemetry/sdk/logs/logger_provider.h | 16 + sdk/src/logs/logger.cc | 43 +- sdk/src/logs/logger_context.cc | 15 + sdk/src/logs/logger_provider.cc | 30 ++ sdk/test/logs/logger_provider_sdk_test.cc | 355 ++++++++++++++- 11 files changed, 1190 insertions(+), 21 deletions(-) create mode 100644 examples/logger_configurator/CMakeLists.txt create mode 100644 examples/logger_configurator/README.md create mode 100644 examples/logger_configurator/main.cc diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 26a3159a10..e1237e5308 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -29,6 +29,7 @@ add_subdirectory(multithreaded) add_subdirectory(multi_processor) add_subdirectory(environment_carrier) add_subdirectory(tracer_configurator) +add_subdirectory(logger_configurator) add_subdirectory(explicit_parent) if(WITH_EXAMPLES_HTTP) diff --git a/examples/logger_configurator/CMakeLists.txt b/examples/logger_configurator/CMakeLists.txt new file mode 100644 index 0000000000..8d89f6b51d --- /dev/null +++ b/examples/logger_configurator/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +add_executable(example_logger_configurator main.cc) +target_link_libraries( + example_logger_configurator + PRIVATE opentelemetry-cpp::logs + opentelemetry-cpp::ostream_log_record_exporter) + +if(BUILD_TESTING) + add_test(NAME examples.logger_configurator + COMMAND "$") +endif() diff --git a/examples/logger_configurator/README.md b/examples/logger_configurator/README.md new file mode 100644 index 0000000000..de50cf77dc --- /dev/null +++ b/examples/logger_configurator/README.md @@ -0,0 +1,420 @@ +# Logger Configurator Example + +This example demonstrates how to set a `LoggerConfigurator` on construction +of the `LoggerProvider` and to update it at runtime using +`LoggerProvider::UpdateLoggerConfigurator` to control the minimum severity +and enabled state of specific loggers without restarting the application or +recreating the loggers. + +`LoggerProvider::UpdateLoggerConfigurator` is thread-safe and updates the +`LoggerConfig` on all existing loggers. + +Three loggers with unique instrumentation scope names are used to simulate +a user application: + +- `my_application`: simulated user application +- `my_library`: simulated user library +- `external_library`: simulated external third-party library + +The example walks through a simulated debugging workflow in four stages: + +- Stage 1: Production baseline. `my_application` and `my_library` loggers + with Warn minmum severity and the + `external_library` logger is disabled. +- Stage 2: User reports unexpected behavior. Lower `my_application` and + `my_library` loggers to Debug and `external_library` logger remains disabled. +- Stage 3: Debug logs implicate `external_library`. Enable its logger at Debug and + Raise `my_application` back to Warn. +- Stage 4: Root cause found and fixed. Restore the production baseline. + +## Build and run + +```sh +~/build/examples/logger_configurator/example_logger_configurator +``` + +**Expected output:** + +Log records are exported to stdout via the `OStreamLogRecordExporter`. + +```sh +Stage 1: production baseline + my_application and my_library at Warn. external_library disabled +{ + timestamp : 0 + observed_timestamp : 1783528026109982345 + severity_num : 13 + severity_text : WARN + body : MyApplication: my_library invoke failed with an error + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_application + version : + schema_url : + attributes : +} + +Stage 2: investigating, loggers lowered to Debug + my_application and my_library at Debug. external_library disabled +{ + timestamp : 0 + observed_timestamp : 1783528026110271494 + severity_num : 5 + severity_text : DEBUG + body : MyApplication: Execute called... + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_application + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026110499958 + severity_num : 5 + severity_text : DEBUG + body : MyModule: Execute called... + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026110864190 + severity_num : 5 + severity_text : DEBUG + body : MyModule: external_library returned an error + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026111027097 + severity_num : 5 + severity_text : DEBUG + body : MyModule: Execute complete + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026111207651 + severity_num : 13 + severity_text : WARN + body : MyApplication: my_library invoke failed with an error + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_application + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026111366894 + severity_num : 5 + severity_text : DEBUG + body : MyApplication: Execute complete + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_application + version : + schema_url : + attributes : +} + +Stage 3: narrowing down, my_library and external_library at Debug + my_library and external_library at Debug. my_application at Warn +{ + timestamp : 0 + observed_timestamp : 1783528026111618733 + severity_num : 5 + severity_text : DEBUG + body : MyModule: Execute called... + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026111778229 + severity_num : 5 + severity_text : DEBUG + body : ExternalModule: Execute called... + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : external_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112008544 + severity_num : 5 + severity_text : DEBUG + body : ExternalModule: executing... + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : external_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112254877 + severity_num : 17 + severity_text : ERROR + body : ExternalModule: error detected! + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : external_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112412571 + severity_num : 5 + severity_text : DEBUG + body : ExternalModule: some error details. + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : external_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112638912 + severity_num : 5 + severity_text : DEBUG + body : ExternalModule: Execute complete + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : external_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112799800 + severity_num : 5 + severity_text : DEBUG + body : MyModule: external_library returned an error + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112858234 + severity_num : 5 + severity_text : DEBUG + body : MyModule: Execute complete + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_library + version : + schema_url : + attributes : +} +{ + timestamp : 0 + observed_timestamp : 1783528026112886063 + severity_num : 13 + severity_text : WARN + body : MyApplication: my_library invoke failed with an error + resource : + telemetry.sdk.version: 1.28.0-dev + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: cpp + service.name: logger_configurator_example + attributes : + event_id : 0 + event_name : + trace_id : 00000000000000000000000000000000 + span_id : 0000000000000000 + trace_flags : 00 + scope : + name : my_application + version : + schema_url : + attributes : +} + +Stage 4: external_library error resolved, production baseline restored + my_application and my_library at Warn. external_library disabled +``` diff --git a/examples/logger_configurator/main.cc b/examples/logger_configurator/main.cc new file mode 100644 index 0000000000..e2a414df41 --- /dev/null +++ b/examples/logger_configurator/main.cc @@ -0,0 +1,294 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// This example shows how to use LoggerProvider::UpdateLoggerConfigurator to dynamically control +// the minimum severity level of loggers at runtime. Updating the LoggerConfigurator +// affects all existing and future loggers provided by the LoggerProvider. It is thread-safe and +// can be called concurrently with logger and log record creation. +// +// Three instrumentation scopes are shown: +// 1. "my_application" (example instrumented user application code), +// 2. "my_library" (example instrumented user library code), +// 3. "external_library" (example instrumented third-party dependency). +// +// The example simulates a typical debugging workflow: +// +// Stage 1: Production baseline. my_application and my_library loggers at Warn. +// external_library logger is disabled. +// Stage 2: A user reports unexpected behavior. Lower my_application and my_library loggers to +// Debug. +// external_library logger remains disabled. +// Stage 3: Debug logs implicate external_library. Re-enable its logger at Debug. +// Raise my_application logger back to Warn. +// Stage 4: Root cause found and fixed. Restore the production baseline. + +#include +#include +#include +#include +#include + +#include +#include +#include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/processor.h" + +#include "opentelemetry/exporters/ostream/log_record_exporter_factory.h" +#include "opentelemetry/logs/logger.h" +#include "opentelemetry/logs/logger_provider.h" +#include "opentelemetry/logs/provider.h" +#include "opentelemetry/logs/severity.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/logger_config.h" +#include "opentelemetry/sdk/logs/logger_provider.h" +#include "opentelemetry/sdk/logs/simple_log_record_processor_factory.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/semconv/service_attributes.h" + +namespace logs_api = opentelemetry::logs; +namespace logs_sdk = opentelemetry::sdk::logs; +namespace logs_exporters = opentelemetry::exporter::logs; +namespace scope_sdk = opentelemetry::sdk::instrumentationscope; +namespace nostd = opentelemetry::nostd; + +namespace +{ + +bool external_library_has_error = true; // simulate an error condition in the external library + +// --------------------------------------------------------------------------- +// EmitLogRecord Helper: For ABIv1 checks Enabled() based on severity of the record and emits. +// For ABI v2 EmitLogRecord applies filtering implicitly based on the LoggerConfig. +// --------------------------------------------------------------------------- +void Log(const nostd::shared_ptr &logger, + logs_api::Severity severity, + nostd::string_view message) noexcept +{ +#if OPENTELEMETRY_ABI_VERSION_NO <= 1 + // EmitLogRecord does not apply the minimum-severity filter in ABI v1. Check Enabled() before + // emitting. + if (logger->Enabled(severity)) + { + logger->EmitLogRecord(severity, message); + } +#else + // EmitLogRecord automatically applies the minimum-severity filter in ABI v2. + logger->EmitLogRecord(severity, message); +#endif +} + +// --------------------------------------------------------------------------- +// Example external library +// --------------------------------------------------------------------------- +namespace external_library +{ +class ExternalModule +{ +public: + ExternalModule() + : logger_(logs_api::Provider::GetLoggerProvider()->GetLogger("ExternalModule", + "external_library")) + {} + + bool Execute() + { + Log(logger_, logs_api::Severity::kDebug, "ExternalModule: Execute called..."); + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + Log(logger_, logs_api::Severity::kDebug, "ExternalModule: executing..."); + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + const bool has_error = external_library_has_error; // simulate an error condition + + if (has_error) + { + Log(logger_, logs_api::Severity::kError, "ExternalModule: error detected!"); + Log(logger_, logs_api::Severity::kDebug, "ExternalModule: some error details."); + } + std::this_thread::sleep_for(std::chrono::microseconds(1)); + Log(logger_, logs_api::Severity::kDebug, "ExternalModule: Execute complete"); + return !has_error; + } + +private: + nostd::shared_ptr logger_; +}; +} // namespace external_library + +// --------------------------------------------------------------------------- +// Example user library +// --------------------------------------------------------------------------- +namespace my_library +{ +class MyModule +{ +public: + MyModule() : logger_(logs_api::Provider::GetLoggerProvider()->GetLogger("MyModule", "my_library")) + {} + + bool Execute() + { + Log(logger_, logs_api::Severity::kDebug, "MyModule: Execute called..."); + + const auto result = external_module_.Execute(); + + if (!result) + { + Log(logger_, logs_api::Severity::kDebug, "MyModule: external_library returned an error"); + } + Log(logger_, logs_api::Severity::kDebug, "MyModule: Execute complete"); + return result; + } + +private: + nostd::shared_ptr logger_; + external_library::ExternalModule external_module_; +}; +} // namespace my_library + +// --------------------------------------------------------------------------- +// Example user application +// --------------------------------------------------------------------------- +class MyApplication +{ +public: + MyApplication() + : logger_( + logs_api::Provider::GetLoggerProvider()->GetLogger("MyApplication", "my_application")) + {} + + void Execute() + { + Log(logger_, logs_api::Severity::kDebug, "MyApplication: Execute called..."); + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + const auto result = my_module_.Execute(); + + if (!result) + { + Log(logger_, logs_api::Severity::kWarn, + "MyApplication: my_library invoke failed with an error"); + } + Log(logger_, logs_api::Severity::kDebug, "MyApplication: Execute complete"); + } + +private: + nostd::shared_ptr logger_; + my_library::MyModule my_module_; +}; + +// --------------------------------------------------------------------------- +// ScopeConfigurator builder helper +// --------------------------------------------------------------------------- + +// Builds a configurator that applies default_config to all scopes, with optional per-scope +// overrides. +std::unique_ptr> MakeLoggerConfigurator( + logs_sdk::LoggerConfig default_config, + std::initializer_list> overrides = {}) +{ + scope_sdk::ScopeConfigurator::Builder builder(default_config); + for (const auto &kv : overrides) + { + const auto &name = kv.first; + const auto &config = kv.second; + builder.AddConditionNameEquals(name, config); + } + return std::make_unique>(builder.Build()); +} + +// Creates a LoggerProvider with an OStreamLogRecordExporter and initial +// ScopeConfigurator. +std::shared_ptr CreateLoggerProvider( + std::unique_ptr> configurator) +{ + auto exporter = logs_exporters::OStreamLogRecordExporterFactory::Create(); + auto processor = logs_sdk::SimpleLogRecordProcessorFactory::Create(std::move(exporter)); + + return std::make_shared( + std::move(processor), + opentelemetry::sdk::resource::Resource::Create( + {{opentelemetry::semconv::service::kServiceName, "logger_configurator_example"}}), + std::move(configurator)); +} + +} // namespace + +int main() +{ + // Create LoggerConfig objects for the various configurations we will use in this example. + const bool is_logger_enabled = true; + const bool is_trace_based = false; + + // A LoggerConfig that enables the logger and sets the minimum severity to Warn. + const logs_sdk::LoggerConfig warn_config = + logs_sdk::LoggerConfig::Create(is_logger_enabled, logs_api::Severity::kWarn, is_trace_based); + + // A LoggerConfig that enables the logger and sets the minimum severity to Debug. + const logs_sdk::LoggerConfig debug_config = + logs_sdk::LoggerConfig::Create(is_logger_enabled, logs_api::Severity::kDebug, is_trace_based); + + // A LoggerConfig that disables the logger. + const logs_sdk::LoggerConfig disabled_config = logs_sdk::LoggerConfig::Disabled(); + + // ------------------------------------------------------------------------- + // Stage 1: Production baseline. + // my_application and my_library loggers at Warn. external_library logger is disabled. + // ------------------------------------------------------------------------- + auto sdk_logger_provider = CreateLoggerProvider( + MakeLoggerConfigurator(warn_config, {{"external_library", disabled_config}})); + + logs_api::Provider::SetLoggerProvider( + nostd::shared_ptr(sdk_logger_provider)); + + // Instantiate the application (this creates the loggers for all three scopes). + MyApplication my_app; + + std::cout << "Stage 1: production baseline\n"; + std::cout << " my_application and my_library at Warn. external_library disabled\n"; + my_app.Execute(); + + // ------------------------------------------------------------------------- + // Stage 2: User reports unexpected behavior. + // Lower my_application and my_library to Debug. external_library still disabled. + // ------------------------------------------------------------------------- + std::cout << "\nStage 2: investigating, loggers lowered to Debug\n"; + std::cout << " my_application and my_library at Debug. external_library disabled\n"; + + sdk_logger_provider->UpdateLoggerConfigurator( + MakeLoggerConfigurator(debug_config, {{"external_library", disabled_config}})); + + my_app.Execute(); + + // ------------------------------------------------------------------------- + // Stage 3: Debug logs implicate external_library. + // Enable its logger and my_library logger at Debug. Raise my_application logger back to + // Warn. + // ------------------------------------------------------------------------- + std::cout << "\nStage 3: narrowing down, my_library and external_library at Debug\n"; + std::cout << " my_library and external_library at Debug. my_application at Warn\n"; + + sdk_logger_provider->UpdateLoggerConfigurator(MakeLoggerConfigurator( + warn_config, {{"my_library", debug_config}, {"external_library", debug_config}})); + + my_app.Execute(); + + // ------------------------------------------------------------------------- + // Stage 4: Root cause found and fixed. Restore the production baseline. + // ------------------------------------------------------------------------- + std::cout << "\nStage 4: external_library error resolved, production baseline restored\n"; + std::cout << " my_application and my_library at Warn. external_library disabled\n"; + + external_library_has_error = false; // simulate that the error condition has been fixed + + sdk_logger_provider->UpdateLoggerConfigurator( + MakeLoggerConfigurator(warn_config, {{"external_library", disabled_config}})); + + my_app.Execute(); + + sdk_logger_provider->ForceFlush(); + sdk_logger_provider->Shutdown(); + return 0; +} diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index 6f72587bc9..44ed4ec1ee 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -91,6 +92,17 @@ class Logger final : public opentelemetry::logs::Logger #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 private: + // LoggerProvider needs access to UpdateLoggerConfig to propagate configuration updates to + // existing loggers. + friend class LoggerProvider; + + /** + * Update this logger's LoggerConfig. Called only by + * LoggerProvider::UpdateLoggerConfigurator when the provider-level + * LoggerConfigurator is replaced at runtime. + */ + void UpdateLoggerConfig(LoggerConfig config) noexcept; + // The name of this logger std::string logger_name_; @@ -98,7 +110,10 @@ class Logger final : public opentelemetry::logs::Logger // logger-context. std::unique_ptr instrumentation_scope_; std::shared_ptr context_; - LoggerConfig logger_config_; + // LoggerConfig state is stored in atomic variables to avoid locking in the hot path of Enabled() + // and EmitLogRecord(). + std::atomic logger_enabled_{true}; + std::atomic trace_based_{false}; static opentelemetry::logs::NoopLogger kNoopLogger; }; diff --git a/sdk/include/opentelemetry/sdk/logs/logger_context.h b/sdk/include/opentelemetry/sdk/logs/logger_context.h index 65d740f722..bf19fa879c 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_context.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_context.h @@ -93,6 +93,13 @@ class LoggerContext */ bool RecordableEnforcesLogRecordLimits() const noexcept; + /** + * Replace the ScopeConfigurator for this logger context. + * @param logger_configurator The new configurator. + */ + void SetLoggerConfigurator(std::unique_ptr> + logger_configurator) noexcept; + /** * Force all active LogProcessors to flush any buffered logs * within the given timeout. diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider.h b/sdk/include/opentelemetry/sdk/logs/logger_provider.h index a1cb054772..06ef66cc6a 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_provider.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider.h @@ -13,7 +13,9 @@ #include "opentelemetry/logs/logger_provider.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/logs/logger.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/resource/resource.h" @@ -107,6 +109,20 @@ class OPENTELEMETRY_EXPORT LoggerProvider final : public opentelemetry::logs::Lo */ void AddProcessor(std::unique_ptr processor) noexcept; + /** + * Update the LoggerConfigurator for this provider. Updates the LoggerConfig for all existing + * loggers. + * + * @param logger_configurator The new configurator. + * + * @note Calling LoggerProvider::GetLogger from within the + * ScopeConfigurator::ComputeConfig function (as a scope_matcher callback set with + * ScopeConfigurator::AddCondition) is not supported and will result in a deadlock. + */ + void UpdateLoggerConfigurator( + std::unique_ptr> + logger_configurator) noexcept; + /** * Obtain the resource associated with this logger provider. * @return The resource for this logger provider. diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 8f10f3a0b9..20bc6e4562 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include #include @@ -62,9 +63,9 @@ trace_api::SpanContext ExtractSpanContext( bool IsAllowedByTraceBasedFiltering( const nostd::variant &context_or_span, - const LoggerConfig &logger_config) noexcept + bool trace_based) noexcept { - if (!logger_config.IsTraceBased()) + if (!trace_based) { return true; } @@ -101,22 +102,28 @@ Logger::Logger( std::unique_ptr instrumentation_scope) noexcept : logger_name_(std::string(name)), instrumentation_scope_(std::move(instrumentation_scope)), - context_(std::move(context)), - logger_config_(context_->GetLoggerConfigurator().ComputeConfig(*instrumentation_scope_)) + context_(std::move(context)) { - SetMinimumSeverity(logger_config_.IsEnabled() - ? static_cast(logger_config_.GetMinimumSeverity()) - : opentelemetry::logs::kMaxSeverity); + LoggerConfig config = context_->GetLoggerConfigurator().ComputeConfig(*instrumentation_scope_); + UpdateLoggerConfig(config); +} + +void Logger::UpdateLoggerConfig(LoggerConfig config) noexcept +{ + logger_enabled_.store(config.IsEnabled(), std::memory_order_relaxed); + trace_based_.store(config.IsTraceBased(), std::memory_order_relaxed); + + SetMinimumSeverity(config.IsEnabled() ? static_cast(config.GetMinimumSeverity()) + : opentelemetry::logs::kMaxSeverity); #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - SetExtendedEnabledRequired(logger_config_.IsTraceBased() || - context_->GetProcessor().HasEnabledFilter()); + SetExtendedEnabledRequired(config.IsTraceBased() || context_->GetProcessor().HasEnabledFilter()); #endif // OPENTELEMETRY_ABI_VERSION_NO >= 2 } const opentelemetry::nostd::string_view Logger::GetName() noexcept { - if (!logger_config_.IsEnabled()) + if (!logger_enabled_.load(std::memory_order_relaxed)) { return kNoopLogger.GetName(); } @@ -125,7 +132,7 @@ const opentelemetry::nostd::string_view Logger::GetName() noexcept opentelemetry::nostd::unique_ptr Logger::CreateLogRecord() noexcept { - if (!logger_config_.IsEnabled()) + if (!logger_enabled_.load(std::memory_order_relaxed)) { return kNoopLogger.CreateLogRecord(); } @@ -151,7 +158,7 @@ opentelemetry::nostd::unique_ptr Logger::CreateL const nostd::variant &context_or_span) noexcept { - if (!logger_config_.IsEnabled()) + if (!logger_enabled_.load(std::memory_order_relaxed)) { return kNoopLogger.CreateLogRecord(); } @@ -173,7 +180,7 @@ opentelemetry::nostd::unique_ptr Logger::CreateL void Logger::EmitLogRecord( opentelemetry::nostd::unique_ptr &&log_record) noexcept { - if (!logger_config_.IsEnabled()) + if (!logger_enabled_.load(std::memory_order_relaxed)) { return kNoopLogger.EmitLogRecord(std::move(log_record)); } @@ -199,7 +206,7 @@ bool Logger::EnabledImplementation(opentelemetry::logs::Severity severity, { const nostd::variant current{ context::RuntimeContext::GetCurrent()}; - if (!IsAllowedByTraceBasedFiltering(current, logger_config_)) + if (!IsAllowedByTraceBasedFiltering(current, trace_based_.load(std::memory_order_relaxed))) { return false; } @@ -213,7 +220,7 @@ bool Logger::EnabledImplementation(opentelemetry::logs::Severity severity, { const nostd::variant current{ context::RuntimeContext::GetCurrent()}; - if (!IsAllowedByTraceBasedFiltering(current, logger_config_)) + if (!IsAllowedByTraceBasedFiltering(current, trace_based_.load(std::memory_order_relaxed))) { return false; } @@ -226,7 +233,8 @@ bool Logger::EnabledImplementation( const nostd::variant &context_or_span, opentelemetry::logs::Severity severity) const noexcept { - if (!IsAllowedByTraceBasedFiltering(context_or_span, logger_config_)) + if (!IsAllowedByTraceBasedFiltering(context_or_span, + trace_based_.load(std::memory_order_relaxed))) { return false; } @@ -239,7 +247,8 @@ bool Logger::EnabledImplementation( opentelemetry::logs::Severity severity, const opentelemetry::logs::EventId &event_id) const noexcept { - if (!IsAllowedByTraceBasedFiltering(context_or_span, logger_config_)) + if (!IsAllowedByTraceBasedFiltering(context_or_span, + trace_based_.load(std::memory_order_relaxed))) { return false; } diff --git a/sdk/src/logs/logger_context.cc b/sdk/src/logs/logger_context.cc index a1e8defbfa..7f6d5315ec 100644 --- a/sdk/src/logs/logger_context.cc +++ b/sdk/src/logs/logger_context.cc @@ -6,6 +6,8 @@ #include #include +#include "opentelemetry/sdk/common/global_log_handler.h" + #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/logs/log_record_limits.h" #include "opentelemetry/sdk/logs/logger_config.h" @@ -67,6 +69,19 @@ bool LoggerContext::RecordableEnforcesLogRecordLimits() const noexcept return recordable_enforces_limits_; } +void LoggerContext::SetLoggerConfigurator( + std::unique_ptr> + logger_configurator) noexcept +{ + if (!logger_configurator) + { + OTEL_INTERNAL_LOG_ERROR( + "[LoggerContext::SetLoggerConfigurator] logger_configurator must not be null, ignoring."); + return; + } + logger_configurator_ = std::move(logger_configurator); +} + bool LoggerContext::ForceFlush(std::chrono::microseconds timeout) noexcept { return processor_->ForceFlush(timeout); diff --git a/sdk/src/logs/logger_provider.cc b/sdk/src/logs/logger_provider.cc index ef345224f9..235c3bb4f8 100644 --- a/sdk/src/logs/logger_provider.cc +++ b/sdk/src/logs/logger_provider.cc @@ -107,6 +107,36 @@ void LoggerProvider::AddProcessor(std::unique_ptr processor) context_->AddProcessor(std::move(processor)); } +void LoggerProvider::UpdateLoggerConfigurator( + std::unique_ptr> + logger_configurator) noexcept +{ + if (!logger_configurator) + { + OTEL_INTERNAL_LOG_ERROR( + "[LoggerProvider::UpdateLoggerConfigurator] logger_configurator is null, " + "ignoring."); + return; + } + + // Lock the provider mutex to ensure that calls to GetLogger are exclusive with respect to the + // LoggerConfigurator update and corresponding LoggerConfig updates. This ensures that a Logger + // will never be returned from GetLogger with a LoggerConfig that is out of date with respect to + // the provider-level LoggerConfigurator. + const std::lock_guard guard(lock_); + context_->SetLoggerConfigurator(std::move(logger_configurator)); + + // The only way to set the LoggerConfig of a logger is on Logger construction in + // LoggerProvider::GetLogger or through Logger::UpdateLoggerConfig (which is private and only + // accessed by LoggerProvider). + for (auto &logger : loggers_) + { + LoggerConfig new_config = + context_->GetLoggerConfigurator().ComputeConfig(logger->GetInstrumentationScope()); + logger->UpdateLoggerConfig(new_config); + } +} + const opentelemetry::sdk::resource::Resource &LoggerProvider::GetResource() const noexcept { return context_->GetResource(); diff --git a/sdk/test/logs/logger_provider_sdk_test.cc b/sdk/test/logs/logger_provider_sdk_test.cc index 8fb47d2cfe..28c2b35932 100644 --- a/sdk/test/logs/logger_provider_sdk_test.cc +++ b/sdk/test/logs/logger_provider_sdk_test.cc @@ -3,37 +3,50 @@ #include #include +#include #include +#include +#include +#include #include +#include #include #include #include #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/timestamp.h" +#include "opentelemetry/logs/logger.h" #include "opentelemetry/logs/logger_provider.h" #include "opentelemetry/logs/provider.h" +#include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/utility.h" #include "opentelemetry/nostd/variant.h" +#include "opentelemetry/sdk/common/exporter_utils.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/logs/event_logger_provider.h" // IWYU pragma: keep #include "opentelemetry/sdk/logs/event_logger_provider_factory.h" // IWYU pragma: keep #include "opentelemetry/sdk/logs/exporter.h" #include "opentelemetry/sdk/logs/logger.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/provider.h" +#include "opentelemetry/sdk/logs/read_write_log_record.h" #include "opentelemetry/sdk/logs/recordable.h" #include "opentelemetry/sdk/logs/simple_log_record_processor.h" #include "opentelemetry/sdk/resource/resource.h" using namespace opentelemetry::sdk::logs; -namespace logs_api = opentelemetry::logs; -namespace logs_sdk = opentelemetry::sdk::logs; -namespace nostd = opentelemetry::nostd; +namespace logs_api = opentelemetry::logs; +namespace logs_sdk = opentelemetry::sdk::logs; +namespace nostd = opentelemetry::nostd; +namespace scope_sdk = opentelemetry::sdk::instrumentationscope; TEST(LoggerProviderSDK, PushToAPI) { @@ -210,6 +223,8 @@ TEST(LoggerProviderSDK, GetLoggerInequalityCheck) EXPECT_NE(logger_attribute_1, logger_attribute_2); } +namespace +{ class DummyLogRecordable final : public opentelemetry::sdk::logs::Recordable { public: @@ -255,6 +270,7 @@ class DummyProcessor : public LogRecordProcessor bool ForceFlush(std::chrono::microseconds /* timeout */) noexcept override { return true; } bool Shutdown(std::chrono::microseconds /* timeout */) noexcept override { return true; } }; +} // namespace TEST(LoggerProviderSDK, GetResource) { @@ -292,3 +308,336 @@ TEST(LoggerProviderSDK, ForceFlush) EXPECT_TRUE(lp.ForceFlush()); } + +// --------------------------------------------------------------------------- +// UpdateLoggerConfigurator tests +// --------------------------------------------------------------------------- +namespace +{ + +// Minimal recording exporter used across the UpdateLoggerConfigurator tests. +class RecordingExporter final : public logs_sdk::LogRecordExporter +{ +public: + std::unique_ptr MakeRecordable() noexcept override + { + return std::make_unique(); + } + + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::nostd::span> &records) noexcept + override + { + std::lock_guard lock(mutex_); + for (auto &r : records) + { + records_.push_back(std::move(r)); + } + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + + bool ForceFlush(std::chrono::microseconds /*timeout*/) noexcept override { return true; } + + bool Shutdown(std::chrono::microseconds /*timeout*/) noexcept override { return true; } + + std::size_t RecordCount() const + { + std::lock_guard lock(mutex_); + return records_.size(); + } + + void Reset() + { + std::lock_guard lock(mutex_); + records_.clear(); + } + +private: + mutable std::mutex mutex_; + std::vector> records_; +}; + +// Helper: make a ScopeConfigurator where all loggers default to enabled with the given min +// severity. +std::unique_ptr> AllAtSeverity( + logs_api::Severity severity) +{ + return std::make_unique>( + scope_sdk::ScopeConfigurator::Builder( + logs_sdk::LoggerConfig::Create(true, severity, false)) + .Build()); +} + +// Helper: make a ScopeConfigurator where the named scope is disabled; default is enabled. +std::unique_ptr> DisableByName( + opentelemetry::nostd::string_view name) +{ + return std::make_unique>( + scope_sdk::ScopeConfigurator::Builder( + logs_sdk::LoggerConfig::Default()) + .AddConditionNameEquals(name, logs_sdk::LoggerConfig::Disabled()) + .Build()); +} + +// Helper: make a ScopeConfigurator that disables all loggers. +std::unique_ptr> DisableAll() +{ + return std::make_unique>( + scope_sdk::ScopeConfigurator::Builder( + logs_sdk::LoggerConfig::Disabled()) + .Build()); +} + +// Helper: make a ScopeConfigurator that enables all loggers (default config). +std::unique_ptr> EnableAll() +{ + return std::make_unique>( + scope_sdk::ScopeConfigurator::Builder( + logs_sdk::LoggerConfig::Default()) + .Build()); +} + +// Helper: Info default for all scopes with the named scope set to a specific minimum severity +// override. +std::unique_ptr> InfoWithOverrideSeverity( + opentelemetry::nostd::string_view scope_name, + logs_api::Severity override_severity) +{ + return std::make_unique>( + scope_sdk::ScopeConfigurator::Builder( + logs_sdk::LoggerConfig::Create(true, logs_api::Severity::kInfo, false)) + .AddConditionNameEquals(scope_name, + logs_sdk::LoggerConfig::Create(true, override_severity, false)) + .Build()); +} + +// Builds a LoggerProvider whose RecordingExporter pointer is returned via the out parameter. +std::unique_ptr MakeProvider( + RecordingExporter *&exporter_out, + std::unique_ptr> configurator = + EnableAll()) +{ + auto exporter = std::make_unique(); + exporter_out = exporter.get(); + auto processor = std::make_unique(std::move(exporter)); + return std::make_unique( + std::move(processor), opentelemetry::sdk::resource::Resource::Create({}), + std::move(configurator)); +} + +} // namespace + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorDisableByName) +{ + RecordingExporter *exporter{}; + auto provider = MakeProvider(exporter); + ASSERT_TRUE(exporter != nullptr); + + auto logger_disabled_by_update = provider->GetLogger("logger.disabled", "scope.disabled"); + auto logger_unaffected = provider->GetLogger("logger.unaffected", "scope.unaffected"); + + // Both loggers are initially enabled. + ASSERT_TRUE(logger_disabled_by_update->Enabled(logs_api::Severity::kInfo)); + ASSERT_TRUE(logger_unaffected->Enabled(logs_api::Severity::kInfo)); + + // Disable scope.disabled by name; scope.unaffected must remain enabled. + provider->UpdateLoggerConfigurator(DisableByName("scope.disabled")); + + EXPECT_FALSE(logger_disabled_by_update->Enabled(logs_api::Severity::kInfo)); + EXPECT_TRUE(logger_unaffected->Enabled(logs_api::Severity::kInfo)); + + // Verify that emission is suppressed for the disabled logger. + exporter->Reset(); + logger_disabled_by_update->Info("should not appear"); + logger_unaffected->Info("should appear"); + EXPECT_EQ(exporter->RecordCount(), 1u); +} + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorReEnable) +{ + RecordingExporter *exporter{}; + auto provider = MakeProvider(exporter, DisableAll()); + ASSERT_TRUE(exporter != nullptr); + + auto existing_logger = provider->GetLogger("existing", "scope.existing"); + + ASSERT_FALSE(existing_logger->Enabled(logs_api::Severity::kInfo)); + + // enable all loggers. + provider->UpdateLoggerConfigurator(EnableAll()); + + // The existing logger handle must immediately reflect the updated config. + EXPECT_TRUE(existing_logger->Enabled(logs_api::Severity::kInfo)); + + // Loggers obtained after the update also reflect the new configurator. + auto logger_after = provider->GetLogger("after", "scope.after"); + EXPECT_TRUE(logger_after->Enabled(logs_api::Severity::kInfo)); +} + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorMinSeverityChange) +{ + RecordingExporter *exporter{}; + // Start with min severity = Info. + auto provider = MakeProvider(exporter, AllAtSeverity(logs_api::Severity::kInfo)); + + ASSERT_TRUE(exporter != nullptr); + + auto logger = provider->GetLogger("mylogger", "scope.test"); + + // At Info threshold: Debug is suppressed, Info passes. + EXPECT_FALSE(logger->Enabled(logs_api::Severity::kDebug)); + EXPECT_TRUE(logger->Enabled(logs_api::Severity::kInfo)); + EXPECT_TRUE(logger->Enabled(logs_api::Severity::kError)); + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + // In ABIv2, emit filtering is implicit + exporter->Reset(); + ASSERT_EQ(exporter->RecordCount(), 0u); + logger->Debug("debug msg"); // filtered + logger->Info("info msg"); // passes + logger->Error("error msg"); // passes + EXPECT_EQ(exporter->RecordCount(), 2u); +#endif + + // Raise min severity to Warn. + provider->UpdateLoggerConfigurator(AllAtSeverity(logs_api::Severity::kWarn)); + + EXPECT_FALSE(logger->Enabled(logs_api::Severity::kInfo)); + EXPECT_TRUE(logger->Enabled(logs_api::Severity::kWarn)); + EXPECT_TRUE(logger->Enabled(logs_api::Severity::kError)); + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + exporter->Reset(); + ASSERT_EQ(exporter->RecordCount(), 0u); + logger->Info("info msg"); // filtered + logger->Warn("warn msg"); // passes + logger->Error("error msg"); // passes + EXPECT_EQ(exporter->RecordCount(), 2u); +#endif + + // Drop min severity to Debug. + provider->UpdateLoggerConfigurator(AllAtSeverity(logs_api::Severity::kDebug)); + + EXPECT_TRUE(logger->Enabled(logs_api::Severity::kDebug)); + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + exporter->Reset(); + ASSERT_EQ(exporter->RecordCount(), 0u); + logger->Debug("debug msg"); // passes + logger->Info("info msg"); // passes + EXPECT_EQ(exporter->RecordCount(), 2u); +#endif +} + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorPerScopeMinSeverity) +{ + RecordingExporter *exporter{}; + // Default Info; "scope.verbose" gets Debug. + auto provider = + MakeProvider(exporter, InfoWithOverrideSeverity("scope.verbose", logs_api::Severity::kDebug)); + + ASSERT_TRUE(exporter != nullptr); + + auto logger_default = provider->GetLogger("default", "scope.default"); + auto logger_verbose = provider->GetLogger("verbose", "scope.verbose"); + + // scope.default filtered at Debug, passes at Info. + EXPECT_FALSE(logger_default->Enabled(logs_api::Severity::kDebug)); + EXPECT_TRUE(logger_default->Enabled(logs_api::Severity::kInfo)); + + // scope.verbose passes at Debug. + EXPECT_TRUE(logger_verbose->Enabled(logs_api::Severity::kDebug)); + + // Now raise scope.verbose to Warn; everything else stays at Info. + provider->UpdateLoggerConfigurator( + InfoWithOverrideSeverity("scope.verbose", logs_api::Severity::kWarn)); + + EXPECT_FALSE(logger_verbose->Enabled(logs_api::Severity::kInfo)); + EXPECT_TRUE(logger_verbose->Enabled(logs_api::Severity::kWarn)); + EXPECT_TRUE(logger_default->Enabled(logs_api::Severity::kInfo)); +} + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorNewLoggerUsesUpdatedConfig) +{ + RecordingExporter *exporter{}; + auto provider = MakeProvider(exporter); + ASSERT_TRUE(exporter != nullptr); + + // Install a configurator that disables "scope.disabled" before any logger for that scope exists. + provider->UpdateLoggerConfigurator(DisableByName("scope.disabled")); + + // A logger obtained after the update must reflect the new config. + auto logger_for_disabled_scope = provider->GetLogger("disabled", "scope.disabled"); + EXPECT_FALSE(logger_for_disabled_scope->Enabled(logs_api::Severity::kInfo)); + + // Scopes not named in the configurator remain enabled. + auto logger_for_unaffected_scope = provider->GetLogger("unaffected", "scope.unaffected"); + EXPECT_TRUE(logger_for_unaffected_scope->Enabled(logs_api::Severity::kInfo)); +} + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorNullIgnored) +{ + RecordingExporter *exporter{}; + auto provider = MakeProvider(exporter, AllAtSeverity(logs_api::Severity::kInfo)); + ASSERT_TRUE(exporter != nullptr); + + auto logger = provider->GetLogger("mylogger", "scope.test"); + ASSERT_TRUE(logger->Enabled(logs_api::Severity::kInfo)); + + // Passing nullptr must be a no-op (logged as an error internally, config unchanged). + provider->UpdateLoggerConfigurator(nullptr); + + EXPECT_TRUE(logger->Enabled(logs_api::Severity::kInfo)); +} + +TEST(LoggerProviderSDK, UpdateLoggerConfiguratorConcurrentEmit) +{ + RecordingExporter *exporter{}; + auto provider = MakeProvider(exporter, AllAtSeverity(logs_api::Severity::kInfo)); + ASSERT_TRUE(exporter != nullptr); + + auto logger = provider->GetLogger("mylogger", "scope.concurrent"); + + std::atomic stop{false}; + std::atomic worker_saw_suppressed{false}; + std::atomic worker_saw_emitted{false}; + + std::promise worker_ready; + std::future worker_ready_future = worker_ready.get_future(); + + // Worker: call Enabled() and emit in a tight loop; flag each observed state. + std::thread worker([&] { + worker_ready.set_value(); + while (!stop.load(std::memory_order_relaxed)) + { + if (logger->Enabled(logs_api::Severity::kInfo)) + { + logger->Info("emit"); + worker_saw_emitted.store(true, std::memory_order_relaxed); + } + else + { + worker_saw_suppressed.store(true, std::memory_order_relaxed); + } + } + }); + + worker_ready_future.wait(); + + // Raise min severity so Info is suppressed; wait for worker to observe it. + provider->UpdateLoggerConfigurator(AllAtSeverity(logs_api::Severity::kError)); + while (!worker_saw_suppressed.load(std::memory_order_relaxed)) + { + std::this_thread::yield(); + } + + // Lower min severity back to Info; wait for worker to observe it. + provider->UpdateLoggerConfigurator(AllAtSeverity(logs_api::Severity::kInfo)); + while (!worker_saw_emitted.load(std::memory_order_relaxed)) + { + std::this_thread::yield(); + } + + stop.store(true, std::memory_order_relaxed); + worker.join(); +} From 234ee7018006661937f9d8f7438b66a9ed698902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:53:31 +0200 Subject: [PATCH 38/49] Bump actions/stale from 10.3.0 to 10.4.0 (#4242) Bumps [actions/stale](https://github.com/actions/stale) from 10.3.0 to 10.4.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899...1e223db275d687790206a7acac4d1a11bd6fe629) --- updated-dependencies: - dependency-name: actions/stale dependency-version: 10.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d88faeb8bf..b03a689fdd 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write # for actions/stale to close stale PRs runs-on: ubuntu-latest steps: - - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 + - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 with: stale-issue-message: "This issue was marked as stale due to lack of activity." days-before-issue-stale: 60 From 1df66ed2c5d6577906ba0c77ad33ff4cd2becec9 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Tue, 14 Jul 2026 07:57:38 -0400 Subject: [PATCH 39/49] [CMAKE] Remove environment variable support from the add external components script (#4211) * remove feature to add external components to the opentelemetry-cpp CMake build using environment variables in favor or users building external components with opentelemetry-cpp as a dependency imported through find_package or FetchContent * Adds a message if the external component is added. Clean up comments to remove env var support * Update cmake/opentelemetry-build-external-component.cmake Co-authored-by: Tom Tan --------- Co-authored-by: Marc Alff Co-authored-by: Tom Tan --- ...entelemetry-build-external-component.cmake | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/cmake/opentelemetry-build-external-component.cmake b/cmake/opentelemetry-build-external-component.cmake index 74b6515209..0c4fd39c12 100644 --- a/cmake/opentelemetry-build-external-component.cmake +++ b/cmake/opentelemetry-build-external-component.cmake @@ -13,37 +13,16 @@ endfunction() # Enable building external components through otel-cpp build # The config options are # - OPENTELEMETRY_EXTERNAL_COMPONENT_PATH - Setting local paths of the external -# component as env variable. Multiple paths can be set by separating them with. -# - OPENTELEMETRY_EXTERNAL_COMPONENT_URL Setting github-repo of external component -# as env variable +# component. Multiple paths can be set by separating them with semicolons. -# Add custom vendor component from local path, or GitHub repo -# Prefer CMake option, then env variable, then URL. +# Add custom vendor component from local path +# CMake option. if(OPENTELEMETRY_EXTERNAL_COMPONENT_PATH) + message(STATUS "Building external components from local path(s): ${OPENTELEMETRY_EXTERNAL_COMPONENT_PATH}") # Add custom component path to build tree and consolidate binary artifacts in # current project binary output directory. foreach(DIR IN LISTS OPENTELEMETRY_EXTERNAL_COMPONENT_PATH) get_directory_name_in_path(${DIR} EXTERNAL_EXPORTER_DIR_NAME) add_subdirectory(${DIR} ${PROJECT_BINARY_DIR}/external/${EXTERNAL_EXPORTER_DIR_NAME}) endforeach() -elseif(DEFINED ENV{OPENTELEMETRY_EXTERNAL_COMPONENT_PATH}) - # Add custom component path to build tree and consolidate binary artifacts in - # current project binary output directory. - set(OPENTELEMETRY_EXTERNAL_COMPONENT_PATH_VAR $ENV{OPENTELEMETRY_EXTERNAL_COMPONENT_PATH}) - foreach(DIR IN LISTS OPENTELEMETRY_EXTERNAL_COMPONENT_PATH_VAR) - get_directory_name_in_path(${DIR} EXTERNAL_EXPORTER_DIR_NAME) - add_subdirectory(${DIR} ${PROJECT_BINARY_DIR}/external/${EXTERNAL_EXPORTER_DIR_NAME}) - endforeach() -elseif(DEFINED $ENV{OPENTELEMETRY_EXTERNAL_COMPONENT_URL}) - # This option requires CMake 3.11+: add standard remote repo to build tree. - include(FetchContent) - FetchContent_Declare( - external - GIT_REPOSITORY $ENV{OPENTELEMETRY_EXTERNAL_COMPONENT_URL} - GIT_TAG "main") - FetchContent_GetProperties(external) - if(NOT external_POPULATED) - FetchContent_Populate(external) - add_subdirectory(${external_SOURCE_DIR}) - endif() endif() From 2fdf929c2e8c06bde821d3373ab046248baf15db Mon Sep 17 00:00:00 2001 From: Tom Tan Date: Wed, 15 Jul 2026 10:48:16 -0700 Subject: [PATCH 40/49] [ETW] Make spans own their names (#4247) --- CHANGELOG.md | 3 +++ .../opentelemetry/exporters/etw/etw_tracer.h | 6 +++--- exporters/etw/test/etw_tracer_test.cc | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c57ff6451..08a3c09304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [ETW] Ensure spans own their names until they are ended + [#4247](https://github.com/open-telemetry/opentelemetry-cpp/pull/4247) + * [SDK] Apply metric cardinality limits to non-overflow attribute sets and reserve the overflow point separately. [#4236](https://github.com/open-telemetry/opentelemetry-cpp/pull/4236) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 2b975e239c..57db2f6510 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -788,7 +788,7 @@ class Span : public opentelemetry::trace::Span /** * @brief Span name. */ - nostd::string_view name_; + std::string name_; /** * @brief Attribute indicating that the span has ended. @@ -852,7 +852,7 @@ class Span : public opentelemetry::trace::Span * @brief Get Span Name. * @return Span Name. */ - nostd::string_view GetName() const { return name_; } + nostd::string_view GetName() const { return nostd::string_view{name_.data(), name_.size()}; } /** * @brief Span constructor @@ -870,10 +870,10 @@ class Span : public opentelemetry::trace::Span : opentelemetry::trace::Span(), start_time_(std::chrono::system_clock::now()), owner_(owner), + name_(name.data(), name.size()), context_(std::move(spanContext)), parent_(parent) { - name_ = name; UNREFERENCED_PARAMETER(options); } diff --git a/exporters/etw/test/etw_tracer_test.cc b/exporters/etw/test/etw_tracer_test.cc index 7d32a2ffba..c61534c761 100644 --- a/exporters/etw/test/etw_tracer_test.cc +++ b/exporters/etw/test/etw_tracer_test.cc @@ -503,6 +503,20 @@ TEST(ETWTracer, AlwayOffTailSampler) auto tracer = tp.GetTracer(providerName); } +TEST(ETWTracer, SpanOwnsName) +{ + exporter::etw::TracerProvider tp; + auto tracer = tp.GetTracer("SpanOwnsName"); + std::string name = "original"; + auto span = tracer->StartSpan(name); + + name[0] = 'X'; + + auto etw_span = static_cast(span.get()); + EXPECT_EQ(etw_span->GetName(), "original"); + span->End(); +} + TEST(ETWTracer, CustomIdGenerator) { std::string providerName = kGlobalProviderName; // supply unique instrumentation name here From 8f60b0ce375174bc60446a825dff417dc4bedf34 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:40:27 -0400 Subject: [PATCH 41/49] [SDK] Span limits configuration (#4232) --- CHANGELOG.md | 5 + .../otlp/otlp_grpc_exporter_options.h | 6 +- .../otlp/otlp_http_exporter_options.h | 6 +- .../exporters/otlp/otlp_recordable.h | 37 ++- exporters/otlp/src/otlp_recordable.cc | 40 ++- exporters/otlp/test/otlp_recordable_test.cc | 272 ++++++++++++++++++ .../sdk/common/attribute_utils.h | 84 +++++- .../configuration/span_limits_configuration.h | 10 +- .../sdk/trace/multi_recordable.h | 2 + .../opentelemetry/sdk/trace/recordable.h | 7 + .../opentelemetry/sdk/trace/span_data.h | 45 ++- .../opentelemetry/sdk/trace/span_limits.h | 68 +++++ sdk/include/opentelemetry/sdk/trace/tracer.h | 4 + .../opentelemetry/sdk/trace/tracer_context.h | 11 +- .../opentelemetry/sdk/trace/tracer_provider.h | 14 +- .../sdk/trace/tracer_provider_factory.h | 17 ++ sdk/src/configuration/configuration_parser.cc | 24 +- sdk/src/configuration/sdk_builder.cc | 34 ++- sdk/src/trace/multi_recordable.cc | 8 + sdk/src/trace/span.cc | 1 + sdk/src/trace/span_data.cc | 113 +++++++- sdk/src/trace/tracer_context.cc | 23 +- sdk/src/trace/tracer_provider.cc | 23 +- sdk/src/trace/tracer_provider_factory.cc | 29 +- sdk/test/common/attribute_utils_test.cc | 20 ++ sdk/test/configuration/sdk_builder_test.cc | 55 ++++ sdk/test/configuration/yaml_trace_test.cc | 73 +++++ sdk/test/trace/span_data_test.cc | 143 +++++++++ sdk/test/trace/tracer_provider_test.cc | 75 ++++- sdk/test/trace/tracer_test.cc | 54 +++- 30 files changed, 1216 insertions(+), 87 deletions(-) create mode 100644 sdk/include/opentelemetry/sdk/trace/span_limits.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 08a3c09304..a654eb3243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,11 @@ Increment the: * [CMAKE] Fix and test WITH_API_ONLY option [#4201](https://github.com/open-telemetry/opentelemetry-cpp/pull/4201) +* [SDK] Span limits can now be configured through the TracerProvider interface + and declaritive configuration. When set, limits are enforced by the + SpanData and OtlpRecordables recordables used by the standard exporters + [#4232](https://github.com/open-telemetry/opentelemetry-cpp/pull/4232) + * [API] Fix `TraceState::IsValidKey()` to comply with the W3C Trace Context Level 2, where keys containing `@` and keys with more than 241 characters before `@` or more than 14 characters after `@` are now accepted. diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h index 2bb4ab6542..94d028978f 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h @@ -35,7 +35,11 @@ struct OPENTELEMETRY_EXPORT OtlpGrpcExporterOptions : public OtlpGrpcClientOptio OtlpGrpcExporterOptions &operator=(OtlpGrpcExporterOptions &&) = default; ~OtlpGrpcExporterOptions() override; - /** Collection Limits. No limit by default. */ + /** Collection Limits. No limit by default. + * @deprecated Configure span limits via TracerProviderFactory::Create(), SpanLimitsConfiguration, + * or the YAML `tracer_provider.limits` node instead. These fields will be + * removed in a future release. + */ std::uint32_t max_attributes = (std::numeric_limits::max)(); std::uint32_t max_events = (std::numeric_limits::max)(); std::uint32_t max_links = (std::numeric_limits::max)(); diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter_options.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter_options.h index 58fad0485e..b146fd3b0a 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter_options.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter_options.h @@ -127,7 +127,11 @@ struct OPENTELEMETRY_EXPORT OtlpHttpExporterOptions /** The backoff will be multiplied by this value after each retry attempt. */ float retry_policy_backoff_multiplier{}; - /** Collection Limits. No limit by default. */ + /** Collection Limits. No limit by default. + * @deprecated Configure span limits via TracerProviderFactory::Create(), SpanLimitsConfiguration, + * or the YAML `tracer_provider.limits` node instead. These fields will be + * removed in a future release. + */ std::uint32_t max_attributes = (std::numeric_limits::max)(); std::uint32_t max_events = (std::numeric_limits::max)(); std::uint32_t max_links = (std::numeric_limits::max)(); diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h index b63bc5ff7d..d4c30483f8 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h @@ -23,6 +23,7 @@ #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" @@ -37,17 +38,25 @@ namespace otlp class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable { public: + /** + * Construct with optional span limits parameters. + * + * @deprecated Configure span limits via TracerProviderFactory::Create(), SpanLimitsConfiguration, + * or the YAML `tracer_provider.limits` node instead. These optional span limit params will be + * removed in a future release. + */ explicit OtlpRecordable( std::uint32_t max_attributes = (std::numeric_limits::max)(), std::uint32_t max_events = (std::numeric_limits::max)(), std::uint32_t max_links = (std::numeric_limits::max)(), std::uint32_t max_attributes_per_event = (std::numeric_limits::max)(), std::uint32_t max_attributes_per_link = (std::numeric_limits::max)()) - : max_attributes_(max_attributes), - max_events_(max_events), - max_links_(max_links), - max_attributes_per_event_(max_attributes_per_event), - max_attributes_per_link_(max_attributes_per_link) + : span_limits_{max_attributes, + (std::numeric_limits::max)(), + max_events, + max_links, + max_attributes_per_event, + max_attributes_per_link} {} proto::trace::v1::Span &span() noexcept { return span_; } @@ -92,6 +101,17 @@ class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable void SetDuration(std::chrono::nanoseconds duration) noexcept override; + /** + * Set span limits. + * + * Until the deprecated OTLP exporter option values are removed, the effective limit for each + * field is the minimum (most restrictive) of the deprecated otlp exporter option values (passed + * to the constructor) and the values set by calling this method. + * + * @param limits The span limits to set. + */ + void SetSpanLimits(const opentelemetry::sdk::trace::SpanLimits &limits) noexcept override; + void SetInstrumentationScope(const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope) noexcept override; @@ -100,11 +120,8 @@ class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable const opentelemetry::sdk::resource::Resource *resource_ = nullptr; const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_ = nullptr; - std::uint32_t max_attributes_; - std::uint32_t max_events_; - std::uint32_t max_links_; - std::uint32_t max_attributes_per_event_; - std::uint32_t max_attributes_per_link_; + opentelemetry::sdk::trace::SpanLimits span_limits_{ + opentelemetry::sdk::trace::SpanLimits::NoLimits()}; }; } // namespace otlp } // namespace exporter diff --git a/exporters/otlp/src/otlp_recordable.cc b/exporters/otlp/src/otlp_recordable.cc index 9270e17383..017fc51905 100644 --- a/exporters/otlp/src/otlp_recordable.cc +++ b/exporters/otlp/src/otlp_recordable.cc @@ -1,8 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include +#include #include +#include #include #include "opentelemetry/common/attribute_value.h" @@ -15,6 +16,7 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" @@ -114,24 +116,40 @@ void OtlpRecordable::SetResource(const sdk::resource::Resource &resource) noexce resource_ = &resource; } +void OtlpRecordable::SetSpanLimits(const opentelemetry::sdk::trace::SpanLimits &limits) noexcept +{ + // Update the span limits to be the minimum of the current limits and the new limits + // The initial limits are set on construction from the deprecated span limits otlp options. + // TODO: replace with a simple copy once the deprecated options are removed. + span_limits_ = { + (std::min)(span_limits_.attribute_count_limit, limits.attribute_count_limit), + (std::min)(span_limits_.attribute_value_length_limit, limits.attribute_value_length_limit), + (std::min)(span_limits_.event_count_limit, limits.event_count_limit), + (std::min)(span_limits_.link_count_limit, limits.link_count_limit), + (std::min)(span_limits_.event_attribute_count_limit, limits.event_attribute_count_limit), + (std::min)(span_limits_.link_attribute_count_limit, limits.link_attribute_count_limit), + }; +} + void OtlpRecordable::SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept { - if (static_cast(span_.attributes_size()) >= max_attributes_) + if (static_cast(span_.attributes_size()) >= span_limits_.attribute_count_limit) { span_.set_dropped_attributes_count(span_.dropped_attributes_count() + 1); return; } auto *attribute = span_.add_attributes(); - OtlpPopulateAttributeUtils::PopulateAttribute(attribute, key, value, false); + OtlpPopulateAttributeUtils::PopulateAttribute(attribute, key, value, false, + span_limits_.attribute_value_length_limit); } void OtlpRecordable::AddEvent(nostd::string_view name, common::SystemTimestamp timestamp, const common::KeyValueIterable &attributes) noexcept { - if (static_cast(span_.events_size()) >= max_events_) + if (static_cast(span_.events_size()) >= span_limits_.event_count_limit) { span_.set_dropped_events_count(span_.dropped_events_count() + 1); return; @@ -142,12 +160,14 @@ void OtlpRecordable::AddEvent(nostd::string_view name, event->set_time_unix_nano(timestamp.time_since_epoch().count()); attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { - if (static_cast(event->attributes_size()) >= max_attributes_per_event_) + if (static_cast(event->attributes_size()) >= + span_limits_.event_attribute_count_limit) { event->set_dropped_attributes_count(event->dropped_attributes_count() + 1); return true; } - OtlpPopulateAttributeUtils::PopulateAttribute(event->add_attributes(), key, value, false); + OtlpPopulateAttributeUtils::PopulateAttribute(event->add_attributes(), key, value, false, + span_limits_.attribute_value_length_limit); return true; }); } @@ -155,7 +175,7 @@ void OtlpRecordable::AddEvent(nostd::string_view name, void OtlpRecordable::AddLink(const trace::SpanContext &span_context, const common::KeyValueIterable &attributes) noexcept { - if (static_cast(span_.links_size()) >= max_links_) + if (static_cast(span_.links_size()) >= span_limits_.link_count_limit) { span_.set_dropped_links_count(span_.dropped_links_count() + 1); return; @@ -167,12 +187,14 @@ void OtlpRecordable::AddLink(const trace::SpanContext &span_context, trace::SpanId::kSize); link->set_trace_state(span_context.trace_state()->ToHeader()); attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { - if (static_cast(link->attributes_size()) >= max_attributes_per_link_) + if (static_cast(link->attributes_size()) >= + span_limits_.link_attribute_count_limit) { link->set_dropped_attributes_count(link->dropped_attributes_count() + 1); return true; } - OtlpPopulateAttributeUtils::PopulateAttribute(link->add_attributes(), key, value, false); + OtlpPopulateAttributeUtils::PopulateAttribute(link->add_attributes(), key, value, false, + span_limits_.attribute_value_length_limit); return true; }); } diff --git a/exporters/otlp/test/otlp_recordable_test.cc b/exporters/otlp/test/otlp_recordable_test.cc index 1645b61b5f..2481383f85 100644 --- a/exporters/otlp/test/otlp_recordable_test.cc +++ b/exporters/otlp/test/otlp_recordable_test.cc @@ -24,6 +24,7 @@ #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" @@ -663,6 +664,7 @@ TEST(OtlpRecordable, SetUint64ArrayOverflowAsStringPerSpec) EXPECT_EQ(array_v.values(1).string_value(), std::to_string(overflow_val)); } +// TODO: remove this test case once the deprecated otlp options span limit fields are removed. TEST(OtlpRecordableTest, TestCollectionLimits) { // Initialize recordable with strict limits: @@ -704,6 +706,7 @@ TEST(OtlpRecordableTest, TestCollectionLimits) EXPECT_EQ(event_list[0].dropped_attributes_count(), 1); } +// TODO: remove this test case once the deprecated otlp options span limit fields are removed. TEST(OtlpRecordableTest, TestLinkLimits) { // Limits: Max Links: 2, Max Attributes Per Link: 1 @@ -785,6 +788,275 @@ TEST(OtlpRecordable, PopulateRequestSameScope) EXPECT_EQ(req.resource_spans(0).scope_spans(0).spans_size(), 2); EXPECT_EQ(req.resource_spans(0).scope_spans(0).scope().name(), "lib"); } + +TEST(OtlpRecordable, SpanLimits) +{ + OtlpRecordable data; + opentelemetry::sdk::trace::SpanLimits limits; + limits.attribute_count_limit = 1; + limits.attribute_value_length_limit = 2; + limits.link_count_limit = 1; + limits.link_attribute_count_limit = 1; + limits.event_count_limit = 1; + limits.event_attribute_count_limit = 1; + + constexpr const char *kKey1 = "one"; + constexpr const char *kKey2 = "two"; + constexpr const char *kValue1 = "1234"; + constexpr const char *kValue2 = "5678"; + + std::map attribute_collection{{kKey1, kValue1}, {kKey2, kValue2}}; + + const auto &proto_span = data.span(); + + data.SetSpanLimits(limits); + + // span attribute count and length limits + data.SetAttribute(kKey1, kValue1); + data.SetAttribute(kKey2, kValue2); + + EXPECT_EQ(proto_span.attributes_size(), 1); + EXPECT_EQ(proto_span.dropped_attributes_count(), 1); + EXPECT_EQ(proto_span.attributes(0).value().string_value(), "12"); + + // event count and per-event attribute count limits + data.AddEvent("event1", std::chrono::system_clock::now(), + common::MakeAttributes(attribute_collection)); + data.AddEvent("event2", std::chrono::system_clock::now(), + common::MakeAttributes(attribute_collection)); + + ASSERT_EQ(proto_span.events_size(), 1); + EXPECT_EQ(proto_span.dropped_events_count(), 1); + const auto &event = proto_span.events(0); + EXPECT_EQ(event.dropped_attributes_count(), 1); + EXPECT_EQ(event.name(), "event1"); + const auto &event_attributes = event.attributes(); + EXPECT_EQ(event_attributes.size(), 1); + EXPECT_EQ(event_attributes.at(0).value().string_value(), "12"); + + // link count and per-link attribute count limits + data.AddLink(trace_api::SpanContext(true, false), common::MakeAttributes(attribute_collection)); + data.AddLink(trace_api::SpanContext(true, false), common::MakeAttributes(attribute_collection)); + ASSERT_EQ(proto_span.links_size(), 1); + EXPECT_EQ(proto_span.dropped_links_count(), 1); + const auto &link = proto_span.links(0); + EXPECT_EQ(link.dropped_attributes_count(), 1); + const auto &link_attributes = link.attributes(); + EXPECT_EQ(link_attributes.size(), 1); + EXPECT_EQ(link_attributes.at(0).value().string_value(), "12"); +} + +TEST(OtlpRecordable, SpanLimitsNoLimitDefault) +{ + constexpr std::uint32_t kMaxCount = 500; + OtlpRecordable recordable; + std::map attributes; + + for (std::uint32_t i = 0; i < kMaxCount; ++i) + { + attributes["attribute_" + std::to_string(i)] = std::to_string(i); + } + + for (std::uint32_t i = 0; i < kMaxCount; ++i) + { + recordable.SetAttribute("attribute_" + std::to_string(i), i); + recordable.AddEvent("event_" + std::to_string(i), std::chrono::system_clock::now(), + common::MakeAttributes(attributes)); + recordable.AddLink(trace::SpanContext::GetInvalid(), common::MakeAttributes(attributes)); + } + + EXPECT_EQ(recordable.span().attributes_size(), kMaxCount); + EXPECT_EQ(recordable.span().dropped_attributes_count(), 0u); + EXPECT_EQ(recordable.span().events_size(), kMaxCount); + EXPECT_EQ(recordable.span().dropped_events_count(), 0u); + EXPECT_EQ(recordable.span().links_size(), kMaxCount); + EXPECT_EQ(recordable.span().dropped_links_count(), 0u); + EXPECT_EQ(recordable.span().events(0).dropped_attributes_count(), 0u); + EXPECT_EQ(recordable.span().links(0).dropped_attributes_count(), 0u); +} + +// TODO: remove this test case once the deprecated otlp options span limit fields are removed. +TEST(OtlpRecordable, SpanLimitsFieldsMerged) +{ + constexpr std::uint32_t kOtlpAttributeCount = 2; + constexpr std::uint32_t kOtlpEventCount = 3; + constexpr std::uint32_t kOtlpLinkCount = 4; + constexpr std::uint32_t kOtlpEventAttributeCount = 5; + constexpr std::uint32_t kOtlpLinkAttributeCount = 6; + + trace_sdk::SpanLimits more_restrictive_limits; + more_restrictive_limits.attribute_count_limit = 1; + more_restrictive_limits.event_count_limit = 2; + more_restrictive_limits.link_count_limit = 3; + more_restrictive_limits.event_attribute_count_limit = 4; + more_restrictive_limits.link_attribute_count_limit = 5; + more_restrictive_limits.attribute_value_length_limit = 2; + + trace_sdk::SpanLimits less_restrictive_limits; + less_restrictive_limits.attribute_count_limit = 3; + less_restrictive_limits.event_count_limit = 4; + less_restrictive_limits.link_count_limit = 5; + less_restrictive_limits.event_attribute_count_limit = 6; + less_restrictive_limits.link_attribute_count_limit = 7; + less_restrictive_limits.attribute_value_length_limit = 6; + + auto make_recordable = + [&](const trace_sdk::SpanLimits &limits) -> std::unique_ptr { + auto recordable = + std::make_unique(kOtlpAttributeCount, kOtlpEventCount, kOtlpLinkCount, + kOtlpEventAttributeCount, kOtlpLinkAttributeCount); + recordable->SetSpanLimits(limits); + return recordable; + }; + + auto make_attributes = []() -> std::map { + constexpr std::uint32_t kMaxCount = 10; + std::map attributes; + for (std::uint32_t i = 0; i < kMaxCount; ++i) + { + attributes["attribute_" + std::to_string(i)] = static_cast(i); + } + return attributes; + }; + + const std::map empty_attributes; + const auto event_time = std::chrono::system_clock::time_point{std::chrono::seconds{1000000000}}; + + // attribute_count_limit: config limits are more restrictive than the deprecated otlp options. + { + auto recordable = make_recordable(more_restrictive_limits); + const auto attributes = make_attributes(); + for (const auto &attr : attributes) + { + recordable->SetAttribute(attr.first, attr.second); + } + EXPECT_EQ(recordable->span().attributes_size(), + static_cast(more_restrictive_limits.attribute_count_limit)); + EXPECT_EQ(recordable->span().dropped_attributes_count(), + attributes.size() - more_restrictive_limits.attribute_count_limit); + } + // attribute_count_limit: config limits are less restrictive than the deprecated otlp options. + { + auto recordable = make_recordable(less_restrictive_limits); + const auto attributes = make_attributes(); + for (const auto &attr : attributes) + { + recordable->SetAttribute(attr.first, attr.second); + } + EXPECT_EQ(recordable->span().attributes_size(), static_cast(kOtlpAttributeCount)); + EXPECT_EQ(recordable->span().dropped_attributes_count(), + attributes.size() - kOtlpAttributeCount); + } + + // attribute_value_length_limit: config limits are more restrictive than the deprecated otlp + // options. + { + auto recordable = make_recordable(more_restrictive_limits); + recordable->SetAttribute("value", nostd::string_view("abcdefghij")); + EXPECT_EQ(recordable->span().attributes(0).value().string_value().size(), + more_restrictive_limits.attribute_value_length_limit); + } + // attribute_value_length_limit: config limits are less restrictive than the deprecated otlp + // options. + { + auto recordable = make_recordable(less_restrictive_limits); + recordable->SetAttribute("value", nostd::string_view("abcdefghij")); + EXPECT_EQ(recordable->span().attributes(0).value().string_value().size(), + less_restrictive_limits.attribute_value_length_limit); + } + + // event_count_limit: config limits are more restrictive than the deprecated otlp options. + { + auto recordable = make_recordable(more_restrictive_limits); + recordable->AddEvent("event_one", event_time, common::MakeAttributes(empty_attributes)); + recordable->AddEvent("event_two", event_time, common::MakeAttributes(empty_attributes)); + recordable->AddEvent("event_three", event_time, common::MakeAttributes(empty_attributes)); + EXPECT_EQ(recordable->span().events_size(), + static_cast(more_restrictive_limits.event_count_limit)); + EXPECT_EQ(recordable->span().dropped_events_count(), 1u); + } + // event_count_limit: config limits are less restrictive than the deprecated otlp options. + { + auto recordable = make_recordable(less_restrictive_limits); + recordable->AddEvent("event_one", event_time, common::MakeAttributes(empty_attributes)); + recordable->AddEvent("event_two", event_time, common::MakeAttributes(empty_attributes)); + recordable->AddEvent("event_three", event_time, common::MakeAttributes(empty_attributes)); + recordable->AddEvent("event_four", event_time, common::MakeAttributes(empty_attributes)); + EXPECT_EQ(recordable->span().events_size(), static_cast(kOtlpEventCount)); + EXPECT_EQ(recordable->span().dropped_events_count(), 1u); + } + + // link_count_limit: config limits are more restrictive than the deprecated otlp options. + { + auto recordable = make_recordable(more_restrictive_limits); + for (std::uint32_t i = 0; i < kOtlpLinkCount; ++i) + { + recordable->AddLink(trace::SpanContext::GetInvalid(), + common::MakeAttributes(empty_attributes)); + } + EXPECT_EQ(recordable->span().links_size(), + static_cast(more_restrictive_limits.link_count_limit)); + EXPECT_EQ(recordable->span().dropped_links_count(), 1u); + } + // link_count_limit: config limits are less restrictive than the deprecated otlp options. + { + auto recordable = make_recordable(less_restrictive_limits); + for (std::uint32_t i = 0; i < less_restrictive_limits.link_count_limit; ++i) + { + recordable->AddLink(trace::SpanContext::GetInvalid(), + common::MakeAttributes(empty_attributes)); + } + EXPECT_EQ(recordable->span().links_size(), static_cast(kOtlpLinkCount)); + EXPECT_EQ(recordable->span().dropped_links_count(), 1u); + } + + // event_attribute_count_limit: config limits are more restrictive than the deprecated otlp + // options. + { + auto recordable = make_recordable(more_restrictive_limits); + const auto attributes = make_attributes(); + recordable->AddEvent("test_event", event_time, common::MakeAttributes(attributes)); + EXPECT_EQ(recordable->span().events(0).attributes_size(), + static_cast(more_restrictive_limits.event_attribute_count_limit)); + EXPECT_EQ(recordable->span().events(0).dropped_attributes_count(), + attributes.size() - more_restrictive_limits.event_attribute_count_limit); + } + // event_attribute_count_limit: config limits are less restrictive than the deprecated otlp + // options. + { + auto recordable = make_recordable(less_restrictive_limits); + const auto attributes = make_attributes(); + recordable->AddEvent("test_event", event_time, common::MakeAttributes(attributes)); + EXPECT_EQ(recordable->span().events(0).attributes_size(), + static_cast(kOtlpEventAttributeCount)); + EXPECT_EQ(recordable->span().events(0).dropped_attributes_count(), + attributes.size() - kOtlpEventAttributeCount); + } + + // link_attribute_count_limit: config limits are more restrictive than the deprecated otlp + // options. + { + auto recordable = make_recordable(more_restrictive_limits); + const auto attributes = make_attributes(); + recordable->AddLink(trace::SpanContext::GetInvalid(), common::MakeAttributes(attributes)); + EXPECT_EQ(recordable->span().links(0).attributes_size(), + static_cast(more_restrictive_limits.link_attribute_count_limit)); + EXPECT_EQ(recordable->span().links(0).dropped_attributes_count(), + attributes.size() - more_restrictive_limits.link_attribute_count_limit); + } + // link_attribute_count_limit: config limits are less restrictive than the deprecated otlp + // options. + { + auto recordable = make_recordable(less_restrictive_limits); + const auto attributes = make_attributes(); + recordable->AddLink(trace::SpanContext::GetInvalid(), common::MakeAttributes(attributes)); + EXPECT_EQ(recordable->span().links(0).attributes_size(), + static_cast(kOtlpLinkAttributeCount)); + EXPECT_EQ(recordable->span().links(0).dropped_attributes_count(), + attributes.size() - kOtlpLinkAttributeCount); + } +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/common/attribute_utils.h b/sdk/include/opentelemetry/sdk/common/attribute_utils.h index c2e75a0f97..8b83b363b0 100644 --- a/sdk/include/opentelemetry/sdk/common/attribute_utils.h +++ b/sdk/include/opentelemetry/sdk/common/attribute_utils.h @@ -21,6 +21,10 @@ #include "opentelemetry/nostd/variant.h" #include "opentelemetry/version.h" +#if OPENTELEMETRY_HAVE_EXCEPTIONS +# include +#endif + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -276,6 +280,62 @@ struct AttributeEqualToVisitor } }; +/** + * Insert or assign a key-value pair into a map using map.insert_or_assign if available, or + * map.emplace otherwise. + * @param map The map to insert or assign the key-value pair into. + * @param key The key to insert or assign. + * @param value The value to insert or assign. + * @return A pair of an iterator to the element and a bool (true if the insertion took place). + */ +template +inline std::pair +AttributeInsertOrAssign(Map &map, opentelemetry::nostd::string_view key, Value &&value) noexcept +{ +#if __cplusplus >= 201703L + // Use insert_or_assign if C++17 is available + return map.insert_or_assign(std::string(key), std::forward(value)); +#else + auto result = map.emplace(std::string(key), typename Map::mapped_type{}); + result.first->second = std::forward(value); + return result; +#endif +} + +/** + * VisitVariant + * + * Invokes nostd::visit(visitor, value) with exception-safe handling. + * Returns pair: second=true on success, false if an exception was caught + * On exception the first element is default-constructed. + */ +template +inline auto VisitVariant(Visitor &&visitor, const Variant &value) noexcept + -> std::pair(visitor), value)), bool> +{ +#if OPENTELEMETRY_HAVE_EXCEPTIONS + using ReturnType = decltype(opentelemetry::nostd::visit(std::forward(visitor), value)); + try + { +#endif + return {opentelemetry::nostd::visit(std::forward(visitor), value), true}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } +# if defined(OPENTELEMETRY_HAVE_STD_VARIANT) + catch (const std::bad_variant_access &) +# else + catch (const opentelemetry::nostd::bad_variant_access &) +# endif + { + return {ReturnType{}, false}; + } + catch (const std::bad_alloc &) + { + return {ReturnType{}, false}; + } +#endif +} + /** * Class for storing attributes. */ @@ -328,10 +388,18 @@ class AttributeMap : public std::unordered_map } // Convert non-owning key-value to owning std::string(key) and OwnedAttributeValue(value) - void SetAttribute(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept + bool SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value, + std::size_t max_length = (std::numeric_limits::max)()) noexcept { - (*this)[std::string(key)] = nostd::visit(AttributeConverter(), value); + std::pair result = + VisitVariant(AttributeConverter(max_length), value); + if (result.second) + { + AttributeInsertOrAssign(*this, key, std::move(result.first)); + return true; + } + return false; } // Compare the attributes of this map with another KeyValueIterable @@ -403,9 +471,15 @@ class OrderedAttributeMap : public std::map // Convert non-owning key-value to owning std::string(key) and OwnedAttributeValue(value) void SetAttribute(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept + const opentelemetry::common::AttributeValue &value, + std::size_t max_length = (std::numeric_limits::max)()) noexcept { - (*this)[std::string(key)] = nostd::visit(AttributeConverter(), value); + std::pair result = + VisitVariant(AttributeConverter(max_length), value); + if (result.second) + { + AttributeInsertOrAssign(*this, key, std::move(result.first)); + } } }; diff --git a/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h b/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h index a4624bb1a5..25e417113d 100644 --- a/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h @@ -17,11 +17,11 @@ class SpanLimitsConfiguration { public: std::size_t attribute_value_length_limit; - std::size_t attribute_count_limit; - std::size_t event_count_limit; - std::size_t link_count_limit; - std::size_t event_attribute_count_limit; - std::size_t link_attribute_count_limit; + std::uint32_t attribute_count_limit; + std::uint32_t event_count_limit; + std::uint32_t link_count_limit; + std::uint32_t event_attribute_count_limit; + std::uint32_t link_attribute_count_limit; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/trace/multi_recordable.h b/sdk/include/opentelemetry/sdk/trace/multi_recordable.h index 1b1798a807..84c4e6be81 100644 --- a/sdk/include/opentelemetry/sdk/trace/multi_recordable.h +++ b/sdk/include/opentelemetry/sdk/trace/multi_recordable.h @@ -58,6 +58,8 @@ class MultiRecordable : public Recordable void SetDuration(std::chrono::nanoseconds duration) noexcept override; + void SetSpanLimits(const SpanLimits &limits) noexcept override; + void SetInstrumentationScope(const InstrumentationScope &instrumentation_scope) noexcept override; private: diff --git a/sdk/include/opentelemetry/sdk/trace/recordable.h b/sdk/include/opentelemetry/sdk/trace/recordable.h index 4f6a895170..e12d069f80 100644 --- a/sdk/include/opentelemetry/sdk/trace/recordable.h +++ b/sdk/include/opentelemetry/sdk/trace/recordable.h @@ -10,6 +10,7 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/empty_attributes.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" @@ -171,6 +172,12 @@ class Recordable */ virtual void SetDuration(std::chrono::nanoseconds duration) noexcept = 0; + /** + * Set the span limits for the span. + * This method must be called before any SetAttribute / AddEvent / AddLink call. + */ + virtual void SetSpanLimits(const SpanLimits & /* limits */) noexcept {} + /** * Get the SpanData object for this Recordable. * diff --git a/sdk/include/opentelemetry/sdk/trace/span_data.h b/sdk/include/opentelemetry/sdk/trace/span_data.h index 6a7d570afb..e5869d8fc6 100644 --- a/sdk/include/opentelemetry/sdk/trace/span_data.h +++ b/sdk/include/opentelemetry/sdk/trace/span_data.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" @@ -38,9 +40,12 @@ namespace trace class SpanDataEvent { public: - SpanDataEvent(std::string name, - opentelemetry::common::SystemTimestamp timestamp, - const opentelemetry::common::KeyValueIterable &attributes); + SpanDataEvent( + std::string name, + opentelemetry::common::SystemTimestamp timestamp, + const opentelemetry::common::KeyValueIterable &attributes, + std::uint32_t attribute_count_limit = (std::numeric_limits::max)(), + std::size_t attribute_value_length_limit = (std::numeric_limits::max)()); /** * Get the name for this event @@ -61,10 +66,17 @@ class SpanDataEvent const std::unordered_map & GetAttributes() const noexcept; + /** + * Get the number of attributes dropped due to the per-event attribute count limit. + * @return the number of attributes dropped + */ + std::uint32_t GetDroppedAttributesCount() const noexcept { return dropped_attributes_count_; } + private: std::string name_; opentelemetry::common::SystemTimestamp timestamp_; opentelemetry::sdk::common::AttributeMap attribute_map_; + std::uint32_t dropped_attributes_count_{0}; }; /** @@ -73,8 +85,11 @@ class SpanDataEvent class SpanDataLink { public: - SpanDataLink(opentelemetry::trace::SpanContext span_context, - const opentelemetry::common::KeyValueIterable &attributes); + SpanDataLink( + opentelemetry::trace::SpanContext span_context, + const opentelemetry::common::KeyValueIterable &attributes, + std::uint32_t attribute_count_limit = (std::numeric_limits::max)(), + std::size_t attribute_value_length_limit = (std::numeric_limits::max)()); /** * Get the attributes for this link @@ -89,9 +104,17 @@ class SpanDataLink */ const opentelemetry::trace::SpanContext &GetSpanContext() const noexcept { return span_context_; } + /** + * Get the number of attributes dropped due to the per-link + * attribute count limit. + * @return the number of attributes dropped + */ + std::uint32_t GetDroppedAttributesCount() const noexcept { return dropped_attributes_count_; } + private: opentelemetry::trace::SpanContext span_context_; opentelemetry::sdk::common::AttributeMap attribute_map_; + std::uint32_t dropped_attributes_count_{0}; }; /** @@ -235,8 +258,16 @@ class SpanData final : public Recordable void SetDuration(std::chrono::nanoseconds duration) noexcept override; + void SetSpanLimits(const SpanLimits &limits) noexcept override; + void SetInstrumentationScope(const InstrumentationScope &instrumentation_scope) noexcept override; + std::uint32_t GetDroppedAttributesCount() const noexcept { return dropped_attributes_count_; } + + std::uint32_t GetDroppedEventsCount() const noexcept { return dropped_events_count_; } + + std::uint32_t GetDroppedLinksCount() const noexcept { return dropped_links_count_; } + private: opentelemetry::trace::SpanContext span_context_{false, false}; opentelemetry::trace::SpanId parent_span_id_; @@ -252,6 +283,10 @@ class SpanData final : public Recordable opentelemetry::trace::SpanKind span_kind_{opentelemetry::trace::SpanKind::kInternal}; const opentelemetry::sdk::resource::Resource *resource_{nullptr}; const InstrumentationScope *instrumentation_scope_{nullptr}; + SpanLimits limits_{SpanLimits::NoLimits()}; + std::uint32_t dropped_attributes_count_{0}; + std::uint32_t dropped_events_count_{0}; + std::uint32_t dropped_links_count_{0}; }; } // namespace trace } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/span_limits.h b/sdk/include/opentelemetry/sdk/trace/span_limits.h new file mode 100644 index 0000000000..3f2db6e3cc --- /dev/null +++ b/sdk/include/opentelemetry/sdk/trace/span_limits.h @@ -0,0 +1,68 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +/** + * Span limits set on the TracerProvider and used by supporting recordables. + * See https://opentelemetry.io/docs/specs/otel/trace/sdk/#span-limits. + */ +struct SpanLimits +{ + static constexpr std::uint32_t kDefaultAttributeCountLimit = 128; + static constexpr std::uint32_t kDefaultEventCountLimit = 128; + static constexpr std::uint32_t kDefaultLinkCountLimit = 128; + static constexpr std::uint32_t kDefaultEventAttributeCountLimit = 128; + static constexpr std::uint32_t kDefaultLinkAttributeCountLimit = 128; + static constexpr std::size_t kDefaultAttributeValueLengthLimit = + (std::numeric_limits::max)(); + + /// Maximum number of attributes per span. + std::uint32_t attribute_count_limit{kDefaultAttributeCountLimit}; + + /// Maximum allowed attribute value length (applies to string values and byte arrays) + std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit}; + + /// Maximum number of events per span. + std::uint32_t event_count_limit{kDefaultEventCountLimit}; + + /// Maximum number of links per span. + std::uint32_t link_count_limit{kDefaultLinkCountLimit}; + + /// Maximum number of attributes per span event. + std::uint32_t event_attribute_count_limit{kDefaultEventAttributeCountLimit}; + + /// Maximum number of attributes per span link. + std::uint32_t link_attribute_count_limit{kDefaultLinkAttributeCountLimit}; + + /** + * Limit values that impose no cap on any field + */ + static constexpr SpanLimits NoLimits() noexcept + { + SpanLimits limits; + limits.attribute_count_limit = (std::numeric_limits::max)(); + limits.attribute_value_length_limit = (std::numeric_limits::max)(); + limits.event_count_limit = (std::numeric_limits::max)(); + limits.link_count_limit = (std::numeric_limits::max)(); + limits.event_attribute_count_limit = (std::numeric_limits::max)(); + limits.link_attribute_count_limit = (std::numeric_limits::max)(); + return limits; + } +}; + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index dcaf683395..601cfc1b7c 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -15,6 +15,7 @@ #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/trace/noop.h" @@ -83,6 +84,9 @@ class Tracer final : public opentelemetry::trace::Tracer, /** Returns the configured span processor. */ SpanProcessor &GetProcessor() noexcept { return context_->GetProcessor(); } + /** Returns the configured span limits. */ + const SpanLimits &GetSpanLimits() const noexcept { return context_->GetSpanLimits(); } + /** Returns the configured Id generator */ IdGenerator &GetIdGenerator() const noexcept { return context_->GetIdGenerator(); } diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_context.h b/sdk/include/opentelemetry/sdk/trace/tracer_context.h index 518c1cbdf6..f94972b8fe 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_context.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_context.h @@ -14,6 +14,7 @@ #include "opentelemetry/sdk/trace/random_id_generator.h" #include "opentelemetry/sdk/trace/sampler.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/version.h" @@ -50,7 +51,8 @@ class TracerContext std::make_unique>( instrumentationscope::ScopeConfigurator::Builder( TracerConfig::Default()) - .Build())) noexcept; + .Build()), + SpanLimits span_limits = SpanLimits::NoLimits()) noexcept; TracerContext(const TracerContext &) = delete; TracerContext(TracerContext &&) = delete; @@ -102,6 +104,12 @@ class TracerContext */ opentelemetry::sdk::trace::IdGenerator &GetIdGenerator() const noexcept; + /** + * Obtain the SpanLimits associated with this tracer context. + * @return The SpanLimits for this tracer context. + */ + const SpanLimits &GetSpanLimits() const noexcept; + /** * Replace the TracerConfigurator for this context. * @@ -129,6 +137,7 @@ class TracerContext std::unique_ptr id_generator_; std::unique_ptr processor_; std::unique_ptr> tracer_configurator_; + SpanLimits span_limits_; }; } // namespace trace diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index b58f46dc20..5b9986a4c3 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -15,6 +15,7 @@ #include "opentelemetry/sdk/trace/random_id_generator.h" #include "opentelemetry/sdk/trace/sampler.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/trace/tracer.h" @@ -41,6 +42,7 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T * not be a nullptr * @param tracer_configurator Provides access to a function that computes the TracerConfig for * Tracers provided by this TracerProvider. + * @param span_limits The span limits for this tracer provider. */ explicit TracerProvider( std::unique_ptr processor, @@ -53,7 +55,8 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T std::make_unique>( instrumentationscope::ScopeConfigurator::Builder( TracerConfig::Default()) - .Build())) noexcept; + .Build()), + SpanLimits span_limits = SpanLimits::NoLimits()) noexcept; explicit TracerProvider( std::vector> &&processors, @@ -66,7 +69,8 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T std::make_unique>( instrumentationscope::ScopeConfigurator::Builder( TracerConfig::Default()) - .Build())) noexcept; + .Build()), + SpanLimits span_limits = SpanLimits::NoLimits()) noexcept; /** * Initialize a new tracer provider with a specified context @@ -130,6 +134,12 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T */ const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + /** + * Obtain the span limits configured for this tracer provider. + * @return The SpanLimits for this tracer provider. + */ + const opentelemetry::sdk::trace::SpanLimits &GetSpanLimits() const noexcept; + /** * Shutdown the span processor associated with this tracer provider. */ diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h index 85177f6565..81e0e36f01 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h @@ -10,6 +10,7 @@ #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/sdk/trace/tracer_provider.h" #include "opentelemetry/trace/tracer_provider.h" @@ -55,6 +56,14 @@ class OPENTELEMETRY_EXPORT TracerProviderFactory std::unique_ptr id_generator, std::unique_ptr> tracer_configurator); + static std::unique_ptr Create( + std::unique_ptr processor, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator, + SpanLimits span_limits); + /* Series of creator methods with a vector of processors. */ static std::unique_ptr Create( @@ -82,6 +91,14 @@ class OPENTELEMETRY_EXPORT TracerProviderFactory std::unique_ptr id_generator, std::unique_ptr> tracer_configurator); + static std::unique_ptr Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator, + SpanLimits span_limits); + /* Create with a tracer context. */ static std::unique_ptr Create( diff --git a/sdk/src/configuration/configuration_parser.cc b/sdk/src/configuration/configuration_parser.cc index 5e00cbc7de..c83f329d0f 100644 --- a/sdk/src/configuration/configuration_parser.cc +++ b/sdk/src/configuration/configuration_parser.cc @@ -1,11 +1,11 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include -#include #include #include +#include #include +#include #include #include #include @@ -1596,12 +1596,22 @@ std::unique_ptr ConfigurationParser::ParseSpanLimitsCon { auto model = std::make_unique(); + const auto get_valid_uint32 = [&node](const std::string &name, std::size_t default_value) { + std::size_t value = node->GetInteger(name, default_value); + if (value > std::numeric_limits::max()) + { + std::string message = "Invalid value for " + name + ": " + std::to_string(value); + throw InvalidSchemaException(node->Location(), message); + } + return static_cast(value); + }; + model->attribute_value_length_limit = node->GetInteger("attribute_value_length_limit", 4096); - model->attribute_count_limit = node->GetInteger("attribute_count_limit", 128); - model->event_count_limit = node->GetInteger("event_count_limit", 128); - model->link_count_limit = node->GetInteger("link_count_limit", 128); - model->event_attribute_count_limit = node->GetInteger("event_attribute_count_limit", 128); - model->link_attribute_count_limit = node->GetInteger("link_attribute_count_limit", 128); + model->attribute_count_limit = get_valid_uint32("attribute_count_limit", 128); + model->event_count_limit = get_valid_uint32("event_count_limit", 128); + model->link_count_limit = get_valid_uint32("link_count_limit", 128); + model->event_attribute_count_limit = get_valid_uint32("event_attribute_count_limit", 128); + model->link_attribute_count_limit = get_valid_uint32("link_attribute_count_limit", 128); return model; } diff --git a/sdk/src/configuration/sdk_builder.cc b/sdk/src/configuration/sdk_builder.cc index c077761859..897413643d 100644 --- a/sdk/src/configuration/sdk_builder.cc +++ b/sdk/src/configuration/sdk_builder.cc @@ -1,9 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#include -#include #include +#include +#include #include #include #include @@ -116,6 +116,7 @@ #include "opentelemetry/sdk/configuration/simple_span_processor_configuration.h" #include "opentelemetry/sdk/configuration/span_exporter_configuration.h" #include "opentelemetry/sdk/configuration/span_exporter_configuration_visitor.h" +#include "opentelemetry/sdk/configuration/span_limits_configuration.h" #include "opentelemetry/sdk/configuration/span_processor_configuration.h" #include "opentelemetry/sdk/configuration/span_processor_configuration_visitor.h" #include "opentelemetry/sdk/configuration/string_array_attribute_value_configuration.h" @@ -173,6 +174,7 @@ #include "opentelemetry/sdk/trace/samplers/parent_factory.h" #include "opentelemetry/sdk/trace/samplers/trace_id_ratio_factory.h" #include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_provider.h" #include "opentelemetry/sdk/trace/tracer_provider_factory.h" @@ -1167,20 +1169,38 @@ std::unique_ptr SdkBuilder::CreateTra sdk_processors.push_back(CreateSpanProcessor(processor_model)); } - // FIXME-SDK: https://github.com/open-telemetry/opentelemetry-cpp/issues/3303 - // FIXME-SDK: use limits, id_generator, ... + opentelemetry::sdk::trace::SpanLimits span_limits; + if (model->limits) + { + span_limits.attribute_value_length_limit = model->limits->attribute_value_length_limit; + span_limits.attribute_count_limit = model->limits->attribute_count_limit; + span_limits.event_count_limit = model->limits->event_count_limit; + span_limits.link_count_limit = model->limits->link_count_limit; + span_limits.event_attribute_count_limit = model->limits->event_attribute_count_limit; + span_limits.link_attribute_count_limit = model->limits->link_attribute_count_limit; + } + if (model->tracer_configurator) { auto tracer_configurator = CreateTracerConfigurator(model->tracer_configurator); auto id_generator = opentelemetry::sdk::trace::RandomIdGeneratorFactory::Create(); sdk = opentelemetry::sdk::trace::TracerProviderFactory::Create( std::move(sdk_processors), resource, std::move(sampler), std::move(id_generator), - std::move(tracer_configurator)); + std::move(tracer_configurator), span_limits); } else { - sdk = opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(sdk_processors), - resource, std::move(sampler)); + auto id_generator = opentelemetry::sdk::trace::RandomIdGeneratorFactory::Create(); + auto tracer_configurator = + std::make_unique>( + opentelemetry::sdk::instrumentationscope:: + ScopeConfigurator::Builder( + opentelemetry::sdk::trace::TracerConfig::Default()) + .Build()); + sdk = opentelemetry::sdk::trace::TracerProviderFactory::Create( + std::move(sdk_processors), resource, std::move(sampler), std::move(id_generator), + std::move(tracer_configurator), span_limits); } return sdk; diff --git a/sdk/src/trace/multi_recordable.cc b/sdk/src/trace/multi_recordable.cc index 5d74fd2403..37959a8e1f 100644 --- a/sdk/src/trace/multi_recordable.cc +++ b/sdk/src/trace/multi_recordable.cc @@ -69,6 +69,14 @@ void MultiRecordable::SetIdentity(const opentelemetry::trace::SpanContext &span_ } } +void MultiRecordable::SetSpanLimits(const SpanLimits &limits) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetSpanLimits(limits); + } +} + void MultiRecordable::SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept { diff --git a/sdk/src/trace/span.cc b/sdk/src/trace/span.cc index d764540875..1233e3361d 100644 --- a/sdk/src/trace/span.cc +++ b/sdk/src/trace/span.cc @@ -66,6 +66,7 @@ Span::Span(std::shared_ptr &&tracer, { return; } + recordable_->SetSpanLimits(tracer_->GetSpanLimits()); recordable_->SetName(name); recordable_->SetInstrumentationScope(tracer_->GetInstrumentationScope()); recordable_->SetIdentity(*span_context_, parent_span_context.IsValid() diff --git a/sdk/src/trace/span_data.cc b/sdk/src/trace/span_data.cc index 8b1503ee5d..e4d42fae4f 100644 --- a/sdk/src/trace/span_data.cc +++ b/sdk/src/trace/span_data.cc @@ -1,7 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#include #include +#include +#include #include #include #include @@ -11,11 +14,14 @@ #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/common/timestamp.h" +#include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/common/attribute_utils.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_flags.h" @@ -27,11 +33,74 @@ namespace sdk namespace trace { +namespace +{ + +bool SetAttributeImpl(opentelemetry::sdk::common::AttributeMap &attribute_map, + nostd::string_view key, + const opentelemetry::common::AttributeValue &value, + std::uint32_t attribute_count_limit, + std::size_t attribute_value_length_limit) noexcept +{ + if (attribute_map.size() >= attribute_count_limit) + { + // The map is at the limit. Can only update existing keys. + auto it = attribute_map.find(std::string(key)); + if (it == attribute_map.end()) + { + return false; // The key is not in the map. Cannot add new attributes. + } + // try to convert the value and assign it to overwrite the existing value + opentelemetry::sdk::common::AttributeConverter converter(attribute_value_length_limit); + std::pair convert_result = + opentelemetry::sdk::common::VisitVariant(converter, value); + if (!convert_result.second) + { + return false; // There was an exception converting the value. Skip this attribute. + } + it->second = std::move(convert_result.first); + return true; + } + // The map is under the limit. Set the attribute (insert or assign). + return attribute_map.SetAttribute(key, value, attribute_value_length_limit); +} + +// Create the attribute map from KeyValueIterable attributes enforcing count and value-length +// limits. +opentelemetry::sdk::common::AttributeMap CreateAttributeMap( + const opentelemetry::common::KeyValueIterable &attributes, + std::uint32_t attribute_count_limit, + std::size_t attribute_value_length_limit, + std::uint32_t &dropped_count) +{ + opentelemetry::sdk::common::AttributeMap map; + map.reserve((std::min)(static_cast(attribute_count_limit), attributes.size())); + dropped_count = 0; + // Insert attributes until the count limit of unique keys is reached. + attributes.ForEachKeyValue( + [attribute_count_limit, attribute_value_length_limit, &map, &dropped_count]( + nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept { + if (!SetAttributeImpl(map, key, value, attribute_count_limit, attribute_value_length_limit)) + { + ++dropped_count; + } + return true; + }); + return map; +} + +} // namespace + SpanDataEvent::SpanDataEvent(std::string name, opentelemetry::common::SystemTimestamp timestamp, - const opentelemetry::common::KeyValueIterable &attributes) - : name_(std::move(name)), timestamp_(timestamp), attribute_map_(attributes) -{} + const opentelemetry::common::KeyValueIterable &attributes, + std::uint32_t attribute_count_limit, + std::size_t attribute_value_length_limit) + : name_(std::move(name)), timestamp_(timestamp) +{ + attribute_map_ = CreateAttributeMap(attributes, attribute_count_limit, + attribute_value_length_limit, dropped_attributes_count_); +} const std::unordered_map & SpanDataEvent::GetAttributes() const noexcept @@ -40,9 +109,14 @@ SpanDataEvent::GetAttributes() const noexcept } SpanDataLink::SpanDataLink(opentelemetry::trace::SpanContext span_context, - const opentelemetry::common::KeyValueIterable &attributes) - : span_context_(std::move(span_context)), attribute_map_(attributes) -{} + const opentelemetry::common::KeyValueIterable &attributes, + std::uint32_t attribute_count_limit, + std::size_t attribute_value_length_limit) + : span_context_(std::move(span_context)) +{ + attribute_map_ = CreateAttributeMap(attributes, attribute_count_limit, + attribute_value_length_limit, dropped_attributes_count_); +} const std::unordered_map & SpanDataLink::GetAttributes() const noexcept @@ -93,20 +167,41 @@ void SpanData::SetIdentity(const opentelemetry::trace::SpanContext &span_context void SpanData::SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept { - attribute_map_.SetAttribute(key, value); + if (!SetAttributeImpl(attribute_map_, key, value, limits_.attribute_count_limit, + limits_.attribute_value_length_limit)) + { + ++dropped_attributes_count_; + } } void SpanData::AddEvent(nostd::string_view name, opentelemetry::common::SystemTimestamp timestamp, const opentelemetry::common::KeyValueIterable &attributes) noexcept { - events_.emplace_back(std::string(name), timestamp, attributes); + if (events_.size() >= limits_.event_count_limit) + { + ++dropped_events_count_; + return; + } + events_.emplace_back(std::string(name), timestamp, attributes, + limits_.event_attribute_count_limit, limits_.attribute_value_length_limit); } void SpanData::AddLink(const opentelemetry::trace::SpanContext &span_context, const opentelemetry::common::KeyValueIterable &attributes) noexcept { - links_.emplace_back(span_context, attributes); + if (links_.size() >= limits_.link_count_limit) + { + ++dropped_links_count_; + return; + } + links_.emplace_back(span_context, attributes, limits_.link_attribute_count_limit, + limits_.attribute_value_length_limit); +} + +void SpanData::SetSpanLimits(const SpanLimits &limits) noexcept +{ + limits_ = limits; } void SpanData::SetStatus(opentelemetry::trace::StatusCode code, diff --git a/sdk/src/trace/tracer_context.cc b/sdk/src/trace/tracer_context.cc index adb3cfe538..43bdabc93b 100644 --- a/sdk/src/trace/tracer_context.cc +++ b/sdk/src/trace/tracer_context.cc @@ -13,6 +13,7 @@ #include "opentelemetry/sdk/trace/multi_span_processor.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/version.h" @@ -24,17 +25,19 @@ namespace trace { namespace resource = opentelemetry::sdk::resource; -TracerContext::TracerContext(std::vector> &&processors, - const resource::Resource &resource, - std::unique_ptr sampler, - std::unique_ptr id_generator, - std::unique_ptr> - tracer_configurator) noexcept +TracerContext::TracerContext( + std::vector> &&processors, + const resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator, + SpanLimits span_limits) noexcept : resource_(resource), sampler_(std::move(sampler)), id_generator_(std::move(id_generator)), processor_(std::unique_ptr(new MultiSpanProcessor(std::move(processors)))), - tracer_configurator_(std::move(tracer_configurator)) + tracer_configurator_(std::move(tracer_configurator)), + span_limits_(span_limits) {} Sampler &TracerContext::GetSampler() const noexcept @@ -58,9 +61,13 @@ opentelemetry::sdk::trace::IdGenerator &TracerContext::GetIdGenerator() const no return *id_generator_; } -void TracerContext::AddProcessor(std::unique_ptr processor) noexcept +const SpanLimits &TracerContext::GetSpanLimits() const noexcept { + return span_limits_; +} +void TracerContext::AddProcessor(std::unique_ptr processor) noexcept +{ auto multi_processor = static_cast(processor_.get()); multi_processor->AddProcessor(std::move(processor)); } diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 7f71a1ec47..47d3bb98b9 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -16,6 +16,7 @@ #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" @@ -41,14 +42,14 @@ TracerProvider::TracerProvider( const resource::Resource &resource, std::unique_ptr sampler, std::unique_ptr id_generator, - std::unique_ptr> - tracer_configurator) noexcept + std::unique_ptr> tracer_configurator, + SpanLimits span_limits) noexcept { std::vector> processors; processors.push_back(std::move(processor)); - context_ = - std::make_shared(std::move(processors), resource, std::move(sampler), - std::move(id_generator), std::move(tracer_configurator)); + context_ = std::make_shared(std::move(processors), resource, std::move(sampler), + std::move(id_generator), + std::move(tracer_configurator), span_limits); } TracerProvider::TracerProvider( @@ -56,13 +57,14 @@ TracerProvider::TracerProvider( const resource::Resource &resource, std::unique_ptr sampler, std::unique_ptr id_generator, - std::unique_ptr> - tracer_configurator) noexcept + std::unique_ptr> tracer_configurator, + SpanLimits span_limits) noexcept : context_(std::make_shared(std::move(processors), resource, std::move(sampler), std::move(id_generator), - std::move(tracer_configurator))) + std::move(tracer_configurator), + span_limits)) {} TracerProvider::~TracerProvider() @@ -163,6 +165,11 @@ const resource::Resource &TracerProvider::GetResource() const noexcept return context_->GetResource(); } +const opentelemetry::sdk::trace::SpanLimits &TracerProvider::GetSpanLimits() const noexcept +{ + return context_->GetSpanLimits(); +} + bool TracerProvider::Shutdown(std::chrono::microseconds timeout) noexcept { return context_->Shutdown(timeout); diff --git a/sdk/src/trace/tracer_provider_factory.cc b/sdk/src/trace/tracer_provider_factory.cc index 49d2aac12d..06f792c659 100644 --- a/sdk/src/trace/tracer_provider_factory.cc +++ b/sdk/src/trace/tracer_provider_factory.cc @@ -12,6 +12,7 @@ #include "opentelemetry/sdk/trace/random_id_generator_factory.h" #include "opentelemetry/sdk/trace/sampler.h" #include "opentelemetry/sdk/trace/samplers/always_on_factory.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/sdk/trace/tracer_provider.h" @@ -68,11 +69,23 @@ std::unique_ptr TracerProviderFactory std::unique_ptr sampler, std::unique_ptr id_generator, std::unique_ptr> tracer_configurator) +{ + return Create(std::move(processor), resource, std::move(sampler), std::move(id_generator), + std::move(tracer_configurator), SpanLimits::NoLimits()); +} + +std::unique_ptr TracerProviderFactory::Create( + std::unique_ptr processor, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator, + SpanLimits span_limits) { std::unique_ptr provider( new opentelemetry::sdk::trace::TracerProvider(std::move(processor), resource, std::move(sampler), std::move(id_generator), - std::move(tracer_configurator))); + std::move(tracer_configurator), span_limits)); return provider; } @@ -120,11 +133,23 @@ std::unique_ptr TracerProviderFactory std::unique_ptr sampler, std::unique_ptr id_generator, std::unique_ptr> tracer_configurator) +{ + return Create(std::move(processors), resource, std::move(sampler), std::move(id_generator), + std::move(tracer_configurator), SpanLimits::NoLimits()); +} + +std::unique_ptr TracerProviderFactory::Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator, + SpanLimits span_limits) { std::unique_ptr provider( new opentelemetry::sdk::trace::TracerProvider(std::move(processors), resource, std::move(sampler), std::move(id_generator), - std::move(tracer_configurator))); + std::move(tracer_configurator), span_limits)); return provider; } diff --git a/sdk/test/common/attribute_utils_test.cc b/sdk/test/common/attribute_utils_test.cc index c4b6abe378..848ce86bc4 100644 --- a/sdk/test/common/attribute_utils_test.cc +++ b/sdk/test/common/attribute_utils_test.cc @@ -195,6 +195,26 @@ TEST(AttributeMapTest, EqualTo) EXPECT_FALSE(attribute_map.EqualTo(kv_iterable_different_all)); } +TEST(AttributeMapTest, SetAttributeTruncation) +{ + opentelemetry::sdk::common::AttributeMap attribute_map; + opentelemetry::nostd::string_view value("abcdefghijk"); + attribute_map.SetAttribute("key", value, 5); + auto stored_value = attribute_map.GetAttributes().at("key"); + auto string_value = opentelemetry::nostd::get(stored_value); + EXPECT_EQ(string_value, "abcde"); +} + +TEST(OrderedAttributeMapTest, SetAttributeTruncation) +{ + opentelemetry::sdk::common::OrderedAttributeMap attribute_map; + opentelemetry::nostd::string_view value("abcdefghijk"); + attribute_map.SetAttribute("key", value, 5); + auto stored_value = attribute_map.GetAttributes().at("key"); + auto string_value = opentelemetry::nostd::get(stored_value); + EXPECT_EQ(string_value, "abcde"); +} + // --------------------------------------------------------------------------- // AttributeConverter truncation // --------------------------------------------------------------------------- diff --git a/sdk/test/configuration/sdk_builder_test.cc b/sdk/test/configuration/sdk_builder_test.cc index cccb6d37ee..22d07a8fc0 100644 --- a/sdk/test/configuration/sdk_builder_test.cc +++ b/sdk/test/configuration/sdk_builder_test.cc @@ -13,14 +13,69 @@ #include "opentelemetry/sdk/configuration/registry.h" #include "opentelemetry/sdk/configuration/sdk_builder.h" #include "opentelemetry/sdk/configuration/severity_number.h" +#include "opentelemetry/sdk/configuration/span_limits_configuration.h" +#include "opentelemetry/sdk/configuration/tracer_provider_configuration.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/logs/logger_config.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/span_limits.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +using opentelemetry::sdk::configuration::Registry; +using opentelemetry::sdk::configuration::SdkBuilder; +using opentelemetry::sdk::configuration::SpanLimitsConfiguration; +using opentelemetry::sdk::configuration::TracerProviderConfiguration; namespace config_sdk = opentelemetry::sdk::configuration; namespace scope_sdk = opentelemetry::sdk::instrumentationscope; namespace logs_sdk = opentelemetry::sdk::logs; +TEST(SdkBuilder, SpanLimitsDefaults) +{ + auto model = std::make_unique(); + model->limits = nullptr; + + SdkBuilder builder(std::make_shared()); + auto resource = opentelemetry::sdk::resource::Resource::Create({}); + auto provider = builder.CreateTracerProvider(model, resource); + ASSERT_NE(provider, nullptr); + + const auto limits = provider->GetSpanLimits(); + const auto default_limits = opentelemetry::sdk::trace::SpanLimits{}; + + EXPECT_EQ(limits.attribute_count_limit, default_limits.attribute_count_limit); + EXPECT_EQ(limits.event_count_limit, default_limits.event_count_limit); + EXPECT_EQ(limits.link_count_limit, default_limits.link_count_limit); + EXPECT_EQ(limits.event_attribute_count_limit, default_limits.event_attribute_count_limit); + EXPECT_EQ(limits.link_attribute_count_limit, default_limits.link_attribute_count_limit); + EXPECT_EQ(limits.attribute_value_length_limit, default_limits.attribute_value_length_limit); +} + +TEST(SdkBuilder, SpanLimitsConfiguration) +{ + auto model = std::make_unique(); + model->limits = std::make_unique(); + model->limits->attribute_value_length_limit = 1111; + model->limits->attribute_count_limit = 2222; + model->limits->event_count_limit = 3333; + model->limits->link_count_limit = 4444; + model->limits->event_attribute_count_limit = 5555; + model->limits->link_attribute_count_limit = 6666; + + SdkBuilder builder(std::make_shared()); + auto resource = opentelemetry::sdk::resource::Resource::Create({}); + auto provider = builder.CreateTracerProvider(model, resource); + ASSERT_NE(provider, nullptr); + + auto limits = provider->GetSpanLimits(); + EXPECT_EQ(limits.attribute_value_length_limit, model->limits->attribute_value_length_limit); + EXPECT_EQ(limits.attribute_count_limit, model->limits->attribute_count_limit); + EXPECT_EQ(limits.event_count_limit, model->limits->event_count_limit); + EXPECT_EQ(limits.link_count_limit, model->limits->link_count_limit); + EXPECT_EQ(limits.event_attribute_count_limit, model->limits->event_attribute_count_limit); + EXPECT_EQ(limits.link_attribute_count_limit, model->limits->link_attribute_count_limit); +} + TEST(SdkBuilder, CreateLoggerConfigurator) { config_sdk::LoggerConfigConfiguration default_config; diff --git a/sdk/test/configuration/yaml_trace_test.cc b/sdk/test/configuration/yaml_trace_test.cc index db93d08c69..f65868e507 100644 --- a/sdk/test/configuration/yaml_trace_test.cc +++ b/sdk/test/configuration/yaml_trace_test.cc @@ -593,6 +593,79 @@ file_format: "1.0-trace" ASSERT_EQ(config->tracer_provider->limits->link_attribute_count_limit, 6666); } +TEST(YamlTrace, limits_with_invalid_values) +{ + std::string invalid_attribute_count_yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + limits: + attribute_count_limit: 4294967296 +)"; + + std::string invalid_event_count_yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + limits: + event_count_limit: 4294967296 +)"; + + std::string invalid_link_count_yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + limits: + link_count_limit: 4294967296 +)"; + + std::string invalid_event_attribute_count_yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + limits: + event_attribute_count_limit: 4294967296 +)"; + + std::string invalid_link_attribute_count_yaml = R"( +file_format: "1.0-trace" +tracer_provider: + processors: + - simple: + exporter: + console: + limits: + link_attribute_count_limit: 4294967296 +)"; + + auto config = DoParse(invalid_attribute_count_yaml); + EXPECT_TRUE(config == nullptr); + + config = DoParse(invalid_event_count_yaml); + EXPECT_TRUE(config == nullptr); + + config = DoParse(invalid_link_count_yaml); + EXPECT_TRUE(config == nullptr); + + config = DoParse(invalid_event_attribute_count_yaml); + EXPECT_TRUE(config == nullptr); + + config = DoParse(invalid_link_attribute_count_yaml); + EXPECT_TRUE(config == nullptr); +} + TEST(YamlTrace, no_sampler) { std::string yaml = R"( diff --git a/sdk/test/trace/span_data_test.cc b/sdk/test/trace/span_data_test.cc index 3e3c27ce4d..d7aad3353c 100644 --- a/sdk/test/trace/span_data_test.cc +++ b/sdk/test/trace/span_data_test.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "opentelemetry/common/key_value_iterable_view.h" @@ -16,6 +17,7 @@ #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/span_metadata.h" @@ -149,3 +151,144 @@ TEST(SpanData, Links) values[i]); } } + +TEST(SpanData, SpanLimits) +{ + SpanData recordable; + opentelemetry::sdk::trace::SpanLimits limits; + limits.attribute_count_limit = 1; + limits.attribute_value_length_limit = 2; + limits.link_count_limit = 1; + limits.link_attribute_count_limit = 1; + limits.event_count_limit = 1; + limits.event_attribute_count_limit = 1; + + constexpr const char *kKey1 = "one"; + constexpr const char *kKey2 = "two"; + constexpr const char *kValue1 = "1234"; + constexpr const char *kValue2 = "5678"; + constexpr const char *kValue3 = "9012"; + + std::map attribute_collection{{kKey1, kValue1}, {kKey2, kValue2}}; + + recordable.SetSpanLimits(limits); + + // span attribute count and length limits + recordable.SetAttribute(kKey1, kValue1); + recordable.SetAttribute(kKey2, kValue2); + recordable.SetAttribute(kKey1, kValue3); // overwrite existing attribute + + EXPECT_EQ(recordable.GetAttributes().size(), 1); + EXPECT_EQ(recordable.GetDroppedAttributesCount(), 1); + EXPECT_EQ(opentelemetry::nostd::get(recordable.GetAttributes().at(kKey1)), "90"); + + // event count and per-event attribute count limits + recordable.AddEvent("event1", std::chrono::system_clock::now(), + common::MakeAttributes(attribute_collection)); + recordable.AddEvent("event2", std::chrono::system_clock::now(), + common::MakeAttributes(attribute_collection)); + + ASSERT_EQ(recordable.GetEvents().size(), 1); + EXPECT_EQ(recordable.GetDroppedEventsCount(), 1); + const auto event = recordable.GetEvents().at(0); + EXPECT_EQ(event.GetDroppedAttributesCount(), 1); + EXPECT_EQ(event.GetName(), "event1"); + const auto &event_attributes = event.GetAttributes(); + EXPECT_EQ(event_attributes.size(), 1); + EXPECT_EQ(opentelemetry::nostd::get(event_attributes.at(kKey1)), "12"); + + // link count and per-link attribute count limits + recordable.AddLink(trace_api::SpanContext::GetInvalid(), + common::MakeAttributes(attribute_collection)); + recordable.AddLink(trace_api::SpanContext::GetInvalid(), + common::MakeAttributes(attribute_collection)); + ASSERT_EQ(recordable.GetLinks().size(), 1); + EXPECT_EQ(recordable.GetDroppedLinksCount(), 1); + const auto link = recordable.GetLinks().at(0); + EXPECT_EQ(link.GetDroppedAttributesCount(), 1); + const auto &link_attributes = link.GetAttributes(); + EXPECT_EQ(link_attributes.size(), 1); + EXPECT_EQ(opentelemetry::nostd::get(link_attributes.at(kKey1)), "12"); +} + +TEST(SpanData, SpanLimitsNoLimitDefault) +{ + constexpr std::uint32_t kMaxCount = 500; + SpanData recordable; + std::map attributes; + + for (std::uint32_t i = 0; i < kMaxCount; ++i) + { + attributes["attribute_" + std::to_string(i)] = std::to_string(i); + } + + for (std::uint32_t i = 0; i < kMaxCount; ++i) + { + recordable.SetAttribute("attribute_" + std::to_string(i), i); + recordable.AddEvent("event_" + std::to_string(i), std::chrono::system_clock::now(), + common::MakeAttributes(attributes)); + recordable.AddLink(trace_api::SpanContext::GetInvalid(), common::MakeAttributes(attributes)); + } + + EXPECT_EQ(recordable.GetAttributes().size(), kMaxCount); + EXPECT_EQ(recordable.GetDroppedAttributesCount(), 0u); + EXPECT_EQ(recordable.GetEvents().size(), kMaxCount); + EXPECT_EQ(recordable.GetDroppedEventsCount(), 0u); + EXPECT_EQ(recordable.GetLinks().size(), kMaxCount); + EXPECT_EQ(recordable.GetDroppedLinksCount(), 0u); + EXPECT_EQ(recordable.GetEvents().at(0).GetDroppedAttributesCount(), 0u); + EXPECT_EQ(recordable.GetLinks().at(0).GetDroppedAttributesCount(), 0u); +} + +TEST(SpanData, SpanLimitsDuplicateAttributeKeys) +{ + SpanData recordable; + opentelemetry::sdk::trace::SpanLimits limits; + limits.attribute_count_limit = 2; + limits.attribute_value_length_limit = 2; + limits.link_count_limit = 1; + limits.link_attribute_count_limit = 2; + limits.event_count_limit = 1; + limits.event_attribute_count_limit = 2; + + recordable.SetSpanLimits(limits); + + // Create three attributes. One has a duplicate key. + std::vector> attributes{ + {"attribute_one", "AA"}, {"attribute_two", "BB"}, {"attribute_one", "CC"}}; + + for (const auto &keyvalue : attributes) + { + recordable.SetAttribute(keyvalue.first, keyvalue.second); + } + + recordable.AddEvent("event", std::chrono::system_clock::now(), + common::MakeAttributes(attributes)); + + recordable.AddLink(trace_api::SpanContext::GetInvalid(), common::MakeAttributes(attributes)); + + EXPECT_EQ(recordable.GetAttributes().size(), 2u); + EXPECT_EQ(recordable.GetDroppedAttributesCount(), 0u); + EXPECT_EQ(opentelemetry::nostd::get(recordable.GetAttributes().at("attribute_one")), + "CC"); + EXPECT_EQ(opentelemetry::nostd::get(recordable.GetAttributes().at("attribute_two")), + "BB"); + + EXPECT_EQ(recordable.GetEvents().size(), 1u); + EXPECT_EQ(recordable.GetDroppedEventsCount(), 0u); + const auto &event = recordable.GetEvents().at(0); + EXPECT_EQ(event.GetAttributes().size(), 2u); + EXPECT_EQ(event.GetDroppedAttributesCount(), 0u); + EXPECT_EQ(opentelemetry::nostd::get(event.GetAttributes().at("attribute_one")), + "CC"); + EXPECT_EQ(opentelemetry::nostd::get(event.GetAttributes().at("attribute_two")), + "BB"); + + EXPECT_EQ(recordable.GetLinks().size(), 1u); + EXPECT_EQ(recordable.GetDroppedLinksCount(), 0u); + const auto &link = recordable.GetLinks().at(0); + EXPECT_EQ(link.GetAttributes().size(), 2u); + EXPECT_EQ(link.GetDroppedAttributesCount(), 0u); + EXPECT_EQ(opentelemetry::nostd::get(link.GetAttributes().at("attribute_one")), "CC"); + EXPECT_EQ(opentelemetry::nostd::get(link.GetAttributes().at("attribute_two")), "BB"); +} diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index 687f63d07f..66efbbf522 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -2,13 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include -#include -#include - #include +#include +#include #include +#include +#include #include +#include +#include #include "opentelemetry/common/macros.h" #include "opentelemetry/exporters/memory/in_memory_span_exporter.h" @@ -26,6 +28,7 @@ #include "opentelemetry/sdk/trace/samplers/always_on.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" @@ -35,7 +38,6 @@ #include "opentelemetry/trace/tracer.h" #if OPENTELEMETRY_ABI_VERSION_NO >= 2 -# include # include # include # include @@ -518,3 +520,66 @@ TEST(TracerProvider, UpdateTracerConfiguratorConcurrentStartSpan) EXPECT_TRUE(tracer->Enabled()); #endif } + +TEST(TracerProvider, SpanLimitsSpecDefaults) +{ + SpanLimits limits; + EXPECT_EQ(limits.attribute_count_limit, 128u); + EXPECT_EQ(limits.event_count_limit, 128u); + EXPECT_EQ(limits.link_count_limit, 128u); + EXPECT_EQ(limits.event_attribute_count_limit, 128u); + EXPECT_EQ(limits.link_attribute_count_limit, 128u); + EXPECT_EQ(limits.attribute_value_length_limit, (std::numeric_limits::max)()); +} + +TEST(TracerProvider, SpanLimitsNoLimits) +{ + SpanLimits limits = SpanLimits::NoLimits(); + EXPECT_EQ(limits.attribute_count_limit, (std::numeric_limits::max)()); + EXPECT_EQ(limits.attribute_value_length_limit, (std::numeric_limits::max)()); + EXPECT_EQ(limits.event_count_limit, (std::numeric_limits::max)()); + EXPECT_EQ(limits.link_count_limit, (std::numeric_limits::max)()); + EXPECT_EQ(limits.event_attribute_count_limit, (std::numeric_limits::max)()); + EXPECT_EQ(limits.link_attribute_count_limit, (std::numeric_limits::max)()); +} + +TEST(TracerProvider, SpanLimitsTracerProviderFactoryCreate) +{ + SpanLimits limits; + limits.attribute_count_limit = 5; + limits.attribute_value_length_limit = 10; + limits.event_count_limit = 3; + limits.link_count_limit = 2; + limits.event_attribute_count_limit = 4; + limits.link_attribute_count_limit = 4; + + auto provider = TracerProviderFactory::Create( + std::make_unique(nullptr), Resource::Create({}), + std::make_unique(), std::make_unique(), + std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()).Build()), + limits); + + const auto &stored = provider->GetSpanLimits(); + EXPECT_EQ(stored.attribute_count_limit, limits.attribute_count_limit); + EXPECT_EQ(stored.attribute_value_length_limit, limits.attribute_value_length_limit); + EXPECT_EQ(stored.event_count_limit, limits.event_count_limit); + EXPECT_EQ(stored.link_count_limit, limits.link_count_limit); + EXPECT_EQ(stored.event_attribute_count_limit, limits.event_attribute_count_limit); + EXPECT_EQ(stored.link_attribute_count_limit, limits.link_attribute_count_limit); +} + +TEST(TracerProvider, SpanLimitsTracerProviderFactoryCreateDefault) +{ + auto provider = TracerProviderFactory::Create(std::make_unique(nullptr)); + + const auto no_limits = SpanLimits::NoLimits(); + + const auto &limits = provider->GetSpanLimits(); + EXPECT_EQ(limits.attribute_count_limit, no_limits.attribute_count_limit); + EXPECT_EQ(limits.event_count_limit, no_limits.event_count_limit); + EXPECT_EQ(limits.link_count_limit, no_limits.link_count_limit); + EXPECT_EQ(limits.event_attribute_count_limit, no_limits.event_attribute_count_limit); + EXPECT_EQ(limits.link_attribute_count_limit, no_limits.link_attribute_count_limit); + EXPECT_EQ(limits.attribute_value_length_limit, no_limits.attribute_value_length_limit); +} diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index 8a2fbcd5fd..d3107db47a 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -38,6 +38,7 @@ #include "opentelemetry/sdk/trace/samplers/parent.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" @@ -188,7 +189,8 @@ std::shared_ptr initTracer( IdGenerator *id_generator = new RandomIdGenerator, const ScopeConfigurator &tracer_configurator = ScopeConfigurator::Builder(TracerConfig::Default()).Build(), - std::unique_ptr scope = InstrumentationScope::Create("")) + std::unique_ptr scope = InstrumentationScope::Create(""), + opentelemetry::sdk::trace::SpanLimits span_limits = SpanLimits::NoLimits()) { auto processor = std::unique_ptr(new SimpleSpanProcessor(std::move(exporter))); std::vector> processors; @@ -197,7 +199,7 @@ std::shared_ptr initTracer( auto context = std::make_shared( std::move(processors), resource, std::unique_ptr(sampler), std::unique_ptr(id_generator), - std::make_unique>(tracer_configurator)); + std::make_unique>(tracer_configurator), span_limits); return std::shared_ptr(new Tracer(context, std::move(scope))); } @@ -1482,3 +1484,51 @@ TEST(Tracer, SpanSamplerDecision) } EXPECT_EQ(3, span_data->GetSpans().size()); } + +TEST(Tracer, SpanLimits) +{ + opentelemetry::sdk::trace::SpanLimits limits; + limits.attribute_count_limit = 2; + limits.attribute_value_length_limit = 5; + limits.event_count_limit = 2; + limits.event_attribute_count_limit = 2; + + InMemorySpanExporter *exporter = new InMemorySpanExporter(); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer( + std::unique_ptr{exporter}, new AlwaysOnSampler(), new RandomIdGenerator, + ScopeConfigurator::Builder(TracerConfig::Default()).Build(), + InstrumentationScope::Create(""), limits); + + std::vector> attributes = { + {"one", "1_value"}, {"two", "2_value"}, {"three", "3_value"}}; + + { + auto span = tracer->StartSpan("span_with_limits", attributes); + span->SetAttribute("four", "4_value"); + span->AddEvent("event1", attributes); + span->AddEvent("event2", attributes); + span->AddEvent("event3", attributes); + span->End(); + } + + const auto span_batch = span_data->GetSpans(); + ASSERT_EQ(1, span_batch.size()); + + const auto &recordable = span_batch.at(0); + + ASSERT_EQ(limits.attribute_count_limit, recordable->GetAttributes().size()); + EXPECT_EQ(limits.attribute_value_length_limit, + nostd::get(recordable->GetAttributes().at("one")).size()); + EXPECT_EQ("1_val", nostd::get(recordable->GetAttributes().at("one"))); + EXPECT_EQ("2_val", nostd::get(recordable->GetAttributes().at("two"))); + + const auto &events = recordable->GetEvents(); + ASSERT_EQ(limits.event_count_limit, events.size()); + EXPECT_EQ("event1", events.at(0).GetName()); + EXPECT_EQ("event2", events.at(1).GetName()); + EXPECT_EQ(limits.event_attribute_count_limit, events.at(0).GetAttributes().size()); + EXPECT_EQ(limits.event_attribute_count_limit, events.at(1).GetAttributes().size()); + EXPECT_EQ(limits.attribute_value_length_limit, + nostd::get(events.at(0).GetAttributes().at("one")).size()); +} From d4327d2a82308e1703592198771b7bb8fd3985e1 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Thu, 16 Jul 2026 06:27:13 -0400 Subject: [PATCH 42/49] [CONFIGURATION] Define and set spec default values in configuration objects (#4244) --- .../attribute_limits_configuration.h | 8 +- ...cket_histogram_aggregation_configuration.h | 15 +- ...batch_log_record_processor_configuration.h | 15 +- .../batch_span_processor_configuration.h | 14 +- .../boolean_attribute_value_configuration.h | 2 +- .../cardinality_limits_configuration.h | 22 ++- ...osable_probability_sampler_configuration.h | 4 +- .../sdk/configuration/configuration.h | 3 +- .../sdk/configuration/configuration_parser.h | 4 +- ...nsole_push_metric_exporter_configuration.h | 10 +- .../sdk/configuration/document_node.h | 6 +- .../double_attribute_value_configuration.h | 2 +- ...cket_histogram_aggregation_configuration.h | 5 +- .../configuration/grpc_tls_configuration.h | 4 +- .../integer_attribute_value_configuration.h | 2 +- .../jaeger_remote_sampler_configuration.h | 5 +- .../log_record_limits_configuration.h | 10 +- .../logger_config_configuration.h | 10 +- .../meter_provider_configuration.h | 2 +- ..._file_push_metric_exporter_configuration.h | 10 +- ...p_grpc_log_record_exporter_configuration.h | 5 +- ..._grpc_push_metric_exporter_configuration.h | 14 +- .../otlp_grpc_span_exporter_configuration.h | 5 +- ...p_http_log_record_exporter_configuration.h | 5 +- ..._http_push_metric_exporter_configuration.h | 14 +- .../otlp_http_span_exporter_configuration.h | 5 +- .../periodic_metric_reader_configuration.h | 9 +- ...theus_pull_metric_exporter_configuration.h | 18 +- .../sdk/configuration/ryml_document_node.h | 2 +- .../configuration/span_limits_configuration.h | 22 ++- ...ace_id_ratio_based_sampler_configuration.h | 5 +- .../configuration/view_stream_configuration.h | 6 +- sdk/src/configuration/configuration_parser.cc | 179 +++++++++++------- 33 files changed, 296 insertions(+), 146 deletions(-) diff --git a/sdk/include/opentelemetry/sdk/configuration/attribute_limits_configuration.h b/sdk/include/opentelemetry/sdk/configuration/attribute_limits_configuration.h index 69f311575c..105856ede0 100644 --- a/sdk/include/opentelemetry/sdk/configuration/attribute_limits_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/attribute_limits_configuration.h @@ -18,8 +18,12 @@ namespace configuration class AttributeLimitsConfiguration { public: - std::size_t attribute_value_length_limit; - std::size_t attribute_count_limit; + // TODO: spec default is no limit, using 4096 to preserve original behavior + static constexpr std::size_t kDefaultAttributeValueLengthLimit = 4096; + static constexpr std::size_t kDefaultAttributeCountLimit = 128; + + std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit}; + std::size_t attribute_count_limit{kDefaultAttributeCountLimit}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h b/sdk/include/opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h index 86d87f8393..e684eadae7 100644 --- a/sdk/include/opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "opentelemetry/sdk/configuration/aggregation_configuration.h" #include "opentelemetry/sdk/configuration/aggregation_configuration_visitor.h" #include "opentelemetry/version.h" @@ -18,14 +20,21 @@ namespace configuration class Base2ExponentialBucketHistogramAggregationConfiguration : public AggregationConfiguration { public: + // TODO: max_scale range is [-10, 20]. Negative values require GetSignedInteger in the parser and + // changing the type of max_scale to an int32_t to match the + // Base2ExponentialHistogramAggregationConfig. + static constexpr std::size_t kDefaultMaxScale = 20; // schema: minimum -10, maximum 20 + static constexpr std::size_t kDefaultMaxSize = 160; + static constexpr bool kDefaultRecordMinMax = true; + void Accept(AggregationConfigurationVisitor *visitor) const override { visitor->VisitBase2ExponentialBucketHistogram(this); } - std::size_t max_scale{0}; - std::size_t max_size{0}; - bool record_min_max{false}; + std::size_t max_scale{kDefaultMaxScale}; + std::size_t max_size{kDefaultMaxSize}; + bool record_min_max{kDefaultRecordMinMax}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h b/sdk/include/opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h index 17570f0552..a011a907a8 100644 --- a/sdk/include/opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "opentelemetry/sdk/configuration/log_record_exporter_configuration.h" @@ -21,15 +22,21 @@ namespace configuration class BatchLogRecordProcessorConfiguration : public LogRecordProcessorConfiguration { public: + // TODO: spec default is 1000ms for schedule_delay, using 5000ms to preserve original behavior + static constexpr std::size_t kDefaultScheduleDelayMs = 5000; + static constexpr std::size_t kDefaultExportTimeoutMs = 30000; + static constexpr std::size_t kDefaultMaxQueueSize = 2048; + static constexpr std::size_t kDefaultMaxExportBatchSize = 512; + void Accept(LogRecordProcessorConfigurationVisitor *visitor) const override { visitor->VisitBatch(this); } - std::size_t schedule_delay{0}; - std::size_t export_timeout{0}; - std::size_t max_queue_size{0}; - std::size_t max_export_batch_size{0}; + std::size_t schedule_delay{kDefaultScheduleDelayMs}; + std::size_t export_timeout{kDefaultExportTimeoutMs}; + std::size_t max_queue_size{kDefaultMaxQueueSize}; + std::size_t max_export_batch_size{kDefaultMaxExportBatchSize}; std::unique_ptr exporter; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/batch_span_processor_configuration.h b/sdk/include/opentelemetry/sdk/configuration/batch_span_processor_configuration.h index 1230f616a2..40446aac20 100644 --- a/sdk/include/opentelemetry/sdk/configuration/batch_span_processor_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/batch_span_processor_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "opentelemetry/sdk/configuration/span_exporter_configuration.h" @@ -21,15 +22,20 @@ namespace configuration class BatchSpanProcessorConfiguration : public SpanProcessorConfiguration { public: + static constexpr std::size_t kDefaultScheduleDelayMs = 5000; + static constexpr std::size_t kDefaultExportTimeoutMs = 30000; + static constexpr std::size_t kDefaultMaxQueueSize = 2048; + static constexpr std::size_t kDefaultMaxExportBatchSize = 512; + void Accept(SpanProcessorConfigurationVisitor *visitor) const override { visitor->VisitBatch(this); } - std::size_t schedule_delay{0}; - std::size_t export_timeout{0}; - std::size_t max_queue_size{0}; - std::size_t max_export_batch_size{0}; + std::size_t schedule_delay{kDefaultScheduleDelayMs}; + std::size_t export_timeout{kDefaultExportTimeoutMs}; + std::size_t max_queue_size{kDefaultMaxQueueSize}; + std::size_t max_export_batch_size{kDefaultMaxExportBatchSize}; std::unique_ptr exporter; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h b/sdk/include/opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h index 2e2700f6eb..3fb81a3465 100644 --- a/sdk/include/opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h @@ -23,7 +23,7 @@ class BooleanAttributeValueConfiguration : public AttributeValueConfiguration visitor->VisitBoolean(this); } - bool value; + bool value{false}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/cardinality_limits_configuration.h b/sdk/include/opentelemetry/sdk/configuration/cardinality_limits_configuration.h index 808663cf4c..a65b90abe7 100644 --- a/sdk/include/opentelemetry/sdk/configuration/cardinality_limits_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/cardinality_limits_configuration.h @@ -18,15 +18,19 @@ namespace configuration class CardinalityLimitsConfiguration { public: - std::size_t default_limit; - // For all limits, 0 means unset, use default_limit - std::size_t counter; - std::size_t gauge; - std::size_t histogram; - std::size_t observable_counter; - std::size_t observable_gauge; - std::size_t observable_up_down_counter; - std::size_t up_down_counter; + static constexpr std::size_t kDefaultLimit = 2000; + // 0 is a sentinel: inherit from default_limit (schema exclusiveMinimum: 0) + static constexpr std::size_t kInheritDefault = 0; + + std::size_t default_limit{kDefaultLimit}; + // For all limits, kInheritDefault means unset, use default_limit + std::size_t counter{kInheritDefault}; + std::size_t gauge{kInheritDefault}; + std::size_t histogram{kInheritDefault}; + std::size_t observable_counter{kInheritDefault}; + std::size_t observable_gauge{kInheritDefault}; + std::size_t observable_up_down_counter{kInheritDefault}; + std::size_t up_down_counter{kInheritDefault}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h index b5d63a40a3..a270a4c17f 100644 --- a/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h @@ -15,8 +15,10 @@ namespace configuration class ComposableProbabilitySamplerConfiguration : public ComposableSamplerConfiguration { public: + static constexpr double kDefaultRatio = 1.0; + ComposableProbabilitySamplerConfiguration() = default; - double ratio{1.0}; + double ratio{kDefaultRatio}; void Accept(SamplerConfigurationVisitor *visitor) const override; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/configuration.h b/sdk/include/opentelemetry/sdk/configuration/configuration.h index 05dfffcb80..a7fbac4845 100644 --- a/sdk/include/opentelemetry/sdk/configuration/configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/configuration.h @@ -58,7 +58,8 @@ namespace configuration class Configuration { public: - Configuration(std::unique_ptr doc) : doc_(std::move(doc)) {} + Configuration() = default; + explicit Configuration(std::unique_ptr doc) : doc_(std::move(doc)) {} Configuration(Configuration &&) = delete; Configuration(const Configuration &) = delete; Configuration &operator=(Configuration &&) = delete; diff --git a/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h b/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h index 0566425946..84f5811874 100644 --- a/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h +++ b/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h @@ -474,8 +474,8 @@ class ConfigurationParser private: std::string version_; - int version_major_; - int version_minor_; + int version_major_{}; + int version_minor_{}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h index 8755b31e9a..bff56b3375 100644 --- a/sdk/include/opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h @@ -21,14 +21,18 @@ namespace configuration class ConsolePushMetricExporterConfiguration : public PushMetricExporterConfiguration { public: + static constexpr TemporalityPreference kDefaultTemporalityPreference = + TemporalityPreference::cumulative; + static constexpr DefaultHistogramAggregation kDefaultHistogramAggregation = + DefaultHistogramAggregation::explicit_bucket_histogram; + void Accept(PushMetricExporterConfigurationVisitor *visitor) const override { visitor->VisitConsole(this); } - TemporalityPreference temporality_preference{TemporalityPreference::cumulative}; - DefaultHistogramAggregation default_histogram_aggregation{ - DefaultHistogramAggregation::explicit_bucket_histogram}; + TemporalityPreference temporality_preference{kDefaultTemporalityPreference}; + DefaultHistogramAggregation default_histogram_aggregation{kDefaultHistogramAggregation}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/document_node.h b/sdk/include/opentelemetry/sdk/configuration/document_node.h index 646a15de17..7a725a3116 100644 --- a/sdk/include/opentelemetry/sdk/configuration/document_node.h +++ b/sdk/include/opentelemetry/sdk/configuration/document_node.h @@ -178,9 +178,9 @@ class PropertiesNodeConstIterator class DocumentNodeLocation { public: - size_t offset; - size_t line; - size_t col; + std::size_t offset{}; + std::size_t line{}; + std::size_t col{}; std::string filename; std::string ToString() const; diff --git a/sdk/include/opentelemetry/sdk/configuration/double_attribute_value_configuration.h b/sdk/include/opentelemetry/sdk/configuration/double_attribute_value_configuration.h index 2408d81bc0..b300e8bc29 100644 --- a/sdk/include/opentelemetry/sdk/configuration/double_attribute_value_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/double_attribute_value_configuration.h @@ -23,7 +23,7 @@ class DoubleAttributeValueConfiguration : public AttributeValueConfiguration visitor->VisitDouble(this); } - double value; + double value{0.0}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/explicit_bucket_histogram_aggregation_configuration.h b/sdk/include/opentelemetry/sdk/configuration/explicit_bucket_histogram_aggregation_configuration.h index 122e84d034..53101a4032 100644 --- a/sdk/include/opentelemetry/sdk/configuration/explicit_bucket_histogram_aggregation_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/explicit_bucket_histogram_aggregation_configuration.h @@ -20,13 +20,16 @@ namespace configuration class ExplicitBucketHistogramAggregationConfiguration : public AggregationConfiguration { public: + // empty boundaries means use the SDK spec default ([0, 5, 10, ...]) + static constexpr bool kDefaultRecordMinMax = true; + void Accept(AggregationConfigurationVisitor *visitor) const override { visitor->VisitExplicitBucketHistogram(this); } std::vector boundaries; - bool record_min_max{false}; + bool record_min_max{kDefaultRecordMinMax}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/grpc_tls_configuration.h b/sdk/include/opentelemetry/sdk/configuration/grpc_tls_configuration.h index 9a10b8e6c8..b1120eda03 100644 --- a/sdk/include/opentelemetry/sdk/configuration/grpc_tls_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/grpc_tls_configuration.h @@ -19,10 +19,12 @@ namespace configuration class GrpcTlsConfiguration { public: + static constexpr bool kDefaultInsecure = false; + std::string ca_file; std::string key_file; std::string cert_file; - bool insecure{false}; + bool insecure{kDefaultInsecure}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/integer_attribute_value_configuration.h b/sdk/include/opentelemetry/sdk/configuration/integer_attribute_value_configuration.h index 60878d728b..28bc09c0be 100644 --- a/sdk/include/opentelemetry/sdk/configuration/integer_attribute_value_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/integer_attribute_value_configuration.h @@ -25,7 +25,7 @@ class IntegerAttributeValueConfiguration : public AttributeValueConfiguration visitor->VisitInteger(this); } - int64_t value; + std::int64_t value{0}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h index 19af801b61..cc882eb62c 100644 --- a/sdk/include/opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -21,13 +22,15 @@ namespace configuration class JaegerRemoteSamplerConfiguration : public SamplerConfiguration { public: + static constexpr std::size_t kDefaultIntervalMs = 60000; + void Accept(SamplerConfigurationVisitor *visitor) const override { visitor->VisitJaegerRemote(this); } std::string endpoint; - std::size_t interval{0}; + std::size_t interval{kDefaultIntervalMs}; std::unique_ptr initial_sampler; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/log_record_limits_configuration.h b/sdk/include/opentelemetry/sdk/configuration/log_record_limits_configuration.h index 81ab1383be..26a66cc661 100644 --- a/sdk/include/opentelemetry/sdk/configuration/log_record_limits_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/log_record_limits_configuration.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -16,8 +18,12 @@ namespace configuration class LogRecordLimitsConfiguration { public: - std::size_t attribute_value_length_limit; - std::size_t attribute_count_limit; + // TODO: spec default is no limit, using 4096 to preserve original behavior + static constexpr std::size_t kDefaultAttributeValueLengthLimit = 4096; + static constexpr std::size_t kDefaultAttributeCountLimit = 128; + + std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit}; + std::size_t attribute_count_limit{kDefaultAttributeCountLimit}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/logger_config_configuration.h b/sdk/include/opentelemetry/sdk/configuration/logger_config_configuration.h index 736699754f..5f79709105 100644 --- a/sdk/include/opentelemetry/sdk/configuration/logger_config_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/logger_config_configuration.h @@ -17,9 +17,13 @@ namespace configuration class LoggerConfigConfiguration { public: - bool enabled{true}; - SeverityNumber minimum_severity{SeverityNumber::trace}; - bool trace_based{false}; + static constexpr bool kDefaultEnabled = true; + static constexpr SeverityNumber kDefaultMinimumSeverity = SeverityNumber::trace; + static constexpr bool kDefaultTraceBased = false; + + bool enabled{kDefaultEnabled}; + SeverityNumber minimum_severity{kDefaultMinimumSeverity}; + bool trace_based{kDefaultTraceBased}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/meter_provider_configuration.h b/sdk/include/opentelemetry/sdk/configuration/meter_provider_configuration.h index a48d522e50..844516a5b6 100644 --- a/sdk/include/opentelemetry/sdk/configuration/meter_provider_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/meter_provider_configuration.h @@ -25,7 +25,7 @@ class MeterProviderConfiguration public: std::vector> readers; std::vector> views; - ExemplarFilter exemplar_filter = ExemplarFilter::trace_based; + ExemplarFilter exemplar_filter{ExemplarFilter::trace_based}; std::unique_ptr meter_configurator; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h index 03b75ef593..6744a5810b 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h @@ -23,15 +23,19 @@ namespace configuration class OtlpFilePushMetricExporterConfiguration : public PushMetricExporterConfiguration { public: + static constexpr TemporalityPreference kDefaultTemporalityPreference = + TemporalityPreference::cumulative; + static constexpr DefaultHistogramAggregation kDefaultHistogramAggregation = + DefaultHistogramAggregation::explicit_bucket_histogram; + void Accept(PushMetricExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpFile(this); } std::string output_stream; - TemporalityPreference temporality_preference{TemporalityPreference::cumulative}; - DefaultHistogramAggregation default_histogram_aggregation{ - DefaultHistogramAggregation::explicit_bucket_histogram}; + TemporalityPreference temporality_preference{kDefaultTemporalityPreference}; + DefaultHistogramAggregation default_histogram_aggregation{kDefaultHistogramAggregation}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_configuration.h index 1d0f4a40d5..e1f036eb4d 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -23,6 +24,8 @@ namespace configuration class OtlpGrpcLogRecordExporterConfiguration : public LogRecordExporterConfiguration { public: + static constexpr std::size_t kDefaultTimeoutMs = 10000; + void Accept(LogRecordExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpGrpc(this); @@ -33,7 +36,7 @@ class OtlpGrpcLogRecordExporterConfiguration : public LogRecordExporterConfigura std::unique_ptr headers; std::string headers_list; std::string compression; - std::size_t timeout{0}; + std::size_t timeout{kDefaultTimeoutMs}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_configuration.h index fe63a5e5b9..3078042ca0 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -25,6 +26,12 @@ namespace configuration class OtlpGrpcPushMetricExporterConfiguration : public PushMetricExporterConfiguration { public: + static constexpr std::size_t kDefaultTimeoutMs = 10000; + static constexpr TemporalityPreference kDefaultTemporalityPreference = + TemporalityPreference::cumulative; + static constexpr DefaultHistogramAggregation kDefaultHistogramAggregation = + DefaultHistogramAggregation::explicit_bucket_histogram; + void Accept(PushMetricExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpGrpc(this); @@ -35,10 +42,9 @@ class OtlpGrpcPushMetricExporterConfiguration : public PushMetricExporterConfigu std::unique_ptr headers; std::string headers_list; std::string compression; - std::size_t timeout{0}; - TemporalityPreference temporality_preference{TemporalityPreference::cumulative}; - DefaultHistogramAggregation default_histogram_aggregation{ - DefaultHistogramAggregation::explicit_bucket_histogram}; + std::size_t timeout{kDefaultTimeoutMs}; + TemporalityPreference temporality_preference{kDefaultTemporalityPreference}; + DefaultHistogramAggregation default_histogram_aggregation{kDefaultHistogramAggregation}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h index 2c2d116907..d61af2f9b1 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -23,6 +24,8 @@ namespace configuration class OtlpGrpcSpanExporterConfiguration : public SpanExporterConfiguration { public: + static constexpr std::size_t kDefaultTimeoutMs = 10000; + void Accept(SpanExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpGrpc(this); @@ -33,7 +36,7 @@ class OtlpGrpcSpanExporterConfiguration : public SpanExporterConfiguration std::unique_ptr headers; std::string headers_list; std::string compression; - std::size_t timeout{0}; + std::size_t timeout{kDefaultTimeoutMs}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_http_log_record_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_http_log_record_exporter_configuration.h index 7a27dfc77d..ac008febb5 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_http_log_record_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_http_log_record_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -24,6 +25,8 @@ namespace configuration class OtlpHttpLogRecordExporterConfiguration : public LogRecordExporterConfiguration { public: + static constexpr std::size_t kDefaultTimeoutMs = 10000; + void Accept(LogRecordExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpHttp(this); @@ -34,7 +37,7 @@ class OtlpHttpLogRecordExporterConfiguration : public LogRecordExporterConfigura std::unique_ptr headers; std::string headers_list; std::string compression; - std::size_t timeout{0}; + std::size_t timeout{kDefaultTimeoutMs}; OtlpHttpEncoding encoding{OtlpHttpEncoding::protobuf}; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_configuration.h index 4c71d45b46..a5da072812 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -26,6 +27,12 @@ namespace configuration class OtlpHttpPushMetricExporterConfiguration : public PushMetricExporterConfiguration { public: + static constexpr std::size_t kDefaultTimeoutMs = 10000; + static constexpr TemporalityPreference kDefaultTemporalityPreference = + TemporalityPreference::cumulative; + static constexpr DefaultHistogramAggregation kDefaultHistogramAggregation = + DefaultHistogramAggregation::explicit_bucket_histogram; + void Accept(PushMetricExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpHttp(this); @@ -36,11 +43,10 @@ class OtlpHttpPushMetricExporterConfiguration : public PushMetricExporterConfigu std::unique_ptr headers; std::string headers_list; std::string compression; - std::size_t timeout{0}; + std::size_t timeout{kDefaultTimeoutMs}; OtlpHttpEncoding encoding{OtlpHttpEncoding::protobuf}; - TemporalityPreference temporality_preference{TemporalityPreference::cumulative}; - DefaultHistogramAggregation default_histogram_aggregation{ - DefaultHistogramAggregation::explicit_bucket_histogram}; + TemporalityPreference temporality_preference{kDefaultTemporalityPreference}; + DefaultHistogramAggregation default_histogram_aggregation{kDefaultHistogramAggregation}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/otlp_http_span_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/otlp_http_span_exporter_configuration.h index 39353fa371..5867db0e24 100644 --- a/sdk/include/opentelemetry/sdk/configuration/otlp_http_span_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/otlp_http_span_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -24,6 +25,8 @@ namespace configuration class OtlpHttpSpanExporterConfiguration : public SpanExporterConfiguration { public: + static constexpr std::size_t kDefaultTimeoutMs = 10000; + void Accept(SpanExporterConfigurationVisitor *visitor) const override { visitor->VisitOtlpHttp(this); @@ -34,7 +37,7 @@ class OtlpHttpSpanExporterConfiguration : public SpanExporterConfiguration std::unique_ptr headers; std::string headers_list; std::string compression; - std::size_t timeout{0}; + std::size_t timeout{kDefaultTimeoutMs}; OtlpHttpEncoding encoding{OtlpHttpEncoding::protobuf}; }; diff --git a/sdk/include/opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h b/sdk/include/opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h index 21264ee88a..6aed2b4e25 100644 --- a/sdk/include/opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -24,13 +25,17 @@ namespace configuration class PeriodicMetricReaderConfiguration : public MetricReaderConfiguration { public: + // TODO: spec default is 60000ms for interval, using 5000ms to preserve original behavior + static constexpr std::size_t kDefaultIntervalMs = 5000; + static constexpr std::size_t kDefaultTimeoutMs = 30000; + void Accept(MetricReaderConfigurationVisitor *visitor) const override { visitor->VisitPeriodic(this); } - std::size_t interval{0}; - std::size_t timeout{0}; + std::size_t interval{kDefaultIntervalMs}; + std::size_t timeout{kDefaultTimeoutMs}; std::unique_ptr exporter; std::vector> producers; std::unique_ptr cardinality_limits; diff --git a/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h index c3aa03fe65..141db9250b 100644 --- a/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "opentelemetry/sdk/configuration/headers_configuration.h" @@ -23,17 +24,24 @@ namespace configuration class PrometheusPullMetricExporterConfiguration : public PullMetricExporterConfiguration { public: + static constexpr const char *kDefaultHost = "localhost"; + static constexpr std::size_t kDefaultPort = 9464; + static constexpr bool kDefaultWithoutScopeInfo = false; // schema: scope_info_enabled=true + static constexpr bool kDefaultWithoutTargetInfo = false; // schema: target_info_enabled=true + static constexpr TranslationStrategy kDefaultTranslationStrategy = + TranslationStrategy::UnderscoreEscapingWithSuffixes; + void Accept(PullMetricExporterConfigurationVisitor *visitor) const override { visitor->VisitPrometheus(this); } - std::string host; - std::size_t port{0}; - bool without_scope_info{false}; - bool without_target_info{false}; + std::string host{kDefaultHost}; + std::size_t port{kDefaultPort}; + bool without_scope_info{kDefaultWithoutScopeInfo}; + bool without_target_info{kDefaultWithoutTargetInfo}; std::unique_ptr with_resource_constant_labels; - TranslationStrategy translation_strategy; + TranslationStrategy translation_strategy{kDefaultTranslationStrategy}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/ryml_document_node.h b/sdk/include/opentelemetry/sdk/configuration/ryml_document_node.h index 82cdb8e683..23e559977a 100644 --- a/sdk/include/opentelemetry/sdk/configuration/ryml_document_node.h +++ b/sdk/include/opentelemetry/sdk/configuration/ryml_document_node.h @@ -70,7 +70,7 @@ class RymlDocumentNode : public DocumentNode const RymlDocument *doc_; ryml::ConstNodeRef node_; - std::size_t depth_; + std::size_t depth_{}; }; class RymlDocumentNodeConstIteratorImpl : public DocumentNodeConstIteratorImpl diff --git a/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h b/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h index 25e417113d..4c0556308c 100644 --- a/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/span_limits_configuration.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -16,12 +18,20 @@ namespace configuration class SpanLimitsConfiguration { public: - std::size_t attribute_value_length_limit; - std::uint32_t attribute_count_limit; - std::uint32_t event_count_limit; - std::uint32_t link_count_limit; - std::uint32_t event_attribute_count_limit; - std::uint32_t link_attribute_count_limit; + // TODO: spec default is no limit, using 4096 to preserve original behavior + static constexpr std::size_t kDefaultAttributeValueLengthLimit = 4096; + static constexpr std::uint32_t kDefaultAttributeCountLimit = 128; + static constexpr std::uint32_t kDefaultEventCountLimit = 128; + static constexpr std::uint32_t kDefaultLinkCountLimit = 128; + static constexpr std::uint32_t kDefaultEventAttributeCountLimit = 128; + static constexpr std::uint32_t kDefaultLinkAttributeCountLimit = 128; + + std::size_t attribute_value_length_limit{kDefaultAttributeValueLengthLimit}; + std::uint32_t attribute_count_limit{kDefaultAttributeCountLimit}; + std::uint32_t event_count_limit{kDefaultEventCountLimit}; + std::uint32_t link_count_limit{kDefaultLinkCountLimit}; + std::uint32_t event_attribute_count_limit{kDefaultEventAttributeCountLimit}; + std::uint32_t link_attribute_count_limit{kDefaultLinkAttributeCountLimit}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h b/sdk/include/opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h index bc19cad509..b884142a77 100644 --- a/sdk/include/opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h @@ -18,12 +18,15 @@ namespace configuration class TraceIdRatioBasedSamplerConfiguration : public SamplerConfiguration { public: + // TODO: spec default is 1.0, using 0.0 to preserve original behavior + static constexpr double kDefaultRatio = 0.0; + void Accept(SamplerConfigurationVisitor *visitor) const override { visitor->VisitTraceIdRatioBased(this); } - double ratio{0.0}; + double ratio{kDefaultRatio}; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/view_stream_configuration.h b/sdk/include/opentelemetry/sdk/configuration/view_stream_configuration.h index 006c83d460..1f10d837c2 100644 --- a/sdk/include/opentelemetry/sdk/configuration/view_stream_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/view_stream_configuration.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -22,10 +23,13 @@ namespace configuration class ViewStreamConfiguration { public: + // 0 is a sentinel: inherit cardinality limit from the metric reader (schema exclusiveMinimum: 0) + static constexpr std::size_t kInheritFromReader = 0; + std::string name; std::string description; std::unique_ptr aggregation; - std::size_t aggregation_cardinality_limit; + std::size_t aggregation_cardinality_limit{kInheritFromReader}; std::unique_ptr attribute_keys; }; diff --git a/sdk/src/configuration/configuration_parser.cc b/sdk/src/configuration/configuration_parser.cc index c83f329d0f..cc83201a97 100644 --- a/sdk/src/configuration/configuration_parser.cc +++ b/sdk/src/configuration/configuration_parser.cc @@ -356,10 +356,13 @@ std::unique_ptr ConfigurationParser::ParseAttributeLimitsConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = AttributeLimitsConfiguration; + auto model = std::make_unique(); - model->attribute_value_length_limit = node->GetInteger("attribute_value_length_limit", 4096); - model->attribute_count_limit = node->GetInteger("attribute_count_limit", 128); + model->attribute_value_length_limit = + node->GetInteger("attribute_value_length_limit", Config::kDefaultAttributeValueLengthLimit); + model->attribute_count_limit = + node->GetInteger("attribute_count_limit", Config::kDefaultAttributeCountLimit); return model; } @@ -379,12 +382,13 @@ std::unique_ptr ConfigurationParser::ParseHttpTlsConfigura std::unique_ptr ConfigurationParser::ParseGrpcTlsConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = GrpcTlsConfiguration; + auto model = std::make_unique(); model->ca_file = node->GetString("ca_file", ""); model->key_file = node->GetString("key_file", ""); model->cert_file = node->GetString("cert_file", ""); - model->insecure = node->GetBoolean("insecure", false); + model->insecure = node->GetBoolean("insecure", Config::kDefaultInsecure); return model; } @@ -393,7 +397,8 @@ std::unique_ptr ConfigurationParser::ParseOtlpHttpLogRecordExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = OtlpHttpLogRecordExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; model->endpoint = node->GetRequiredString("endpoint"); @@ -412,7 +417,7 @@ ConfigurationParser::ParseOtlpHttpLogRecordExporterConfiguration( model->headers_list = node->GetString("headers_list", ""); model->compression = node->GetString("compression", ""); - model->timeout = node->GetInteger("timeout", 10000); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); const std::string encoding = node->GetString("encoding", "protobuf"); model->encoding = ParseOtlpHttpEncoding(node, encoding); @@ -424,7 +429,8 @@ std::unique_ptr ConfigurationParser::ParseOtlpGrpcLogRecordExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = OtlpGrpcLogRecordExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; model->endpoint = node->GetRequiredString("endpoint"); @@ -443,7 +449,7 @@ ConfigurationParser::ParseOtlpGrpcLogRecordExporterConfiguration( model->headers_list = node->GetString("headers_list", ""); model->compression = node->GetString("compression", ""); - model->timeout = node->GetInteger("timeout", 10000); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); return model; } @@ -534,13 +540,15 @@ std::unique_ptr ConfigurationParser::ParseBatchLogRecordProcessorConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = BatchLogRecordProcessorConfiguration; + auto model = std::make_unique(); std::unique_ptr child; - model->schedule_delay = node->GetInteger("schedule_delay", 5000); - model->export_timeout = node->GetInteger("export_timeout", 30000); - model->max_queue_size = node->GetInteger("max_queue_size", 2048); - model->max_export_batch_size = node->GetInteger("max_export_batch_size", 512); + model->schedule_delay = node->GetInteger("schedule_delay", Config::kDefaultScheduleDelayMs); + model->export_timeout = node->GetInteger("export_timeout", Config::kDefaultExportTimeoutMs); + model->max_queue_size = node->GetInteger("max_queue_size", Config::kDefaultMaxQueueSize); + model->max_export_batch_size = + node->GetInteger("max_export_batch_size", Config::kDefaultMaxExportBatchSize); child = node->GetRequiredChildNode("exporter"); model->exporter = ParseLogRecordExporterConfiguration(child); @@ -618,10 +626,13 @@ std::unique_ptr ConfigurationParser::ParseLogRecordLimitsConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = LogRecordLimitsConfiguration; + auto model = std::make_unique(); - model->attribute_value_length_limit = node->GetInteger("attribute_value_length_limit", 4096); - model->attribute_count_limit = node->GetInteger("attribute_count_limit", 128); + model->attribute_value_length_limit = + node->GetInteger("attribute_value_length_limit", Config::kDefaultAttributeValueLengthLimit); + model->attribute_count_limit = + node->GetInteger("attribute_count_limit", Config::kDefaultAttributeCountLimit); return model; } @@ -629,13 +640,14 @@ ConfigurationParser::ParseLogRecordLimitsConfiguration( LoggerConfigConfiguration ConfigurationParser::ParseLoggerConfigConfiguration( const std::unique_ptr &node) const { - LoggerConfigConfiguration model; - model.enabled = node->GetBoolean("enabled", true); + using Config = LoggerConfigConfiguration; + Config model; + model.enabled = node->GetBoolean("enabled", Config::kDefaultEnabled); const std::string minimum_severity_str = node->GetString("minimum_severity", "trace"); model.minimum_severity = ParseSeverityNumber(node, minimum_severity_str); - model.trace_based = node->GetBoolean("trace_based", false); + model.trace_based = node->GetBoolean("trace_based", Config::kDefaultTraceBased); return model; } @@ -756,7 +768,8 @@ std::unique_ptr ConfigurationParser::ParseOtlpHttpPushMetricExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = OtlpHttpPushMetricExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; model->endpoint = node->GetRequiredString("endpoint"); @@ -775,7 +788,7 @@ ConfigurationParser::ParseOtlpHttpPushMetricExporterConfiguration( model->headers_list = node->GetString("headers_list", ""); model->compression = node->GetString("compression", ""); - model->timeout = node->GetInteger("timeout", 10000); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); const std::string temporality_preference = node->GetString("temporality_preference", "cumulative"); @@ -796,7 +809,8 @@ std::unique_ptr ConfigurationParser::ParseOtlpGrpcPushMetricExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = OtlpGrpcPushMetricExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; model->endpoint = node->GetRequiredString("endpoint"); @@ -815,7 +829,7 @@ ConfigurationParser::ParseOtlpGrpcPushMetricExporterConfiguration( model->headers_list = node->GetString("headers_list", ""); model->compression = node->GetString("compression", ""); - model->timeout = node->GetInteger("timeout", 10000); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); const std::string temporality_preference = node->GetString("temporality_preference", "cumulative"); @@ -901,13 +915,16 @@ std::unique_ptr ConfigurationParser::ParsePrometheusPullMetricExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = PrometheusPullMetricExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; - model->host = node->GetString("host", "localhost"); - model->port = node->GetInteger("port", 9464); - model->without_scope_info = node->GetBoolean("without_scope_info", false); - model->without_target_info = node->GetBoolean("without_target_info", false); + model->host = node->GetString("host", Config::kDefaultHost); + model->port = node->GetInteger("port", Config::kDefaultPort); + model->without_scope_info = + node->GetBoolean("without_scope_info", Config::kDefaultWithoutScopeInfo); + model->without_target_info = + node->GetBoolean("without_target_info", Config::kDefaultWithoutTargetInfo); child = node->GetChildNode("with_resource_constant_labels"); if (child) @@ -1093,16 +1110,18 @@ std::unique_ptr ConfigurationParser::ParseCardinalityLimitsConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = CardinalityLimitsConfiguration; + auto model = std::make_unique(); - model->default_limit = node->GetInteger("default", 2000); - model->counter = node->GetInteger("counter", 0); - model->gauge = node->GetInteger("gauge", 0); - model->histogram = node->GetInteger("histogram", 0); - model->observable_counter = node->GetInteger("observable_counter", 0); - model->observable_gauge = node->GetInteger("observable_gauge", 0); - model->observable_up_down_counter = node->GetInteger("observable_up_down_counter", 0); - model->up_down_counter = node->GetInteger("up_down_counter", 0); + model->default_limit = node->GetInteger("default", Config::kDefaultLimit); + model->counter = node->GetInteger("counter", Config::kInheritDefault); + model->gauge = node->GetInteger("gauge", Config::kInheritDefault); + model->histogram = node->GetInteger("histogram", Config::kInheritDefault); + model->observable_counter = node->GetInteger("observable_counter", Config::kInheritDefault); + model->observable_gauge = node->GetInteger("observable_gauge", Config::kInheritDefault); + model->observable_up_down_counter = + node->GetInteger("observable_up_down_counter", Config::kInheritDefault); + model->up_down_counter = node->GetInteger("up_down_counter", Config::kInheritDefault); return model; } @@ -1111,11 +1130,12 @@ std::unique_ptr ConfigurationParser::ParsePeriodicMetricReaderConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = PeriodicMetricReaderConfiguration; + auto model = std::make_unique(); std::unique_ptr child; - model->interval = node->GetInteger("interval", 5000); - model->timeout = node->GetInteger("timeout", 30000); + model->interval = node->GetInteger("interval", Config::kDefaultIntervalMs); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); child = node->GetRequiredChildNode("exporter"); model->exporter = ParsePushMetricExporterConfiguration(child); @@ -1320,7 +1340,8 @@ std::unique_ptr ConfigurationParser::ParseExplicitBucketHistogramAggregationConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = ExplicitBucketHistogramAggregationConfiguration; + auto model = std::make_unique(); std::unique_ptr child; child = node->GetChildNode("boundaries"); @@ -1337,7 +1358,7 @@ ConfigurationParser::ParseExplicitBucketHistogramAggregationConfiguration( } } - model->record_min_max = node->GetBoolean("record_min_max", true); + model->record_min_max = node->GetBoolean("record_min_max", Config::kDefaultRecordMinMax); return model; } @@ -1346,11 +1367,12 @@ std::unique_ptr ConfigurationParser::ParseBase2ExponentialBucketHistogramAggregationConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = Base2ExponentialBucketHistogramAggregationConfiguration; + auto model = std::make_unique(); - model->max_scale = node->GetInteger("max_scale", 20); - model->max_size = node->GetInteger("max_size", 160); - model->record_min_max = node->GetBoolean("record_min_max", true); + model->max_scale = node->GetInteger("max_scale", Config::kDefaultMaxScale); + model->max_size = node->GetInteger("max_size", Config::kDefaultMaxSize); + model->record_min_max = node->GetBoolean("record_min_max", Config::kDefaultRecordMinMax); return model; } @@ -1427,12 +1449,14 @@ std::unique_ptr ConfigurationParser::ParseAggregationC std::unique_ptr ConfigurationParser::ParseViewStreamConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = ViewStreamConfiguration; + auto model = std::make_unique(); std::unique_ptr child; - model->name = node->GetString("name", ""); - model->description = node->GetString("description", ""); - model->aggregation_cardinality_limit = node->GetInteger("aggregation_cardinality_limit", 0); + model->name = node->GetString("name", ""); + model->description = node->GetString("description", ""); + model->aggregation_cardinality_limit = + node->GetInteger("aggregation_cardinality_limit", Config::kInheritFromReader); child = node->GetChildNode("aggregation"); if (child) @@ -1594,7 +1618,8 @@ std::unique_ptr ConfigurationParser::ParsePropagatorCon std::unique_ptr ConfigurationParser::ParseSpanLimitsConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = SpanLimitsConfiguration; + auto model = std::make_unique(); const auto get_valid_uint32 = [&node](const std::string &name, std::size_t default_value) { std::size_t value = node->GetInteger(name, default_value); @@ -1606,12 +1631,16 @@ std::unique_ptr ConfigurationParser::ParseSpanLimitsCon return static_cast(value); }; - model->attribute_value_length_limit = node->GetInteger("attribute_value_length_limit", 4096); - model->attribute_count_limit = get_valid_uint32("attribute_count_limit", 128); - model->event_count_limit = get_valid_uint32("event_count_limit", 128); - model->link_count_limit = get_valid_uint32("link_count_limit", 128); - model->event_attribute_count_limit = get_valid_uint32("event_attribute_count_limit", 128); - model->link_attribute_count_limit = get_valid_uint32("link_attribute_count_limit", 128); + model->attribute_value_length_limit = + node->GetInteger("attribute_value_length_limit", Config::kDefaultAttributeValueLengthLimit); + model->attribute_count_limit = + get_valid_uint32("attribute_count_limit", Config::kDefaultAttributeCountLimit); + model->event_count_limit = get_valid_uint32("event_count_limit", Config::kDefaultEventCountLimit); + model->link_count_limit = get_valid_uint32("link_count_limit", Config::kDefaultLinkCountLimit); + model->event_attribute_count_limit = + get_valid_uint32("event_attribute_count_limit", Config::kDefaultEventAttributeCountLimit); + model->link_attribute_count_limit = + get_valid_uint32("link_attribute_count_limit", Config::kDefaultLinkAttributeCountLimit); return model; } @@ -1642,6 +1671,8 @@ ConfigurationParser::ParseJaegerRemoteSamplerConfiguration( const std::unique_ptr &node, size_t depth) const { + using Config = JaegerRemoteSamplerConfiguration; + auto model = std::make_unique(); std::unique_ptr child; @@ -1650,7 +1681,7 @@ ConfigurationParser::ParseJaegerRemoteSamplerConfiguration( OTEL_INTERNAL_LOG_ERROR("JaegerRemoteSamplerConfiguration: FIXME"); model->endpoint = node->GetString("endpoint", "FIXME"); - model->interval = node->GetInteger("interval", 0); + model->interval = node->GetInteger("interval", Config::kDefaultIntervalMs); child = node->GetChildNode("initial_sampler"); if (child) @@ -1713,10 +1744,11 @@ ConfigurationParser::ParseTraceIdRatioBasedSamplerConfiguration( const std::unique_ptr &node, size_t /* depth */) const { - auto model = std::make_unique(); + using Config = TraceIdRatioBasedSamplerConfiguration; + auto model = std::make_unique(); std::unique_ptr child; - model->ratio = node->GetDouble("ratio", 0); + model->ratio = node->GetDouble("ratio", Config::kDefaultRatio); return model; } @@ -1742,8 +1774,9 @@ ConfigurationParser::ParseComposableProbabilitySamplerConfiguration( const std::unique_ptr &node, size_t /* depth */) const { + using Config = ComposableProbabilitySamplerConfiguration; auto model = std::make_unique(); - model->ratio = node->GetDouble("ratio", 1.0); + model->ratio = node->GetDouble("ratio", Config::kDefaultRatio); return model; } @@ -2019,7 +2052,8 @@ std::unique_ptr ConfigurationParser::ParseOtlpHttpSpanExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = OtlpHttpSpanExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; model->endpoint = node->GetRequiredString("endpoint"); @@ -2038,7 +2072,7 @@ ConfigurationParser::ParseOtlpHttpSpanExporterConfiguration( model->headers_list = node->GetString("headers_list", ""); model->compression = node->GetString("compression", ""); - model->timeout = node->GetInteger("timeout", 10000); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); const std::string encoding = node->GetString("encoding", "protobuf"); model->encoding = ParseOtlpHttpEncoding(node, encoding); @@ -2050,7 +2084,8 @@ std::unique_ptr ConfigurationParser::ParseOtlpGrpcSpanExporterConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = OtlpGrpcSpanExporterConfiguration; + auto model = std::make_unique(); std::unique_ptr child; model->endpoint = node->GetRequiredString("endpoint"); @@ -2069,7 +2104,7 @@ ConfigurationParser::ParseOtlpGrpcSpanExporterConfiguration( model->headers_list = node->GetString("headers_list", ""); model->compression = node->GetString("compression", ""); - model->timeout = node->GetInteger("timeout", 10000); + model->timeout = node->GetInteger("timeout", Config::kDefaultTimeoutMs); return model; } @@ -2159,13 +2194,15 @@ std::unique_ptr ConfigurationParser::ParseBatchSpanProcessorConfiguration( const std::unique_ptr &node) const { - auto model = std::make_unique(); + using Config = BatchSpanProcessorConfiguration; + auto model = std::make_unique(); std::unique_ptr child; - model->schedule_delay = node->GetInteger("schedule_delay", 5000); - model->export_timeout = node->GetInteger("export_timeout", 30000); - model->max_queue_size = node->GetInteger("max_queue_size", 2048); - model->max_export_batch_size = node->GetInteger("max_export_batch_size", 512); + model->schedule_delay = node->GetInteger("schedule_delay", Config::kDefaultScheduleDelayMs); + model->export_timeout = node->GetInteger("export_timeout", Config::kDefaultExportTimeoutMs); + model->max_queue_size = node->GetInteger("max_queue_size", Config::kDefaultMaxQueueSize); + model->max_export_batch_size = + node->GetInteger("max_export_batch_size", Config::kDefaultMaxExportBatchSize); child = node->GetRequiredChildNode("exporter"); model->exporter = ParseSpanExporterConfiguration(child); From 4fcd6177206508bf6bb9961049c58f973209fd10 Mon Sep 17 00:00:00 2001 From: Doug Barker <3782873+dbarker@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:47:28 -0400 Subject: [PATCH 43/49] [CMAKE] Upgrade to protobuf 35.1 and grpc 1.82.1 (#4255) --- cmake/grpc.cmake | 2 +- .../otlp/test/otlp_http_exporter_test.cc | 14 +++++++-- .../otlp_http_log_record_exporter_test.cc | 14 +++++++-- .../test/otlp_http_metric_exporter_test.cc | 30 +++++++++++++++---- install/cmake/CMakeLists.txt | 2 +- install/cmake/third_party_latest | 4 +-- third_party_release | 4 +-- 7 files changed, 54 insertions(+), 16 deletions(-) diff --git a/cmake/grpc.cmake b/cmake/grpc.cmake index a420ccb353..3b0cf2af4f 100644 --- a/cmake/grpc.cmake +++ b/cmake/grpc.cmake @@ -23,7 +23,6 @@ if(NOT gRPC_FOUND) "third_party/abseil-cpp" "third_party/protobuf" "third_party/cares/cares" - "third_party/boringssl-with-bazel" ) set(gRPC_PROVIDER "fetch_repository") @@ -38,6 +37,7 @@ if(NOT gRPC_FOUND) set(gRPC_BUILD_GRPC_PYTHON_PLUGIN OFF CACHE BOOL "" FORCE) set(gRPC_BUILD_GRPC_RUBY_PLUGIN OFF CACHE BOOL "" FORCE) set(gRPC_BUILD_GRPCPP_OTEL_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_SSL_PROVIDER "package" CACHE STRING "" FORCE) set(gRPC_ZLIB_PROVIDER "package" CACHE STRING "" FORCE) set(gRPC_RE2_PROVIDER "module" CACHE STRING "" FORCE) set(RE2_BUILD_TESTING OFF CACHE BOOL "" FORCE) diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index a77a5fe590..72adc689ed 100644 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -423,9 +423,14 @@ class OtlpHttpExporterTestPeer : public ::testing::Test [&mock_session, report_trace_id, &received_trace_id_counter]( const std::shared_ptr &callback) { opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request_body; - request_body.ParseFromArray( + const bool parsed = request_body.ParseFromArray( &mock_session->GetRequest()->body_[0], static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_spans_size() == 0 || request_body.resource_spans(0).scope_spans_size() == 0 || request_body.resource_spans(0).scope_spans(0).spans_size() == 0) @@ -520,9 +525,14 @@ class OtlpHttpExporterTestPeer : public ::testing::Test [&mock_session, report_trace_id, &received_trace_id_counter]( const std::shared_ptr &callback) { opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request_body; - request_body.ParseFromArray( + const bool parsed = request_body.ParseFromArray( &mock_session->GetRequest()->body_[0], static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_spans_size() == 0 || request_body.resource_spans(0).scope_spans_size() == 0 || request_body.resource_spans(0).scope_spans(0).spans_size() == 0) diff --git a/exporters/otlp/test/otlp_http_log_record_exporter_test.cc b/exporters/otlp/test/otlp_http_log_record_exporter_test.cc index 918d21b515..3dbf115b58 100644 --- a/exporters/otlp/test/otlp_http_log_record_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_log_record_exporter_test.cc @@ -462,9 +462,14 @@ class OtlpHttpLogRecordExporterTestPeer : public ::testing::Test [&mock_session, report_trace_id, report_span_id, &received_record_counter]( const std::shared_ptr &callback) { opentelemetry::proto::collector::logs::v1::ExportLogsServiceRequest request_body; - request_body.ParseFromArray( + const bool parsed = request_body.ParseFromArray( &mock_session->GetRequest()->body_[0], static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_logs_size() == 0 || request_body.resource_logs(0).scope_logs_size() == 0 || request_body.resource_logs(0).scope_logs(0).log_records_size() == 0) @@ -590,9 +595,14 @@ class OtlpHttpLogRecordExporterTestPeer : public ::testing::Test [&mock_session, report_trace_id, report_span_id, schema_url, &received_record_counter]( const std::shared_ptr &callback) { opentelemetry::proto::collector::logs::v1::ExportLogsServiceRequest request_body; - request_body.ParseFromArray( + const bool parsed = request_body.ParseFromArray( &mock_session->GetRequest()->body_[0], static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_logs_size() == 0 || request_body.resource_logs(0).scope_logs_size() == 0 || request_body.resource_logs(0).scope_logs(0).log_records_size() == 0) diff --git a/exporters/otlp/test/otlp_http_metric_exporter_test.cc b/exporters/otlp/test/otlp_http_metric_exporter_test.cc index 4934423ca4..787c982bf0 100644 --- a/exporters/otlp/test/otlp_http_metric_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_metric_exporter_test.cc @@ -301,8 +301,14 @@ class OtlpHttpMetricExporterTestPeer : public ::testing::Test const std::shared_ptr &callback) { opentelemetry::proto::collector::metrics::v1::ExportMetricsServiceRequest request_body; - request_body.ParseFromArray(&mock_session->GetRequest()->body_[0], - static_cast(mock_session->GetRequest()->body_.size())); + const bool parsed = request_body.ParseFromArray( + &mock_session->GetRequest()->body_[0], + static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_metrics_size() == 0 || request_body.resource_metrics(0).scope_metrics_size() == 0 || request_body.resource_metrics(0).scope_metrics(0).metrics_size() == 0) @@ -522,8 +528,14 @@ class OtlpHttpMetricExporterTestPeer : public ::testing::Test const std::shared_ptr &callback) { opentelemetry::proto::collector::metrics::v1::ExportMetricsServiceRequest request_body; - request_body.ParseFromArray(&mock_session->GetRequest()->body_[0], - static_cast(mock_session->GetRequest()->body_.size())); + const bool parsed = request_body.ParseFromArray( + &mock_session->GetRequest()->body_[0], + static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_metrics_size() == 0 || request_body.resource_metrics(0).scope_metrics_size() == 0 || request_body.resource_metrics(0).scope_metrics(0).metrics_size() == 0) @@ -788,8 +800,14 @@ class OtlpHttpMetricExporterTestPeer : public ::testing::Test const std::shared_ptr &callback) { opentelemetry::proto::collector::metrics::v1::ExportMetricsServiceRequest request_body; - request_body.ParseFromArray(&mock_session->GetRequest()->body_[0], - static_cast(mock_session->GetRequest()->body_.size())); + const bool parsed = request_body.ParseFromArray( + &mock_session->GetRequest()->body_[0], + static_cast(mock_session->GetRequest()->body_.size())); + EXPECT_TRUE(parsed); + if (!parsed) + { + return; + } if (request_body.resource_metrics_size() == 0 || request_body.resource_metrics(0).scope_metrics_size() == 0 || request_body.resource_metrics(0).scope_metrics(0).metrics_size() == 0) diff --git a/install/cmake/CMakeLists.txt b/install/cmake/CMakeLists.txt index 44c9f7bb82..427f28f998 100644 --- a/install/cmake/CMakeLists.txt +++ b/install/cmake/CMakeLists.txt @@ -223,7 +223,6 @@ if(grpc IN_LIST _THIRDPARTY_PACKAGE_LIST) GIT_TAG ${grpc_GIT_TAG} GIT_SHALLOW ON GIT_SUBMODULES "third_party/re2" "third_party/cares/cares" - "third_party/boringssl-with-bazel" PREFIX ${CMAKE_BINARY_DIR}/external/grpc INSTALL_DIR ${CMAKE_INSTALL_PREFIX} CMAKE_ARGS "${CMAKE_OPTIONS}" @@ -238,6 +237,7 @@ if(grpc IN_LIST _THIRDPARTY_PACKAGE_LIST) "-DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF" "-DgRPC_BUILD_GRPCPP_OTEL_PLUGIN=OFF" "-DRE2_BUILD_TESTING=OFF" + "-DgRPC_SSL_PROVIDER=package" "-DgRPC_ZLIB_PROVIDER=package" "-DgRPC_PROTOBUF_PROVIDER=package" "-DgRPC_PROTOBUF_PACKAGE_TYPE=CONFIG" diff --git a/install/cmake/third_party_latest b/install/cmake/third_party_latest index febf7bae79..e3d30c74b6 100644 --- a/install/cmake/third_party_latest +++ b/install/cmake/third_party_latest @@ -7,8 +7,8 @@ abseil=20250512.1 zlib=v1.3.2 curl=curl-8_21_0 -protobuf=v33.6 -grpc=v1.81.1 +protobuf=v35.1 +grpc=v1.82.1 benchmark=v1.9.4 googletest=v1.17.0 ms-gsl=v4.2.1 diff --git a/third_party_release b/third_party_release index fb7939f726..b1ff1cfc1e 100644 --- a/third_party_release +++ b/third_party_release @@ -16,8 +16,8 @@ abseil=20250512.1 zlib=v1.3.2 curl=curl-8_21_0 -protobuf=v33.6 -grpc=v1.81.1 +protobuf=v35.1 +grpc=v1.82.1 benchmark=v1.9.4 googletest=v1.17.0 ms-gsl=v4.2.1 From 40e58c5fca7229f8044df138258f7abb4356461d Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Thu, 16 Jul 2026 22:46:07 +0200 Subject: [PATCH 44/49] [RELEASE] Release opentelemetry-cpp 1.28.0 (#4233) --- CHANGELOG.md | 380 ++++++++++++++---- CMakeLists.txt | 2 +- MODULE.bazel | 2 +- api/include/opentelemetry/version.h | 2 +- .../opentelemetry/sdk/version/version.h | 2 +- sdk/src/version/version.cc | 6 +- tbump.toml | 2 +- 7 files changed, 309 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a654eb3243..27f2899caa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,58 +15,74 @@ Increment the: ## [Unreleased] -* [ETW] Ensure spans own their names until they are ended - [#4247](https://github.com/open-telemetry/opentelemetry-cpp/pull/4247) +## [1.28.0] 2026-07-16 -* [SDK] Apply metric cardinality limits to non-overflow attribute sets and - reserve the overflow point separately. - [#4236](https://github.com/open-telemetry/opentelemetry-cpp/pull/4236) +* [RELEASE] Bump main branch to 1.28.0-dev + [#4081](https://github.com/open-telemetry/opentelemetry-cpp/pull/4081) -* [CODE HEALTH] Move otlp, zipkin and prometheus test classes into anonymous namespace - [#4231](https://github.com/open-telemetry/opentelemetry-cpp/pull/4231) +* Bump step-security/harden-runner from 2.19.1 to 2.19.2 + [#4082](https://github.com/open-telemetry/opentelemetry-cpp/pull/4082) -* [CODE HEALTH] Move ext and exporters test classes into anonymous namespace - [#4225](https://github.com/open-telemetry/opentelemetry-cpp/pull/4225) +* Bump step-security/harden-runner from 2.19.2 to 2.19.3 + [#4084](https://github.com/open-telemetry/opentelemetry-cpp/pull/4084) -* [CODE HEALTH] Move api/test and sdk/test classes into anonymous namespace - [#4217](https://github.com/open-telemetry/opentelemetry-cpp/pull/4217) +* Fix IWYU Clang22 warnings + [#4083](https://github.com/open-telemetry/opentelemetry-cpp/pull/4083) -* [CMAKE] Fix and test WITH_API_ONLY option - [#4201](https://github.com/open-telemetry/opentelemetry-cpp/pull/4201) +* Bump github/codeql-action from 4.35.4 to 4.35.5 + [#4087](https://github.com/open-telemetry/opentelemetry-cpp/pull/4087) -* [SDK] Span limits can now be configured through the TracerProvider interface - and declaritive configuration. When set, limits are enforced by the - SpanData and OtlpRecordables recordables used by the standard exporters - [#4232](https://github.com/open-telemetry/opentelemetry-cpp/pull/4232) +* [CODE HEALTH] Remove unused alias declarations + [#4091](https://github.com/open-telemetry/opentelemetry-cpp/pull/4091) -* [API] Fix `TraceState::IsValidKey()` to comply with the W3C Trace Context - Level 2, where keys containing `@` and keys with more than 241 characters - before `@` or more than 14 characters after `@` are now accepted. - Only the total 256-character key length limit is enforced. - **Note**: this is a correctness fix to an inline API header; the observable - behavior of `IsValidKey`, `IsValidKeyRegEx`, and `IsValidKeyNonRegEx` - changes (see `docs/abi-policy.md`). - [#4194](https://github.com/open-telemetry/opentelemetry-cpp/pull/4194) +* Bump codecov/codecov-action from 6.0.0 to 6.0.1 + [#4092](https://github.com/open-telemetry/opentelemetry-cpp/pull/4092) -* [SDK] Add `TracerProvider::UpdateTracerConfigurator()` and example - [#4065](https://github.com/open-telemetry/opentelemetry-cpp/pull/4065) +* [SDK] MeterProvider: do not warn in destructor after explicit Shutdown + [#4085](https://github.com/open-telemetry/opentelemetry-cpp/pull/4085) -* [RELEASE] Bump main branch to 1.28.0-dev - [#4081](https://github.com/open-telemetry/opentelemetry-cpp/pull/4081) +* Fix: preserve delta start timestamp in fast path + [#4069](https://github.com/open-telemetry/opentelemetry-cpp/pull/4069) -* [CODE HEALTH] Fix IWYU Clang22 warnings - [#4083](https://github.com/open-telemetry/opentelemetry-cpp/pull/4083) +* Bump step-security/harden-runner from 2.19.3 to 2.19.4 + [#4098](https://github.com/open-telemetry/opentelemetry-cpp/pull/4098) -* [EXPORTER] Spec-compliant uint64_t attribute encoding in OTLP - [#4090](https://github.com/open-telemetry/opentelemetry-cpp/pull/4090) +* Bump docker/build-push-action from 7.1.0 to 7.2.0 + [#4097](https://github.com/open-telemetry/opentelemetry-cpp/pull/4097) -* [CODE HEALTH] Remove unused alias declarations - [#4091](https://github.com/open-telemetry/opentelemetry-cpp/pull/4091) +* Bump actions/stale from 10.2.0 to 10.3.0 + [#4096](https://github.com/open-telemetry/opentelemetry-cpp/pull/4096) -* [SDK] MeterProvider: do not warn in destructor after explicit Shutdown - [#4085](https://github.com/open-telemetry/opentelemetry-cpp/pull/4085) +* docs: update custom log handler example + [#4089](https://github.com/open-telemetry/opentelemetry-cpp/pull/4089) + +* [BUILD] Suppress deprecated warnings in generated semconv metric headers + [#4088](https://github.com/open-telemetry/opentelemetry-cpp/pull/4088) + +* Bump docker/setup-buildx-action from 4.0.0 to 4.1.0 + [#4103](https://github.com/open-telemetry/opentelemetry-cpp/pull/4103) + +* Bump github/codeql-action from 4.35.5 to 4.36.0 + [#4102](https://github.com/open-telemetry/opentelemetry-cpp/pull/4102) + +* Bump docker/setup-qemu-action from 4.0.0 to 4.1.0 + [#4107](https://github.com/open-telemetry/opentelemetry-cpp/pull/4107) + +* [CI] add cmake gcc maintainer abiv2 job + [#4105](https://github.com/open-telemetry/opentelemetry-cpp/pull/4105) + +* [DOC] Fix typos in SDK header comments + [#4111](https://github.com/open-telemetry/opentelemetry-cpp/pull/4111) + +* [CI] update codeql to cover all options with abiv2 + [#4106](https://github.com/open-telemetry/opentelemetry-cpp/pull/4106) -* [CODE HEALTH] Remove last unused nostd namespace alias in otlp_populate +* [EXPORTER] Convert uint64_t attribute values exceeding + INT64_MAX to string per OTel spec + [#4090](https://github.com/open-telemetry/opentelemetry-cpp/pull/4090) + +* [CODE HEALTH] Remove last unused nostd namespace alias + in otlp_populate_attribute_utils [#4114](https://github.com/open-telemetry/opentelemetry-cpp/pull/4114) * [CODE HEALTH] Move curl_http_test classes into anonymous namespace @@ -75,12 +91,24 @@ Increment the: * [CODE HEALTH] Move simple_log_record_processor_test into anonymous namespace [#4116](https://github.com/open-telemetry/opentelemetry-cpp/pull/4116) +* Bump actions/checkout from 6.0.2 to 6.0.3 + [#4118](https://github.com/open-telemetry/opentelemetry-cpp/pull/4118) + +* Bump github/codeql-action from 4.36.0 to 4.36.1 + [#4117](https://github.com/open-telemetry/opentelemetry-cpp/pull/4117) + * [CODE HEALTH] Move registry.cc propagator builders into anonymous namespace [#4121](https://github.com/open-telemetry/opentelemetry-cpp/pull/4121) * [CODE HEALTH] Move sdk_builder.cc builders into anonymous namespace [#4122](https://github.com/open-telemetry/opentelemetry-cpp/pull/4122) +* Bump github/codeql-action from 4.36.1 to 4.36.2 + [#4123](https://github.com/open-telemetry/opentelemetry-cpp/pull/4123) + +* [CODE HEALTH] Fix clang-tidy bugprone-exception-escape in sum_aggregation.cc + [#4109](https://github.com/open-telemetry/opentelemetry-cpp/pull/4109) + * [CODE HEALTH] Move logger_sdk_test classes into anonymous namespace [#4124](https://github.com/open-telemetry/opentelemetry-cpp/pull/4124) @@ -90,90 +118,284 @@ Increment the: * [CODE HEALTH] Move func_grpc_main classes into anonymous namespace [#4129](https://github.com/open-telemetry/opentelemetry-cpp/pull/4129) -* [CONFIGURATION] Implement missing minimum_severity and trace_based for - LoggerConfig declarative configuration +* [CONFIGURATION] Implementing minimum_severity and trace_based + attributes for LoggerConfig [#4131](https://github.com/open-telemetry/opentelemetry-cpp/pull/4131) -* [CODE HEALTH] Fix clang-tidy bugprone-exception-escape in ostream exporters - [#4137](https://github.com/open-telemetry/opentelemetry-cpp/pull/4137) +* [SDK] Support TracerConfigurator updates + [#4065](https://github.com/open-telemetry/opentelemetry-cpp/pull/4065) -* [API] (ABI v2) `Logger::EmitLogRecord(...)` templates now apply the - `Enabled` filter chain when a `Severity` is in args. v1 behavior is - unchanged. - [#4079](https://github.com/open-telemetry/opentelemetry-cpp/pull/4079) +* Bump codecov/codecov-action from 6.0.1 to 7.0.0 + [#4143](https://github.com/open-telemetry/opentelemetry-cpp/pull/4143) -* [API/SDK] (ABI v2) Add `Logger::CreateLogRecord` virtual taking - `const nostd::variant &` - for explicit-context record creation. `Logger::EmitLogRecord(args...)` - also detects a `Context`, `SpanContext` - or `TraceId` + `SpanId` [+ `TraceFlags`] in args and routes filtering. - [#4079](https://github.com/open-telemetry/opentelemetry-cpp/pull/4079) +* Bump bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml + [#4142](https://github.com/open-telemetry/opentelemetry-cpp/pull/4142) -* [SDK] Add `LogRecordProcessor::HasEnabledFilter()` so the SDK Logger can - include processor-level filtering in its extended-enabled cache. Defaults - to `true`. Built-in `SimpleLogRecordProcessor` and - `BatchLogRecordProcessor` override to `false` since they use the default - Enabled. - [#4079](https://github.com/open-telemetry/opentelemetry-cpp/pull/4079) +* [CODE HEALTH] Fix clang-tidy bugprone-exception-escape warnings in API + [#3964](https://github.com/open-telemetry/opentelemetry-cpp/pull/3964) -* [API/SDK] Replace `Context`-only signatures on - `LogRecordProcessor::Enabled`, - `LogRecordProcessor::EnabledImplementation`, - `Logger::EnabledImplementation` (v2), and `Logger::CreateLogRecord` (v2) - with `nostd::variant`. +* [API+SDK] apply filtering EmitLogRecord [#4079](https://github.com/open-telemetry/opentelemetry-cpp/pull/4079) -* [CI] iwyu and clang-tidy: use install_thirdparty.sh for third-party +* [CI] iwyu clang tidy install thirdparty script to unify dev container [#4136](https://github.com/open-telemetry/opentelemetry-cpp/pull/4136) -* [SDK] LogRecord attribute limits enforcement - [#4157](https://github.com/open-telemetry/opentelemetry-cpp/pull/4157) - -* [CONFIGURATION] File configuration: declarative resource detection types - [#4148](https://github.com/open-telemetry/opentelemetry-cpp/pull/4148) +* [CONTEXT] Fix env carrier spec compliance + [#4141](https://github.com/open-telemetry/opentelemetry-cpp/pull/4141) -* [SDK] Per-instrument creation time as delta first-interval start_ts +* [SDK] Delta temporality: use per-instrument creation time for first interval start_ts [#4144](https://github.com/open-telemetry/opentelemetry-cpp/pull/4144) -* [SDK] Add ComposableSampler and CompositeSampler with the consistent - probability sampling variants (AlwaysOn, AlwaysOff, ComposableProbability, - ParentThreshold, RuleBased) - [#4028](https://github.com/open-telemetry/opentelemetry-cpp/issues/4028) +* Bump bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml + [#4147](https://github.com/open-telemetry/opentelemetry-cpp/pull/4147) -* [SDK] Rename ComposableTraceIdRatioBasedSampler to ComposableProbabilitySampler - to align with the OpenTelemetry specification - [#4161](https://github.com/open-telemetry/opentelemetry-cpp/issues/4161) +* [EXPORTER] Add custom HTTP client + [#4071](https://github.com/open-telemetry/opentelemetry-cpp/pull/4071) + +* docs: add CNCF Slack join instructions + [#4153](https://github.com/open-telemetry/opentelemetry-cpp/pull/4153) + +* [SDK] Add ComposableSampler interface and CompositeSampler + [#4133](https://github.com/open-telemetry/opentelemetry-cpp/pull/4133) + +* Bump bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml + [#4150](https://github.com/open-telemetry/opentelemetry-cpp/pull/4150) + +* [CONFIGURATION] Declarative resource detection types + [#4148](https://github.com/open-telemetry/opentelemetry-cpp/pull/4148) * [BUILD] Fix protobuf build failure [#4154](https://github.com/open-telemetry/opentelemetry-cpp/pull/4154) -* [SEMANTIC CONVENTIONS] Generate event name constants (e.g. `semconv::exception::kException`) +* [SEMANTIC CONVENTIONS] Upgrade to semconv 1.42.0 + [#4156](https://github.com/open-telemetry/opentelemetry-cpp/pull/4156) + +* Bump rules_cc from 0.2.18 to 0.2.19 + [#4120](https://github.com/open-telemetry/opentelemetry-cpp/pull/4120) + +* [CMAKE] upgrade grpc and protobuf + [#4167](https://github.com/open-telemetry/opentelemetry-cpp/pull/4167) + +* Bump actions/checkout from 6.0.3 to 7.0.0 + [#4169](https://github.com/open-telemetry/opentelemetry-cpp/pull/4169) + +* [BENCHMARK] add span otlp recordable benchmark + [#4165](https://github.com/open-telemetry/opentelemetry-cpp/pull/4165) + +* [METRICS] Enable synchronous gauge delta temporality + and add tests for multi reader path + [#4159](https://github.com/open-telemetry/opentelemetry-cpp/pull/4159) + +* Add preview support for bound instruments + [#4042](https://github.com/open-telemetry/opentelemetry-cpp/pull/4042) + +* [API/SDK] add `WITH_LOG_FILTERING_PREVIEW` option + [#4160](https://github.com/open-telemetry/opentelemetry-cpp/pull/4160) + +* [SEMANTIC CONVENTIONS] semconv generate events [#4171](https://github.com/open-telemetry/opentelemetry-cpp/pull/4171) * [EXPORTER] Handle OTLP partial success response - [#4104](https://github.com/open-telemetry/opentelemetry-cpp/pull/4104) + [#4104](https://github.com/open-telemetry/openelemetry-cpp/pull/4104) + +* Bump rules_cc from 0.2.19 to 0.2.20 + [#4173](https://github.com/open-telemetry/opentelemetry-cpp/pull/4173) + +* [CI] pin ci workflow Bazel on MacOS runner to macos-15 + [#4176](https://github.com/open-telemetry/opentelemetry-cpp/pull/4176) + +* [ADMIN] Revert log filtering preview option + [#4174](https://github.com/open-telemetry/opentelemetry-cpp/pull/4174) + +* [TEST] fix metrics simple example + [#4175](https://github.com/open-telemetry/opentelemetry-cpp/pull/4175) + +* [EXPORTER] Replace new operator with std::make_unique + [#4181](https://github.com/open-telemetry/opentelemetry-cpp/pull/4181) + +* [DOC] update ABI policy docs to clarify breaking changes + [#4180](https://github.com/open-telemetry/opentelemetry-cpp/pull/4180) + +* [CODE HEALTH] Fix clang-tidy bugprone-exception-escape in ostream exporters + [#4137](https://github.com/open-telemetry/opentelemetry-cpp/pull/4137) + +* [CMAKE] Upgrade ryml to 0.15.2 + [#4185](https://github.com/open-telemetry/opentelemetry-cpp/pull/4185) * [CONFIGURATION] Apply default sampler when none is specified [#4170](https://github.com/open-telemetry/opentelemetry-cpp/pull/4170) +* [CMAKE] Upgrade curl to 8.21.0 + [#4187](https://github.com/open-telemetry/opentelemetry-cpp/pull/4187) + +* Bump actions/cache from 5.0.5 to 6.0.0 + [#4177](https://github.com/open-telemetry/opentelemetry-cpp/pull/4177) + +* Bump actions/cache from 6.0.0 to 6.1.0 + [#4189](https://github.com/open-telemetry/opentelemetry-cpp/pull/4189) + +* [API] fix the trace state regex to comply with the w3c trace context level 2 + [#4194](https://github.com/open-telemetry/opentelemetry-cpp/pull/4194) + * [CODE HEALTH] Move trace and baggage propagation test classes into anonymous namespace [#4199](https://github.com/open-telemetry/opentelemetry-cpp/pull/4199) +* [CMAKE] Add pkg-config support for OpenTelemetry proto installation + [#4192](https://github.com/open-telemetry/opentelemetry-cpp/pull/4192) + +* [CMAKE] Fix CMake WITH_API_ONLY option + [#4201](https://github.com/open-telemetry/opentelemetry-cpp/pull/4201) + * [CODE HEALTH] Move context propagation test classes into anonymous namespace [#4200](https://github.com/open-telemetry/opentelemetry-cpp/pull/4200) * [CODE HEALTH] Fix clang-tidy bugprone-unused-local-non-trivial-variable warnings [#4202](https://github.com/open-telemetry/opentelemetry-cpp/pull/4202) +* [SDK] LogRecord attribute limits enforcement + [#4157](https://github.com/open-telemetry/opentelemetry-cpp/pull/4157) + +* [SDK] Rename ComposableTraceIdRatioBasedSampler to ComposableProbabilitySampler + [#4210](https://github.com/open-telemetry/opentelemetry-cpp/pull/4210) + +* Bump docker/build-push-action from 7.2.0 to 7.3.0 + [#4204](https://github.com/open-telemetry/opentelemetry-cpp/pull/4204) + +* Bump docker/setup-qemu-action from 4.1.0 to 4.2.0 + [#4205](https://github.com/open-telemetry/opentelemetry-cpp/pull/4205) + +* Bump docker/setup-buildx-action from 4.1.0 to 4.2.0 + [#4207](https://github.com/open-telemetry/opentelemetry-cpp/pull/4207) + +* Bump rules_cc from 0.2.20 to 0.2.21 + [#4214](https://github.com/open-telemetry/opentelemetry-cpp/pull/4214) + +* Bump github/codeql-action/upload-sarif from 4.36.2 to 4.36.3 + [#4213](https://github.com/open-telemetry/opentelemetry-cpp/pull/4213) + +* Bump fossas/fossa-action from 1.9.0 to 2.0.0 + [#4212](https://github.com/open-telemetry/opentelemetry-cpp/pull/4212) + +* Bump github/codeql-action/init from 4.36.2 to 4.36.3 + [#4209](https://github.com/open-telemetry/opentelemetry-cpp/pull/4209) + +* [CODE HEALTH] Move api/test and sdk/test classes into anonymous namespace + [#4217](https://github.com/open-telemetry/opentelemetry-cpp/pull/4217) + * [CODE HEALTH] Fix clang-tidy bugprone-unchecked-string-to-number-conversion warnings [#4216](https://github.com/open-telemetry/opentelemetry-cpp/pull/4216) * [CODE HEALTH] Fix clang-tidy misc-override-with-different-visibility warnings [#4215](https://github.com/open-telemetry/opentelemetry-cpp/pull/4215) +* [BUILD] add missing link dependency for bazel + [#4220](https://github.com/open-telemetry/opentelemetry-cpp/pull/4220) + * [CODE HEALTH] Fix clang-tidy `bugprone-throwing-static-initialization` warnings [#4206](https://github.com/open-telemetry/opentelemetry-cpp/pull/4206) +* Bump rules_cc from 0.2.21 to 0.2.22 + [#4223](https://github.com/open-telemetry/opentelemetry-cpp/pull/4223) + +* Bump step-security/harden-runner from 2.19.4 to 2.20.0 + [#4222](https://github.com/open-telemetry/opentelemetry-cpp/pull/4222) + +* Bump bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml + [#4221](https://github.com/open-telemetry/opentelemetry-cpp/pull/4221) + +* [CODE HEALTH] Move ext and exporters test classes into anonymous namespace + [#4225](https://github.com/open-telemetry/opentelemetry-cpp/pull/4225) + +* Bump github/codeql-action/upload-sarif from 4.36.3 to 4.37.0 + [#4227](https://github.com/open-telemetry/opentelemetry-cpp/pull/4227) + +* Bump github/codeql-action/init from 4.36.3 to 4.37.0 + [#4229](https://github.com/open-telemetry/opentelemetry-cpp/pull/4229) + +* [SEMANTIC CONVENTIONS] Upgrade to semconv 1.43.0 + [#4230](https://github.com/open-telemetry/opentelemetry-cpp/pull/4230) + +* [CODE HEALTH] Move otlp, zipkin and prometheus test classes into anonymous namespace + [#4231](https://github.com/open-telemetry/opentelemetry-cpp/pull/4231) + +* [BAZEL] Upgrade to rapidyaml 1.15.2 + [#4234](https://github.com/open-telemetry/opentelemetry-cpp/pull/4234) + +* [BENCHMARK] add SpanData recordable benchmark and unify common utils + [#4203](https://github.com/open-telemetry/opentelemetry-cpp/pull/4203) + +* [CI] Add `modernize-` and `cppcoreguidelines-pro-` checks to clang-tidy (#4238) + [#4238](https://github.com/open-telemetry/opentelemetry-cpp/pull/4238) + +* [SDK] Apply metric cardinality limits to non-overflow attribute sets and + reserve the overflow point separately. + [#4236](https://github.com/open-telemetry/opentelemetry-cpp/pull/4236) + +* [CONFIGURATION] fix yaml configuration of the logger configurator + [#4241](https://github.com/open-telemetry/opentelemetry-cpp/pull/4241) + +* [SDK] Support LoggerConfigurator updates and example + [#4224](https://github.com/open-telemetry/opentelemetry-cpp/pull/4224) + +* Bump actions/stale from 10.3.0 to 10.4.0 + [#4242](https://github.com/open-telemetry/opentelemetry-cpp/pull/4242) + +* [CMAKE] Remove environment variable support from the add external components script + [#4211](https://github.com/open-telemetry/opentelemetry-cpp/pull/4211) + +* [ETW] Make spans own their names + [#4247](https://github.com/open-telemetry/opentelemetry-cpp/pull/4247) + +* [SDK] Span limits configuration + [#4232](https://github.com/open-telemetry/opentelemetry-cpp/pull/4232) + +* [CONFIGURATION] Define and set spec default values in configuration objects + [#4244](https://github.com/open-telemetry/opentelemetry-cpp/pull/4244) + +* [CMAKE] Upgrade to protobuf 35.1 and grpc 1.82.1 + [#4255](https://github.com/open-telemetry/opentelemetry-cpp/pull/4255) + +* [RELEASE] Release opentelemetry-cpp 1.28.0 + [#4233](https://github.com/open-telemetry/opentelemetry-cpp/pull/4233) + +Important changes: + +* [API] fix the trace state regex to comply with the w3c trace context level 2 (#4194) + [#4194](https://github.com/open-telemetry/opentelemetry-cpp/pull/4194) + + * [API] Fix `TraceState::IsValidKey()` to comply with the W3C Trace Context + Level 2, where keys containing `@` and keys with more than 241 characters + before `@` or more than 14 characters after `@` are now accepted. + Only the total 256-character key length limit is enforced. + **Note**: this is a correctness fix to an inline API header; the observable + behavior of `IsValidKey`, `IsValidKeyRegEx`, and `IsValidKeyNonRegEx` + changes (see `docs/abi-policy.md`). + +* [API+SDK] apply filtering EmitLogRecord (#4079) + [#4079](https://github.com/open-telemetry/opentelemetry-cpp/pull/4079) + + * [API] (ABI v2) `Logger::EmitLogRecord(...)` templates now apply the + `Enabled` filter chain when a `Severity` is in args. v1 behavior is + unchanged. + + * [API/SDK] (ABI v2) Add `Logger::CreateLogRecord` virtual taking + `const nostd::variant &` + for explicit-context record creation. `Logger::EmitLogRecord(args...)` + also detects a `Context`, `SpanContext` + or `TraceId` + `SpanId` [+ `TraceFlags`] in args and routes filtering. + + * [SDK] Add `LogRecordProcessor::HasEnabledFilter()` so the SDK Logger can + include processor-level filtering in its extended-enabled cache. Defaults + to `true`. Built-in `SimpleLogRecordProcessor` and + `BatchLogRecordProcessor` override to `false` since they use the default + Enabled. + + * [API/SDK] Replace `Context`-only signatures on + `LogRecordProcessor::Enabled`, + `LogRecordProcessor::EnabledImplementation`, + `Logger::EnabledImplementation` (v2), and `Logger::CreateLogRecord` (v2) + with `nostd::variant`. + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index e44aa1544f..3380189ff4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.20.0") endif() set(OPENTELEMETRY_VERSION_NUMBER "1.28.0") -set(OPENTELEMETRY_VERSION_SUFFIX "-dev") +set(OPENTELEMETRY_VERSION_SUFFIX "") set(OPENTELEMETRY_VERSION "${OPENTELEMETRY_VERSION_NUMBER}${OPENTELEMETRY_VERSION_SUFFIX}") diff --git a/MODULE.bazel b/MODULE.bazel index f02a516052..c8d409be41 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -3,7 +3,7 @@ module( name = "opentelemetry-cpp", - version = "1.28.0-dev", + version = "1.28.0", compatibility_level = 0, repo_name = "io_opentelemetry_cpp", ) diff --git a/api/include/opentelemetry/version.h b/api/include/opentelemetry/version.h index 574d1df49f..48908afe88 100644 --- a/api/include/opentelemetry/version.h +++ b/api/include/opentelemetry/version.h @@ -12,7 +12,7 @@ #endif // NOLINTBEGIN(cppcoreguidelines-macro-to-enum) -#define OPENTELEMETRY_VERSION "1.28.0-dev" +#define OPENTELEMETRY_VERSION "1.28.0" #define OPENTELEMETRY_VERSION_MAJOR 1 #define OPENTELEMETRY_VERSION_MINOR 28 #define OPENTELEMETRY_VERSION_PATCH 0 diff --git a/sdk/include/opentelemetry/sdk/version/version.h b/sdk/include/opentelemetry/sdk/version/version.h index 487797e603..5924fa4b74 100644 --- a/sdk/include/opentelemetry/sdk/version/version.h +++ b/sdk/include/opentelemetry/sdk/version/version.h @@ -3,7 +3,7 @@ #pragma once -#define OPENTELEMETRY_SDK_VERSION "1.28.0-dev" +#define OPENTELEMETRY_SDK_VERSION "1.28.0" #include "opentelemetry/version.h" diff --git a/sdk/src/version/version.cc b/sdk/src/version/version.cc index f7c6730f4f..7504c6c2f2 100644 --- a/sdk/src/version/version.cc +++ b/sdk/src/version/version.cc @@ -14,16 +14,16 @@ namespace version const int major_version = 1; const int minor_version = 28; const int patch_version = 0; -const char *pre_release = "dev"; +const char *pre_release = ""; const char *build_metadata = "none"; const char *short_version = "1.28.0"; -const char *full_version = "1.28.0-dev"; +const char *full_version = "1.28.0"; /** * Release date. * For published releases: YYYY-MM-DD * For -dev releases: empty string */ -const char *build_date = ""; +const char *build_date = "2026-07-16"; } // namespace version } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/tbump.toml b/tbump.toml index dc0218ae06..e1a3bdd310 100644 --- a/tbump.toml +++ b/tbump.toml @@ -21,7 +21,7 @@ github_url = "https://github.com/open-telemetry/opentelemetry-cpp" [version] -current = "1.28.0-dev" +current = "1.28.0" # Example of a semver regexp. # Make sure this matches current_version before From 52eb46b7609d1a7c0871414e694e83dcb7ca9d93 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Thu, 16 Jul 2026 23:42:58 +0200 Subject: [PATCH 45/49] [RELEASE] Bump main branch to 1.29.0-dev (#4259) --- CHANGELOG.md | 3 +++ CMakeLists.txt | 4 ++-- MODULE.bazel | 2 +- api/include/opentelemetry/version.h | 4 ++-- sdk/include/opentelemetry/sdk/version/version.h | 2 +- sdk/src/version/version.cc | 10 +++++----- tbump.toml | 2 +- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27f2899caa..0d521375a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [RELEASE] Bump main branch to 1.29.0-dev + [#4259](https://github.com/open-telemetry/opentelemetry-cpp/pull/4259) + ## [1.28.0] 2026-07-16 * [RELEASE] Bump main branch to 1.28.0-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index 3380189ff4..c6f255c49c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.20.0") cmake_policy(SET CMP0117 NEW) endif() -set(OPENTELEMETRY_VERSION_NUMBER "1.28.0") -set(OPENTELEMETRY_VERSION_SUFFIX "") +set(OPENTELEMETRY_VERSION_NUMBER "1.29.0") +set(OPENTELEMETRY_VERSION_SUFFIX "-dev") set(OPENTELEMETRY_VERSION "${OPENTELEMETRY_VERSION_NUMBER}${OPENTELEMETRY_VERSION_SUFFIX}") diff --git a/MODULE.bazel b/MODULE.bazel index c8d409be41..e46bf14c66 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -3,7 +3,7 @@ module( name = "opentelemetry-cpp", - version = "1.28.0", + version = "1.29.0-dev", compatibility_level = 0, repo_name = "io_opentelemetry_cpp", ) diff --git a/api/include/opentelemetry/version.h b/api/include/opentelemetry/version.h index 48908afe88..6e05c89eb0 100644 --- a/api/include/opentelemetry/version.h +++ b/api/include/opentelemetry/version.h @@ -12,9 +12,9 @@ #endif // NOLINTBEGIN(cppcoreguidelines-macro-to-enum) -#define OPENTELEMETRY_VERSION "1.28.0" +#define OPENTELEMETRY_VERSION "1.29.0-dev" #define OPENTELEMETRY_VERSION_MAJOR 1 -#define OPENTELEMETRY_VERSION_MINOR 28 +#define OPENTELEMETRY_VERSION_MINOR 29 #define OPENTELEMETRY_VERSION_PATCH 0 // NOLINTEND(cppcoreguidelines-macro-to-enum) diff --git a/sdk/include/opentelemetry/sdk/version/version.h b/sdk/include/opentelemetry/sdk/version/version.h index 5924fa4b74..78074a50f5 100644 --- a/sdk/include/opentelemetry/sdk/version/version.h +++ b/sdk/include/opentelemetry/sdk/version/version.h @@ -3,7 +3,7 @@ #pragma once -#define OPENTELEMETRY_SDK_VERSION "1.28.0" +#define OPENTELEMETRY_SDK_VERSION "1.29.0-dev" #include "opentelemetry/version.h" diff --git a/sdk/src/version/version.cc b/sdk/src/version/version.cc index 7504c6c2f2..5230bbdd6b 100644 --- a/sdk/src/version/version.cc +++ b/sdk/src/version/version.cc @@ -12,18 +12,18 @@ namespace sdk namespace version { const int major_version = 1; -const int minor_version = 28; +const int minor_version = 29; const int patch_version = 0; -const char *pre_release = ""; +const char *pre_release = "dev"; const char *build_metadata = "none"; -const char *short_version = "1.28.0"; -const char *full_version = "1.28.0"; +const char *short_version = "1.29.0"; +const char *full_version = "1.29.0-dev"; /** * Release date. * For published releases: YYYY-MM-DD * For -dev releases: empty string */ -const char *build_date = "2026-07-16"; +const char *build_date = ""; } // namespace version } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/tbump.toml b/tbump.toml index e1a3bdd310..3992a3a56a 100644 --- a/tbump.toml +++ b/tbump.toml @@ -21,7 +21,7 @@ github_url = "https://github.com/open-telemetry/opentelemetry-cpp" [version] -current = "1.28.0" +current = "1.29.0-dev" # Example of a semver regexp. # Make sure this matches current_version before From 1025785195002f73f0b048e6efde459b493a6319 Mon Sep 17 00:00:00 2001 From: Tom Tan Date: Thu, 16 Jul 2026 17:20:51 -0700 Subject: [PATCH 46/49] docs: update supported development platforms (#4260) --- CHANGELOG.md | 3 +++ README.md | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d521375a6..468fbd8f7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* docs: update supported development platforms + [#4260](https://github.com/open-telemetry/opentelemetry-cpp/pull/4260) + * [RELEASE] Bump main branch to 1.29.0-dev [#4259](https://github.com/open-telemetry/opentelemetry-cpp/pull/4259) diff --git a/README.md b/README.md index 3890fad172..a5ad3a79cb 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ C++ standards: * ISO/IEC 14882:2014 (C++14) * ISO/IEC 14882:2017 (C++17) * ISO/IEC 14882:2020 (C++20) +* ISO/IEC 14882:2024 (C++23) Any exceptions to this are noted in the individual `README.md` files. @@ -37,16 +38,16 @@ of the current project. ## Supported Development Platforms - Our CI pipeline builds and tests on following `x86-64` platforms: +Our CI pipeline builds and tests on the following platforms: -| Platform | Build type | -|---------------------------------------------------------------------|---------------| -| ubuntu-22.04 (GCC 10, GCC 12, Clang 14) | CMake, Bazel | -| ubuntu-20.04 (GCC 9.4.0 - default compiler) | CMake, Bazel | -| ubuntu-20.04 (GCC 9.4.0 with -std=c++14/17/20 flags) | CMake, Bazel | -| macOS 12.7 (Xcode 14.2) | Bazel | -| Windows Server 2019 (Visual Studio Enterprise 2019) | CMake, Bazel | -| Windows Server 2022 (Visual Studio Enterprise 2022) | CMake | +| Platform | Architecture | Build type | +|---------------------|--------------|--------------| +| Ubuntu 22.04 | x86-64 | CMake | +| Ubuntu 24.04 | x86-64 | CMake, Bazel | +| macOS 14 | arm64 | CMake | +| macOS 15 | arm64 | Bazel | +| Windows Server 2022 | x86-64 | CMake, Bazel | +| Windows Server 2025 | x86-64 | CMake | In general, the code shipped from this repository should build on all platforms having C++ compiler with [supported C++ standards](#supported-c-versions). From 4f25e2924d4a237bdd6c3e1f77baedf5ab893ce6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:48:52 +0200 Subject: [PATCH 47/49] Bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.1 (#4262) Bumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a) --- updated-dependencies: - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index dd42cd542e..319fdcfea1 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -47,6 +47,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 with: sarif_file: results.sarif From 94d9904f91c4e37f132df0083c00afbf64ec1910 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 14:15:11 +0200 Subject: [PATCH 48/49] Bump github/codeql-action/analyze from 4.37.0 to 4.37.1 (#4263) * Bump github/codeql-action/analyze from 4.37.0 to 4.37.1 Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Apply suggestion from @marcalff --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marc Alff --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c7bae0d749..739c723e8f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: run: | sudo -E ./ci/install_thirdparty.sh --install-dir /usr/local --tags-file third_party_release --packages "ryml" - name: Initialize CodeQL - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 with: languages: cpp config: | @@ -71,4 +71,4 @@ jobs: "${GITHUB_WORKSPACE}" cmake --build . --parallel - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 From ad03be695669f10daff53f49fb3d298417572f0d Mon Sep 17 00:00:00 2001 From: proost Date: Sat, 18 Jul 2026 14:19:14 +0900 Subject: [PATCH 49/49] [API] reduce allocation in no span case (#4254) --- CHANGELOG.md | 5 +++ api/include/opentelemetry/trace/context.h | 4 +- .../trace/propagation/b3_propagator.h | 4 +- .../trace/propagation/http_trace_context.h | 2 +- .../opentelemetry/trace/propagation/jaeger.h | 2 +- api/test/trace/context_test.cc | 42 +++++++++++++++++++ opentracing-shim/src/tracer_shim.cc | 2 +- .../sdk/metrics/exemplar/reservoir_cell.h | 11 ++--- sdk/src/trace/tracer.cc | 2 +- 9 files changed, 58 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468fbd8f7e..ad43a3e6e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ Increment the: * [RELEASE] Bump main branch to 1.29.0-dev [#4259](https://github.com/open-telemetry/opentelemetry-cpp/pull/4259) +* [API] Add `trace::GetSpanContext()` to read the active span's `SpanContext` + from a `Context` without the `DefaultSpan` allocation that + `GetSpan(context)->GetContext()` incurs, and use it at existing call sites. + [#4254](https://github.com/open-telemetry/opentelemetry-cpp/pull/4254) + ## [1.28.0] 2026-07-16 * [RELEASE] Bump main branch to 1.28.0-dev diff --git a/api/include/opentelemetry/trace/context.h b/api/include/opentelemetry/trace/context.h index d68f19a7db..90ef5adc41 100644 --- a/api/include/opentelemetry/trace/context.h +++ b/api/include/opentelemetry/trace/context.h @@ -38,14 +38,14 @@ inline SpanContext GetSpanContext(const context::Context &context) noexcept if (const nostd::shared_ptr *maybe_span = nostd::get_if>(&context_value)) { - return (*maybe_span)->GetContext(); + return *maybe_span ? (*maybe_span)->GetContext() : SpanContext::GetInvalid(); } // 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 *maybe_span_context = nostd::get_if>(&context_value)) { - return **maybe_span_context; + return *maybe_span_context ? **maybe_span_context : SpanContext::GetInvalid(); } return SpanContext::GetInvalid(); } diff --git a/api/include/opentelemetry/trace/propagation/b3_propagator.h b/api/include/opentelemetry/trace/propagation/b3_propagator.h index a13d2f0767..1202cbe7de 100644 --- a/api/include/opentelemetry/trace/propagation/b3_propagator.h +++ b/api/include/opentelemetry/trace/propagation/b3_propagator.h @@ -139,7 +139,7 @@ class B3Propagator : public B3PropagatorExtractor void Inject(context::propagation::TextMapCarrier &carrier, const context::Context &context) noexcept override { - SpanContext span_context = trace::GetSpan(context)->GetContext(); + SpanContext span_context = trace::GetSpanContext(context); if (!span_context.IsValid()) { return; @@ -171,7 +171,7 @@ class B3PropagatorMultiHeader : public B3PropagatorExtractor void Inject(context::propagation::TextMapCarrier &carrier, const context::Context &context) noexcept override { - SpanContext span_context = GetSpan(context)->GetContext(); + SpanContext span_context = GetSpanContext(context); if (!span_context.IsValid()) { return; diff --git a/api/include/opentelemetry/trace/propagation/http_trace_context.h b/api/include/opentelemetry/trace/propagation/http_trace_context.h index f8f349d33e..65c6e511f1 100644 --- a/api/include/opentelemetry/trace/propagation/http_trace_context.h +++ b/api/include/opentelemetry/trace/propagation/http_trace_context.h @@ -41,7 +41,7 @@ class HttpTraceContext : public context::propagation::TextMapPropagator void Inject(context::propagation::TextMapCarrier &carrier, const context::Context &context) noexcept override { - SpanContext span_context = trace::GetSpan(context)->GetContext(); + SpanContext span_context = trace::GetSpanContext(context); if (!span_context.IsValid()) { return; diff --git a/api/include/opentelemetry/trace/propagation/jaeger.h b/api/include/opentelemetry/trace/propagation/jaeger.h index ac843f97b6..a4ff4ebc9d 100644 --- a/api/include/opentelemetry/trace/propagation/jaeger.h +++ b/api/include/opentelemetry/trace/propagation/jaeger.h @@ -23,7 +23,7 @@ class JaegerPropagator : public context::propagation::TextMapPropagator void Inject(context::propagation::TextMapCarrier &carrier, const context::Context &context) noexcept override { - SpanContext span_context = trace::GetSpan(context)->GetContext(); + SpanContext span_context = trace::GetSpanContext(context); if (!span_context.IsValid()) { return; diff --git a/api/test/trace/context_test.cc b/api/test/trace/context_test.cc index 1081fb0e7a..b72f41abf1 100644 --- a/api/test/trace/context_test.cc +++ b/api/test/trace/context_test.cc @@ -69,6 +69,48 @@ TEST(TraceContextTest, GetSpan) } } +TEST(TraceContextTest, GetSpanContext) +{ + { + context_api::Context context; + EXPECT_FALSE(trace_api::GetSpanContext(context).IsValid()); + } + + { + context_api::Context context; + auto input_span = MakeValidSpan(); + auto context_with_span = trace_api::SetSpan(context, input_span); + auto span_context = trace_api::GetSpanContext(context_with_span); + EXPECT_TRUE(span_context.IsValid()); + EXPECT_EQ(span_context, input_span->GetContext()); + } + + { + context_api::Context context; + auto context_with_null_span = + context.SetValue(trace_api::kSpanKey, nostd::shared_ptr{}); + EXPECT_FALSE(trace_api::GetSpanContext(context_with_null_span).IsValid()); + } + + { + context_api::Context context; + const auto input_span_context = MakeValidSpan()->GetContext(); + auto context_with_span_context = context.SetValue( + trace_api::kSpanKey, + nostd::shared_ptr{new trace_api::SpanContext{input_span_context}}); + auto span_context = trace_api::GetSpanContext(context_with_span_context); + EXPECT_TRUE(span_context.IsValid()); + EXPECT_EQ(span_context, input_span_context); + } + + { + context_api::Context context; + auto context_with_null_span_context = + context.SetValue(trace_api::kSpanKey, nostd::shared_ptr{}); + EXPECT_FALSE(trace_api::GetSpanContext(context_with_null_span_context).IsValid()); + } +} + TEST(TraceContextTest, SetSpan) { context_api::Context context; diff --git a/opentracing-shim/src/tracer_shim.cc b/opentracing-shim/src/tracer_shim.cc index bfc6055971..6cd5342a99 100644 --- a/opentracing-shim/src/tracer_shim.cc +++ b/opentracing-shim/src/tracer_shim.cc @@ -183,7 +183,7 @@ opentracing::expected> TracerShim::ext CarrierReaderShim carrier{reader}; auto current_context = opentelemetry::context::RuntimeContext::GetCurrent(); auto context = propagator->Extract(carrier, current_context); - auto span_context = opentelemetry::trace::GetSpan(context)->GetContext(); + auto span_context = opentelemetry::trace::GetSpanContext(context); auto baggage = opentelemetry::baggage::GetBaggage(context); // The operation MUST return a `SpanContext` Shim instance with the extracted values if any of diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h index 91e66264df..be22ba9e61 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h @@ -18,7 +18,6 @@ # include "opentelemetry/sdk/metrics/data/metric_data.h" # include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/trace/context.h" -# include "opentelemetry/trace/span.h" # include "opentelemetry/trace/span_context.h" # include "opentelemetry/version.h" @@ -135,14 +134,10 @@ class ReservoirCell { attributes_ = attributes; record_time_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); - auto span = opentelemetry::trace::GetSpan(context); - if (span) + const auto current_ctx = opentelemetry::trace::GetSpanContext(context); + if (current_ctx.IsValid()) { - auto current_ctx = span->GetContext(); - if (current_ctx.IsValid()) - { - context_.reset(new opentelemetry::trace::SpanContext{current_ctx}); - } + context_.reset(new opentelemetry::trace::SpanContext{current_ctx}); } } diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index b0f0e593e8..96b2d81fc9 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -75,7 +75,7 @@ nostd::shared_ptr Tracer::StartSpan( else if (const context::Context *context = nostd::get_if(&options.parent)) { // fetch span context from parent span stored in the context - auto parent_span_context = opentelemetry::trace::GetSpan(*context)->GetContext(); + auto parent_span_context = opentelemetry::trace::GetSpanContext(*context); if (parent_span_context.IsValid()) { parent_context = parent_span_context;