diff --git a/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h b/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h index efad19b098..59097185ee 100644 --- a/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h +++ b/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h @@ -6,8 +6,8 @@ #include #include "opentelemetry/common/timestamp.h" -#include "opentelemetry/sdk/common/attribute_utils.h" #include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/version.h" @@ -16,7 +16,7 @@ namespace sdk { namespace metrics { -using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +using MetricAttributes = opentelemetry::sdk::metrics::FilteredOrderedAttributeMap; /** * A sample input measurement. * diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h index f3bc904e26..a5fd8966e3 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h @@ -5,7 +5,7 @@ #include -#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -23,7 +23,7 @@ class OrderedAttributeMap; namespace metrics { -using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +using MetricAttributes = opentelemetry::sdk::metrics::FilteredOrderedAttributeMap; /** * Exemplar filters are used to pre-filter measurements before attempting to store them in a diff --git a/sdk/include/opentelemetry/sdk/metrics/instruments.h b/sdk/include/opentelemetry/sdk/metrics/instruments.h index 84244128a4..d7c6870945 100644 --- a/sdk/include/opentelemetry/sdk/metrics/instruments.h +++ b/sdk/include/opentelemetry/sdk/metrics/instruments.h @@ -6,7 +6,7 @@ #include #include -#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -63,7 +63,7 @@ struct InstrumentDescriptor InstrumentValueType value_type_; }; -using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +using MetricAttributes = opentelemetry::sdk::metrics::FilteredOrderedAttributeMap; using AggregationTemporalitySelector = std::function; /*class InstrumentSelector { diff --git a/sdk/include/opentelemetry/sdk/metrics/observer_result.h b/sdk/include/opentelemetry/sdk/metrics/observer_result.h index 38c5843633..5be40329c1 100644 --- a/sdk/include/opentelemetry/sdk/metrics/observer_result.h +++ b/sdk/include/opentelemetry/sdk/metrics/observer_result.h @@ -30,15 +30,7 @@ class ObserverResultT final : public opentelemetry::metrics::ObserverResultT void Observe(T value, const opentelemetry::common::KeyValueIterable &attributes) noexcept override { - if (attributes_processor_) - { - auto attr = attributes_processor_->process(attributes); - data_.insert({attr, value}); - } - else - { - data_.insert({MetricAttributes{attributes}, value}); - } + data_.insert({MetricAttributes{attributes, attributes_processor_}, value}); } const std::unordered_map &GetMeasurements() diff --git a/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h b/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h index 8dd820fd95..adc3f415af 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h @@ -69,6 +69,7 @@ class AttributesHashMap * value and store in the hash */ Aggregation *GetOrSetDefault(const opentelemetry::common::KeyValueIterable &attributes, + const AttributesProcessor *attributes_processor, std::function()> aggregation_callback, size_t hash) { @@ -83,7 +84,7 @@ class AttributesHashMap return GetOrSetOveflowAttributes(aggregation_callback); } - MetricAttributes attr{attributes}; + MetricAttributes attr{attributes, attributes_processor}; hash_map_[hash] = {attr, aggregation_callback()}; return hash_map_[hash].second.get(); @@ -133,6 +134,7 @@ class AttributesHashMap * Set the value for given key, overwriting the value if already present */ void Set(const opentelemetry::common::KeyValueIterable &attributes, + const AttributesProcessor *attributes_processor, std::unique_ptr aggr, size_t hash) { @@ -149,7 +151,7 @@ class AttributesHashMap } else { - MetricAttributes attr{attributes}; + MetricAttributes attr{attributes, attributes_processor}; hash_map_[hash] = {attr, std::move(aggr)}; } } @@ -169,8 +171,7 @@ class AttributesHashMap } else { - MetricAttributes attr{attributes}; - hash_map_[hash] = {attr, std::move(aggr)}; + hash_map_[hash] = {attributes, std::move(aggr)}; } } diff --git a/sdk/include/opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h b/sdk/include/opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h new file mode 100644 index 0000000000..f9880b5073 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include "opentelemetry/sdk/common/attributemap_hash.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class AttributesProcessor; +class FilteredOrderedAttributeMap : public opentelemetry::sdk::common::OrderedAttributeMap +{ +public: + FilteredOrderedAttributeMap() = default; + FilteredOrderedAttributeMap( + std::initializer_list> + attributes) + : OrderedAttributeMap(attributes) + {} + FilteredOrderedAttributeMap(const opentelemetry::common::KeyValueIterable &attributes) + : FilteredOrderedAttributeMap(attributes, nullptr) + {} + FilteredOrderedAttributeMap(const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::sdk::metrics::AttributesProcessor *processor); + FilteredOrderedAttributeMap( + std::initializer_list> + attributes, + const opentelemetry::sdk::metrics::AttributesProcessor *processor); +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE 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 cf249c39f2..6168415ff1 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h @@ -99,7 +99,8 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage }); std::lock_guard guard(attribute_hashmap_lock_); - attributes_hashmap_->GetOrSetDefault(attributes, create_default_aggregation_, hash) + attributes_hashmap_ + ->GetOrSetDefault(attributes, attributes_processor_, create_default_aggregation_, hash) ->Aggregate(value); } @@ -148,7 +149,8 @@ class SyncMetricStorage : public MetricStorage, public SyncWritableMetricStorage } }); std::lock_guard guard(attribute_hashmap_lock_); - attributes_hashmap_->GetOrSetDefault(attributes, create_default_aggregation_, hash) + attributes_hashmap_ + ->GetOrSetDefault(attributes, attributes_processor_, create_default_aggregation_, hash) ->Aggregate(value); } diff --git a/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h b/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h index 91e4b268cf..a71abb7a99 100644 --- a/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h +++ b/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h @@ -5,11 +5,10 @@ #include #include - #include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/sdk/common/attribute_utils.h" #include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -17,7 +16,7 @@ namespace sdk { namespace metrics { -using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +using MetricAttributes = opentelemetry::sdk::metrics::FilteredOrderedAttributeMap; /** * The AttributesProcessor is responsible for customizing which diff --git a/sdk/src/metrics/CMakeLists.txt b/sdk/src/metrics/CMakeLists.txt index a0c9aa6771..134b227893 100644 --- a/sdk/src/metrics/CMakeLists.txt +++ b/sdk/src/metrics/CMakeLists.txt @@ -13,6 +13,7 @@ add_library( instrument_metadata_validator.cc export/periodic_exporting_metric_reader.cc export/periodic_exporting_metric_reader_factory.cc + state/filtered_ordered_attribute_map.cc state/metric_collector.cc state/observable_registry.cc state/sync_metric_storage.cc diff --git a/sdk/src/metrics/state/filtered_ordered_attribute_map.cc b/sdk/src/metrics/state/filtered_ordered_attribute_map.cc new file mode 100644 index 0000000000..1af0fe0d92 --- /dev/null +++ b/sdk/src/metrics/state/filtered_ordered_attribute_map.cc @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h" +#include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +FilteredOrderedAttributeMap::FilteredOrderedAttributeMap( + const opentelemetry::common::KeyValueIterable &attributes, + const AttributesProcessor *processor) + : OrderedAttributeMap() +{ + attributes.ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + if (processor && processor->isPresent(key)) + { + SetAttribute(key, value); + } + return true; + }); +} + +FilteredOrderedAttributeMap::FilteredOrderedAttributeMap( + std::initializer_list> + attributes, + const AttributesProcessor *processor) + : OrderedAttributeMap() +{ + for (auto &kv : attributes) + { + if (processor && processor->isPresent(kv.first)) + { + SetAttribute(kv.first, kv.second); + } + } +} +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/test/metrics/cardinality_limit_test.cc b/sdk/test/metrics/cardinality_limit_test.cc index d290f889b5..621241557b 100644 --- a/sdk/test/metrics/cardinality_limit_test.cc +++ b/sdk/test/metrics/cardinality_limit_test.cc @@ -26,8 +26,8 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) long record_value = 100; for (auto i = 0; i < 10; i++) { - OrderedAttributeMap attributes = {{"key", std::to_string(i)}}; - auto hash = opentelemetry::sdk::common::GetHashForAttributeMap(attributes); + FilteredOrderedAttributeMap attributes = {{"key", std::to_string(i)}}; + auto hash = opentelemetry::sdk::common::GetHashForAttributeMap(attributes); static_cast( hash_map.GetOrSetDefault(attributes, aggregation_callback, hash)) ->Aggregate(record_value); @@ -37,8 +37,8 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) // overflowmetric point. for (auto i = 10; i < 15; i++) { - OrderedAttributeMap attributes = {{"key", std::to_string(i)}}; - auto hash = opentelemetry::sdk::common::GetHashForAttributeMap(attributes); + FilteredOrderedAttributeMap attributes = {{"key", std::to_string(i)}}; + auto hash = opentelemetry::sdk::common::GetHashForAttributeMap(attributes); static_cast( hash_map.GetOrSetDefault(attributes, aggregation_callback, hash)) ->Aggregate(record_value); @@ -46,7 +46,7 @@ TEST(CardinalityLimit, AttributesHashMapBasicTests) EXPECT_EQ(hash_map.Size(), 10); // only one more metric point should be added as overflow. // get the overflow metric point auto agg = hash_map.GetOrSetDefault( - OrderedAttributeMap({{kAttributesLimitOverflowKey, kAttributesLimitOverflowValue}}), + FilteredOrderedAttributeMap({{kAttributesLimitOverflowKey, kAttributesLimitOverflowValue}}), aggregation_callback, kOverflowAttributesHash); EXPECT_NE(agg, nullptr); auto sum_agg = static_cast(agg); diff --git a/sdk/test/metrics/sum_aggregation_test.cc b/sdk/test/metrics/sum_aggregation_test.cc index 403bf34b22..33a111974e 100644 --- a/sdk/test/metrics/sum_aggregation_test.cc +++ b/sdk/test/metrics/sum_aggregation_test.cc @@ -72,6 +72,60 @@ TEST(HistogramToSum, Double) ASSERT_EQ(1000275.0, opentelemetry::nostd::get(actual.value_)); } +TEST(HistogramToSumFilterAttributes, Double) +{ + MeterProvider mp; + auto m = mp.GetMeter("meter1", "version1", "schema1"); + std::string instrument_unit = "ms"; + std::string instrument_name = "historgram1"; + std::string instrument_desc = "histogram metrics"; + + std::unordered_map allowedattr; + allowedattr["attr1"] = true; + std::unique_ptr attrproc{ + new opentelemetry::sdk::metrics::FilteringAttributesProcessor(allowedattr)}; + + std::shared_ptr dummy_aggregation_config{ + new opentelemetry::sdk::metrics::AggregationConfig}; + std::unique_ptr exporter(new MockMetricExporter()); + std::shared_ptr reader{new MockMetricReader(std::move(exporter))}; + mp.AddMetricReader(reader); + + std::unique_ptr view{new View("view1", "view1_description", instrument_unit, + AggregationType::kSum, dummy_aggregation_config, + std::move(attrproc))}; + std::unique_ptr instrument_selector{ + new InstrumentSelector(InstrumentType::kHistogram, instrument_name, instrument_unit)}; + std::unique_ptr meter_selector{new MeterSelector("meter1", "version1", "schema1")}; + mp.AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view)); + + auto h = m->CreateDoubleHistogram(instrument_name, instrument_desc, instrument_unit); + std::unordered_map attr1 = {{"attr1", "val1"}, {"attr2", "val2"}}; + std::unordered_map attr2 = {{"attr1", "val1"}, {"attr2", "val2"}}; + h->Record(5, attr1, opentelemetry::context::Context{}); + h->Record(10, attr2, opentelemetry::context::Context{}); + + reader->Collect([&](ResourceMetrics &rm) { + for (const ScopeMetrics &smd : rm.scope_metric_data_) + { + for (const MetricData &md : smd.metric_data_) + { + EXPECT_EQ(1, md.point_data_attr_.size()); + if (md.point_data_attr_.size() == 1) + { + EXPECT_EQ(15.0, opentelemetry::nostd::get(opentelemetry::nostd::get( + md.point_data_attr_[0].point_data) + .value_)); + EXPECT_EQ(1, md.point_data_attr_[0].attributes.size()); + EXPECT_NE(md.point_data_attr_[0].attributes.end(), + md.point_data_attr_[0].attributes.find("attr1")); + } + } + } + return true; + }); +} + TEST(CounterToSum, Double) { MeterProvider mp; @@ -121,6 +175,60 @@ TEST(CounterToSum, Double) ASSERT_EQ(1000275.0, opentelemetry::nostd::get(actual.value_)); } +TEST(CounterToSumFilterAttributes, Double) +{ + MeterProvider mp; + auto m = mp.GetMeter("meter1", "version1", "schema1"); + std::string instrument_unit = "ms"; + std::string instrument_name = "counter1"; + std::string instrument_desc = "counter metrics"; + + std::unordered_map allowedattr; + allowedattr["attr1"] = true; + std::unique_ptr attrproc{ + new opentelemetry::sdk::metrics::FilteringAttributesProcessor(allowedattr)}; + + std::shared_ptr dummy_aggregation_config{ + new opentelemetry::sdk::metrics::AggregationConfig}; + std::unique_ptr exporter(new MockMetricExporter()); + std::shared_ptr reader{new MockMetricReader(std::move(exporter))}; + mp.AddMetricReader(reader); + + std::unique_ptr view{new View("view1", "view1_description", instrument_unit, + AggregationType::kSum, dummy_aggregation_config, + std::move(attrproc))}; + std::unique_ptr instrument_selector{ + new InstrumentSelector(InstrumentType::kCounter, instrument_name, instrument_unit)}; + std::unique_ptr meter_selector{new MeterSelector("meter1", "version1", "schema1")}; + mp.AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view)); + + auto c = m->CreateDoubleCounter(instrument_name, instrument_desc, instrument_unit); + std::unordered_map attr1 = {{"attr1", "val1"}, {"attr2", "val2"}}; + std::unordered_map attr2 = {{"attr1", "val1"}, {"attr2", "val2"}}; + c->Add(5, attr1, opentelemetry::context::Context{}); + c->Add(10, attr2, opentelemetry::context::Context{}); + + reader->Collect([&](ResourceMetrics &rm) { + for (const ScopeMetrics &smd : rm.scope_metric_data_) + { + for (const MetricData &md : smd.metric_data_) + { + EXPECT_EQ(1, md.point_data_attr_.size()); + if (md.point_data_attr_.size() == 1) + { + EXPECT_EQ(15.0, opentelemetry::nostd::get(opentelemetry::nostd::get( + md.point_data_attr_[0].point_data) + .value_)); + EXPECT_EQ(1, md.point_data_attr_[0].attributes.size()); + EXPECT_NE(md.point_data_attr_[0].attributes.end(), + md.point_data_attr_[0].attributes.find("attr1")); + } + } + } + return true; + }); +} + class UpDownCounterToSumFixture : public ::testing::TestWithParam {};