diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f280eb22..304d7649a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ Increment the: * [SDK] Fix instrumentation scope attributes evaluated in equal method [#3214](https://github.com/open-telemetry/opentelemetry-cpp/pull/3214) +* [SDK] Implement spec: MetricFilter + [#3235](https://github.com/open-telemetry/opentelemetry-cpp/pull/3235) + * [EXPORTER] Fix scope attributes missing from otlp traces metrics [#3185](https://github.com/open-telemetry/opentelemetry-cpp/pull/3185) diff --git a/sdk/include/opentelemetry/sdk/metrics/export/metric_filter.h b/sdk/include/opentelemetry/sdk/metrics/export/metric_filter.h new file mode 100644 index 0000000000..3aea0b0a1d --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/export/metric_filter.h @@ -0,0 +1,111 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/instruments.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +/** + * MetricFilter defines the interface which enables the MetricReader’s + * registered MetricProducers or the SDK’s MetricProducer to filter aggregated + * data points (Metric Points) inside its Produce operation. The filtering is + * done at the MetricProducer for performance reasons. + * + * The MetricFilter allows filtering an entire metric stream - dropping or + * allowing all its attribute sets - by its TestMetric operation, which accepts + * the metric stream information (scope, name, kind and unit) and returns an + * enumeration: kAccept, kDrop or kAcceptPartial. If the latter returned, the + * TestAttributes operation is to be called per attribute set of that metric + * stream, returning an enumeration determining if the data point for that + * (metric stream, attributes) pair is to be allowed in the result of the + * MetricProducer Produce operation. + */ +class MetricFilter +{ +public: + enum class MetricFilterResult + { + kAccept, + kDrop, + kAcceptPartial, + }; + + enum class AttributesFilterResult + { + kAccept, + kDrop, + }; + + using TestMetricFn = std::function; + + using TestAttributesFn = std::function; + + // static + static std::unique_ptr Create(TestMetricFn test_metric_fn, + TestAttributesFn test_attributes_fn) + { + return std::make_unique(test_metric_fn, test_attributes_fn); + } + + MetricFilter(TestMetricFn test_metric_fn, TestAttributesFn test_attributes_fn) + : test_metric_fn_(test_metric_fn), test_attributes_fn_(test_attributes_fn) {}; + + /** + * TestMetric is called once for every metric stream, in each MetricProducer + * Produce operation. + */ + MetricFilterResult TestMetric( + const opentelemetry::sdk::instrumentationscope::InstrumentationScope &scope, + opentelemetry::nostd::string_view name, + const InstrumentType &type, + opentelemetry::nostd::string_view unit) + { + return test_metric_fn_(scope, name, type, unit); + } + + /** + * TestAttributes determines for a given metric stream and attribute set if + * it should be allowed or filtered out. + * + * This operation should only be called if TestMetric operation returned + * kAcceptPartial for the given metric stream arguments. + */ + AttributesFilterResult TestAttributes( + const opentelemetry::sdk::instrumentationscope::InstrumentationScope &scope, + opentelemetry::nostd::string_view name, + const InstrumentType &type, + opentelemetry::nostd::string_view unit, + const PointAttributes &attributes) + { + return test_attributes_fn_(scope, name, type, unit, attributes); + } + +private: + TestMetricFn test_metric_fn_; + TestAttributesFn test_attributes_fn_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h b/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h index f6aa4ca2a4..2695728fad 100644 --- a/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h +++ b/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h @@ -3,12 +3,14 @@ #pragma once +#include #include #include #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -80,7 +82,8 @@ struct ResourceMetrics class MetricProducer { public: - MetricProducer() = default; + MetricProducer(std::unique_ptr metric_filter = nullptr) + : metric_filter_(std::move(metric_filter)) {}; virtual ~MetricProducer() = default; MetricProducer(const MetricProducer &) = delete; @@ -107,6 +110,8 @@ class MetricProducer * partial failure. */ virtual Result Produce() noexcept = 0; + + std::unique_ptr metric_filter_; }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_context.h b/sdk/include/opentelemetry/sdk/metrics/meter_context.h index 8a19cd7466..c071118957 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_context.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_context.h @@ -13,6 +13,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include "opentelemetry/sdk/metrics/state/metric_collector.h" #include "opentelemetry/sdk/metrics/view/instrument_selector.h" @@ -96,14 +97,20 @@ class MeterContext : public std::enable_shared_from_this opentelemetry::common::SystemTimestamp GetSDKStartTime() noexcept; /** - * Attaches a metric reader to list of configured readers for this Meter context. - * @param reader The metric reader for this meter context. This - * must not be a nullptr. + * Create a MetricCollector from a MetricReader using this MeterContext and add it to the list of + * configured collectors. + * @param reader The MetricReader for which a MetricCollector is to be created. This must not be a + * nullptr. + * @param metric_filter The optional MetricFilter used when creating the MetricCollector. + * @return The MetricCollector created. * * Note: This reader may not receive any in-flight meter data, but will get newly created meter - * data. Note: This method is not thread safe, and should ideally be called from main thread. + * data. + * Note: This method is not thread safe, and should ideally be called from main thread. */ - void AddMetricReader(std::shared_ptr reader) noexcept; + std::shared_ptr AddMetricReader( + std::shared_ptr reader, + std::unique_ptr metric_filter = nullptr) noexcept; /** * Attaches a View to list of configured Views for this Meter context. diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h index 46b4efc93c..49b84f8496 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h @@ -11,6 +11,7 @@ #include "opentelemetry/metrics/meter_provider.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include "opentelemetry/sdk/metrics/view/instrument_selector.h" @@ -79,14 +80,20 @@ class OPENTELEMETRY_EXPORT MeterProvider final : public opentelemetry::metrics:: const sdk::resource::Resource &GetResource() const noexcept; /** - * Attaches a metric reader to list of configured readers for this Meter providers. - * @param reader The metric reader for this meter provider. This - * must not be a nullptr. + * Create a MetricCollector from a MetricReader using the MeterContext of this MeterProvider and + * add it to the list of configured collectors. + * @param reader The MetricReader for which a MetricCollector is to be created. This must not be a + * nullptr. + * @param metric_filter The optional MetricFilter used when creating the MetricCollector. + * @return The MetricCollector created. * * Note: This reader may not receive any in-flight meter data, but will get newly created meter - * data. Note: This method is not thread safe, and should ideally be called from main thread. + * data. + * Note: This method is not thread safe, and should ideally be called from main thread. */ - void AddMetricReader(std::shared_ptr reader) noexcept; + std::shared_ptr AddMetricReader( + std::shared_ptr reader, + std::unique_ptr metric_filter = nullptr) noexcept; /** * Attaches a View to list of configured Views for this Meter provider. diff --git a/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h b/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h index bfc3a06324..e1df3f61fc 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h @@ -7,6 +7,7 @@ #include #include "opentelemetry/nostd/function_ref.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/export/metric_producer.h" #include "opentelemetry/sdk/metrics/instruments.h" #include "opentelemetry/sdk/metrics/metric_reader.h" @@ -40,7 +41,9 @@ class CollectorHandle class MetricCollector : public MetricProducer, public CollectorHandle { public: - MetricCollector(MeterContext *context, std::shared_ptr metric_reader); + MetricCollector(MeterContext *context, + std::shared_ptr metric_reader, + std::unique_ptr metric_filter = nullptr); ~MetricCollector() override = default; @@ -62,6 +65,7 @@ class MetricCollector : public MetricProducer, public CollectorHandle private: MeterContext *meter_context_; std::shared_ptr metric_reader_; + std::unique_ptr metric_filter_; }; } // namespace metrics } // namespace sdk diff --git a/sdk/src/metrics/meter_context.cc b/sdk/src/metrics/meter_context.cc index 31dd1af8b1..9556eda853 100644 --- a/sdk/src/metrics/meter_context.cc +++ b/sdk/src/metrics/meter_context.cc @@ -79,10 +79,14 @@ opentelemetry::common::SystemTimestamp MeterContext::GetSDKStartTime() noexcept return sdk_start_ts_; } -void MeterContext::AddMetricReader(std::shared_ptr reader) noexcept +std::shared_ptr MeterContext::AddMetricReader( + std::shared_ptr reader, + std::unique_ptr metric_filter) noexcept { - auto collector = std::shared_ptr{new MetricCollector(this, std::move(reader))}; + auto collector = std::shared_ptr{ + new MetricCollector(this, std::move(reader), std::move(metric_filter))}; collectors_.push_back(collector); + return collector; } void MeterContext::AddView(std::unique_ptr instrument_selector, diff --git a/sdk/src/metrics/meter_provider.cc b/sdk/src/metrics/meter_provider.cc index 1e0df0f6f3..6ce18c5bbc 100644 --- a/sdk/src/metrics/meter_provider.cc +++ b/sdk/src/metrics/meter_provider.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include @@ -12,6 +13,8 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" +#include "opentelemetry/sdk/metrics/export/metric_producer.h" #include "opentelemetry/sdk/metrics/meter.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/meter_provider.h" @@ -107,9 +110,11 @@ const resource::Resource &MeterProvider::GetResource() const noexcept return context_->GetResource(); } -void MeterProvider::AddMetricReader(std::shared_ptr reader) noexcept +std::shared_ptr MeterProvider::AddMetricReader( + std::shared_ptr reader, + std::unique_ptr metric_filter) noexcept { - context_->AddMetricReader(std::move(reader)); + return context_->AddMetricReader(std::move(reader), std::move(metric_filter)); } void MeterProvider::AddView(std::unique_ptr instrument_selector, diff --git a/sdk/src/metrics/state/metric_collector.cc b/sdk/src/metrics/state/metric_collector.cc index 8e8a88485b..824f52a6c7 100644 --- a/sdk/src/metrics/state/metric_collector.cc +++ b/sdk/src/metrics/state/metric_collector.cc @@ -11,6 +11,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/export/metric_producer.h" #include "opentelemetry/sdk/metrics/instruments.h" #include "opentelemetry/sdk/metrics/meter.h" @@ -28,8 +29,11 @@ namespace metrics using opentelemetry::sdk::resource::Resource; MetricCollector::MetricCollector(opentelemetry::sdk::metrics::MeterContext *context, - std::shared_ptr metric_reader) - : meter_context_{context}, metric_reader_{std::move(metric_reader)} + std::shared_ptr metric_reader, + std::unique_ptr metric_filter) + : meter_context_{context}, + metric_reader_{std::move(metric_reader)}, + metric_filter_(std::move(metric_filter)) { metric_reader_->SetMetricProducer(this); } @@ -63,12 +67,61 @@ MetricProducer::Result MetricCollector::Produce() noexcept meter_context_->ForEachMeter([&](const std::shared_ptr &meter) noexcept { auto collection_ts = std::chrono::system_clock::now(); auto metric_data = meter->Collect(this, collection_ts); - if (!metric_data.empty()) + if (metric_data.empty()) + { + return true; + } + ScopeMetrics scope_metrics; + scope_metrics.metric_data_ = std::move(metric_data); + scope_metrics.scope_ = meter->GetInstrumentationScope(); + if (!this->metric_filter_) { - ScopeMetrics scope_metrics; - scope_metrics.metric_data_ = std::move(metric_data); - scope_metrics.scope_ = meter->GetInstrumentationScope(); resource_metrics.scope_metric_data_.emplace_back(std::move(scope_metrics)); + return true; + } + + ScopeMetrics filtered_scope_metrics; + filtered_scope_metrics.scope_ = meter->GetInstrumentationScope(); + for (MetricData &metric : scope_metrics.metric_data_) + { + const opentelemetry::sdk::instrumentationscope::InstrumentationScope &scope = + *scope_metrics.scope_; + opentelemetry::nostd::string_view name = metric.instrument_descriptor.name_; + const InstrumentType &type = metric.instrument_descriptor.type_; + opentelemetry::nostd::string_view unit = metric.instrument_descriptor.unit_; + + MetricFilter::MetricFilterResult metric_filter_result = + this->metric_filter_->TestMetric(scope, name, type, unit); + if (metric_filter_result == MetricFilter::MetricFilterResult::kAccept) + { + filtered_scope_metrics.metric_data_.emplace_back(std::move(metric)); + continue; + } + else if (metric_filter_result == MetricFilter::MetricFilterResult::kDrop) + { + continue; + } + + std::vector filtered_point_data_attrs; + for (PointDataAttributes &point_data_attr : metric.point_data_attr_) + { + const PointAttributes &attributes = point_data_attr.attributes; + MetricFilter::AttributesFilterResult attributes_filter_result = + this->metric_filter_->TestAttributes(scope, name, type, unit, attributes); + if (attributes_filter_result == MetricFilter::AttributesFilterResult::kAccept) + { + filtered_point_data_attrs.emplace_back(std::move(point_data_attr)); + } + } + if (!filtered_point_data_attrs.empty()) + { + metric.point_data_attr_ = std::move(filtered_point_data_attrs); + filtered_scope_metrics.metric_data_.emplace_back(std::move(metric)); + } + } + if (!filtered_scope_metrics.metric_data_.empty()) + { + resource_metrics.scope_metric_data_.emplace_back(std::move(filtered_scope_metrics)); } return true; }); diff --git a/sdk/test/metrics/CMakeLists.txt b/sdk/test/metrics/CMakeLists.txt index 9effa73a4c..e5c271370d 100644 --- a/sdk/test/metrics/CMakeLists.txt +++ b/sdk/test/metrics/CMakeLists.txt @@ -27,6 +27,7 @@ foreach( observer_result_test sync_instruments_test async_instruments_test + metric_collector_test metric_reader_test observable_registry_test periodic_exporting_metric_reader_test diff --git a/sdk/test/metrics/metric_collector_test.cc b/sdk/test/metrics/metric_collector_test.cc new file mode 100644 index 0000000000..5b715223b8 --- /dev/null +++ b/sdk/test/metrics/metric_collector_test.cc @@ -0,0 +1,391 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/context/context.h" +#include "opentelemetry/metrics/async_instruments.h" +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/metrics/observer_result.h" +#include "opentelemetry/metrics/provider.h" +#include "opentelemetry/metrics/sync_instruments.h" // IWYU pragma: keep +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/data/point_data.h" +#include "opentelemetry/sdk/metrics/export/metric_producer.h" +#include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/meter.h" +#include "opentelemetry/sdk/metrics/meter_context.h" +#include "opentelemetry/sdk/metrics/meter_context_factory.h" +#include "opentelemetry/sdk/metrics/meter_provider.h" +#include "opentelemetry/sdk/metrics/meter_provider_factory.h" +#include "opentelemetry/sdk/metrics/metric_reader.h" +#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.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" +#include "opentelemetry/sdk/metrics/view/view.h" +#include "opentelemetry/sdk/metrics/view/view_factory.h" +#include "opentelemetry/sdk/metrics/view/view_registry_factory.h" +#include "opentelemetry/version.h" + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationscope; +using namespace opentelemetry::sdk::metrics; +using namespace testing; + +using M = std::map; + +namespace +{ + +MetricFilter::TestMetricFn AcceptAllTestMetricFn() +{ + return [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit) -> MetricFilter::MetricFilterResult { + return MetricFilter::MetricFilterResult::kAccept; + }; +} +MetricFilter::TestMetricFn DropAllTestMetricFn() +{ + return [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit) -> MetricFilter::MetricFilterResult { + return MetricFilter::MetricFilterResult::kDrop; + }; +} +MetricFilter::TestMetricFn AcceptPartialAllTestMetricFn() +{ + return [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit) -> MetricFilter::MetricFilterResult { + return MetricFilter::MetricFilterResult::kAcceptPartial; + }; +} + +MetricFilter::TestAttributesFn AcceptAllTestAttributesFn() +{ + return [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit, + const PointAttributes &attributes) -> MetricFilter::AttributesFilterResult { + return MetricFilter::AttributesFilterResult::kAccept; + }; +} +MetricFilter::TestAttributesFn DropAllTestAttributesFn() +{ + return [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit, + const PointAttributes &attributes) -> MetricFilter::AttributesFilterResult { + return MetricFilter::AttributesFilterResult::kDrop; + }; +} + +} // namespace + +TEST(MetricCollectorTest, CollectWithMetricFilterTestMetricTest1) +{ + auto context = std::shared_ptr(new MeterContext(ViewRegistryFactory::Create())); + auto scope = InstrumentationScope::Create("CollectWithMetricFilterTestMetricTest1"); + auto meter = std::shared_ptr(new Meter(context, std::move(scope))); + context->AddMeter(meter); + + auto filter = MetricFilter::Create(AcceptAllTestMetricFn(), DropAllTestAttributesFn()); + auto reader = std::shared_ptr(new MockMetricReader()); + auto collector = context->AddMetricReader(reader, std::move(filter)); + + auto instrument_1_name = "instrument_1"; + auto instrument_1 = meter->CreateUInt64Counter(instrument_1_name); + + auto instrument_2_name = "instrument_2"; + auto instrument_2 = meter->CreateUInt64Counter(instrument_2_name); + M m_2 = {{"stream", "1"}}; + instrument_2->Add(1, opentelemetry::common::KeyValueIterableView(m_2), + opentelemetry::context::Context{}); + + auto instrument_3_name = "instrument_3"; + auto instrument_3 = meter->CreateUInt64Counter(instrument_3_name); + for (int s = 1; s <= 10; ++s) + { + for (int i = 0; i < s; ++i) + { + M m_3 = {{"stream", std::to_string(s)}}; + instrument_3->Add(1, opentelemetry::common::KeyValueIterableView(m_3), + opentelemetry::context::Context{}); + } + } + + auto resource_metrics = collector->Produce().points_; + for (const ScopeMetrics &scope_metrics : resource_metrics.scope_metric_data_) + { + for (const MetricData &metric : scope_metrics.metric_data_) + { + auto instrument_name = metric.instrument_descriptor.name_; + ASSERT_TRUE(instrument_name == instrument_2_name || instrument_name == instrument_3_name); + if (instrument_name == instrument_2_name) + { + EXPECT_EQ(metric.point_data_attr_.size(), 1); + } + else if (instrument_name == instrument_3_name) + { + EXPECT_EQ(metric.point_data_attr_.size(), 10); + } + } + } +} + +TEST(MetricCollectorTest, CollectWithMetricFilterTestMetricTest2) +{ + auto context = std::shared_ptr(new MeterContext(ViewRegistryFactory::Create())); + auto scope = InstrumentationScope::Create("CollectWithMetricFilterTestMetricTest2"); + auto meter = std::shared_ptr(new Meter(context, std::move(scope))); + context->AddMeter(meter); + + auto filter = MetricFilter::Create(DropAllTestMetricFn(), AcceptAllTestAttributesFn()); + auto reader = std::shared_ptr(new MockMetricReader()); + auto collector = context->AddMetricReader(reader, std::move(filter)); + + auto instrument_1_name = "instrument_1"; + auto instrument_1 = meter->CreateUInt64Counter(instrument_1_name); + + auto instrument_2_name = "instrument_2"; + auto instrument_2 = meter->CreateUInt64Counter(instrument_2_name); + M m_2 = {{"stream", "1"}}; + instrument_2->Add(1, opentelemetry::common::KeyValueIterableView(m_2), + opentelemetry::context::Context{}); + + auto instrument_3_name = "instrument_3"; + auto instrument_3 = meter->CreateUInt64Counter(instrument_3_name); + for (int s = 1; s <= 10; ++s) + { + for (int i = 0; i < s; ++i) + { + M m_3 = {{"stream", std::to_string(s)}}; + instrument_3->Add(1, opentelemetry::common::KeyValueIterableView(m_3), + opentelemetry::context::Context{}); + } + } + + auto resource_metrics = collector->Produce().points_; + EXPECT_EQ(resource_metrics.scope_metric_data_.size(), 0); +} + +TEST(MetricCollectorTest, CollectWithMetricFilterTestMetricTest3) +{ + auto context = std::shared_ptr(new MeterContext(ViewRegistryFactory::Create())); + auto scope = InstrumentationScope::Create("CollectWithMetricFilterTestMetricTest3"); + auto meter = std::shared_ptr(new Meter(context, std::move(scope))); + context->AddMeter(meter); + + auto test_metric_fn = [](const InstrumentationScope &scope, nostd::string_view name, + const InstrumentType &type, + nostd::string_view unit) -> MetricFilter::MetricFilterResult { + std::string name_copy = {name.begin(), name.end()}; + if (name_copy.find("_accept") != std::string::npos) + { + return MetricFilter::MetricFilterResult::kAccept; + } + return MetricFilter::MetricFilterResult::kDrop; + }; + auto filter = MetricFilter::Create(test_metric_fn, DropAllTestAttributesFn()); + auto reader = std::shared_ptr(new MockMetricReader()); + auto collector = context->AddMetricReader(reader, std::move(filter)); + + auto instrument_1_name = "instrument_1"; + auto instrument_1 = meter->CreateUInt64Counter(instrument_1_name); + + auto instrument_2_name = "instrument_2"; + auto instrument_2 = meter->CreateUInt64Counter(instrument_2_name); + M m_2 = {{"stream", "1"}}; + instrument_2->Add(1, opentelemetry::common::KeyValueIterableView(m_2), + opentelemetry::context::Context{}); + + auto instrument_3_name = "instrument_3_accept"; + auto instrument_3 = meter->CreateUInt64Counter(instrument_3_name); + for (int s = 1; s <= 10; ++s) + { + for (int i = 0; i < s; ++i) + { + M m_3 = {{"stream", std::to_string(s)}}; + instrument_3->Add(1, opentelemetry::common::KeyValueIterableView(m_3), + opentelemetry::context::Context{}); + } + } + + auto resource_metrics = collector->Produce().points_; + for (const ScopeMetrics &scope_metrics : resource_metrics.scope_metric_data_) + { + for (const MetricData &metric : scope_metrics.metric_data_) + { + auto instrument_name = metric.instrument_descriptor.name_; + ASSERT_EQ(instrument_name, instrument_3_name); + EXPECT_EQ(metric.point_data_attr_.size(), 10); + } + } +} + +TEST(MetricCollectorTest, CollectWithMetricFilterTestAttributesTest1) +{ + auto context = std::shared_ptr(new MeterContext(ViewRegistryFactory::Create())); + auto scope = InstrumentationScope::Create("CollectWithMetricFilterTestAttributesTest1"); + auto meter = std::shared_ptr(new Meter(context, std::move(scope))); + context->AddMeter(meter); + + auto test_attributes_fn = + [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit, + const PointAttributes &attributes) -> MetricFilter::AttributesFilterResult { + if (attributes.GetAttributes().find("stream") != attributes.GetAttributes().end()) + { + return MetricFilter::AttributesFilterResult::kAccept; + } + return MetricFilter::AttributesFilterResult::kDrop; + }; + auto filter = MetricFilter::Create(AcceptPartialAllTestMetricFn(), test_attributes_fn); + auto reader = std::shared_ptr(new MockMetricReader()); + auto collector = context->AddMetricReader(reader, std::move(filter)); + + auto instrument_1_name = "instrument_1"; + auto instrument_1 = meter->CreateUInt64Counter(instrument_1_name); + M m_1 = {{"ocean", "1"}}; + instrument_1->Add(1, opentelemetry::common::KeyValueIterableView(m_1), + opentelemetry::context::Context{}); + + auto instrument_2_name = "instrument_2"; + auto instrument_2 = meter->CreateUInt64Counter(instrument_2_name); + M m_2 = {{"stream", "1"}, {"river", "10"}}; + instrument_2->Add(1, opentelemetry::common::KeyValueIterableView(m_2), + opentelemetry::context::Context{}); + + auto instrument_3_name = "instrument_3"; + auto instrument_3 = meter->CreateUInt64Counter(instrument_3_name); + for (int s = 1; s <= 10; ++s) + { + for (int i = 0; i < s; ++i) + { + M m_3 = {{"stream", std::to_string(s)}, {"river", std::to_string(s * 10)}}; + instrument_3->Add(1, opentelemetry::common::KeyValueIterableView(m_3), + opentelemetry::context::Context{}); + } + } + + auto resource_metrics = collector->Produce().points_; + for (const ScopeMetrics &scope_metrics : resource_metrics.scope_metric_data_) + { + for (const MetricData &metric : scope_metrics.metric_data_) + { + auto instrument_name = metric.instrument_descriptor.name_; + ASSERT_TRUE(instrument_name == instrument_2_name || instrument_name == instrument_3_name); + if (instrument_name == instrument_2_name) + { + EXPECT_EQ(metric.point_data_attr_.size(), 1); + } + else if (instrument_name == instrument_3_name) + { + EXPECT_EQ(metric.point_data_attr_.size(), 10); + for (const PointDataAttributes &pda : metric.point_data_attr_) + { + auto sum_point_data = nostd::get(pda.point_data); + auto value = nostd::get(sum_point_data.value_); + auto stream = + std::stoi(nostd::get(pda.attributes.GetAttributes().at("stream"))); + auto river = + std::stoi(nostd::get(pda.attributes.GetAttributes().at("river"))); + std::vector stream_v = {value, stream}; + std::vector river_v = {value, river}; + EXPECT_THAT(stream_v, AnyOf(ElementsAre(1, 1), ElementsAre(2, 2), ElementsAre(3, 3), + ElementsAre(4, 4), ElementsAre(5, 5), ElementsAre(6, 6), + ElementsAre(7, 7), ElementsAre(8, 8), ElementsAre(9, 9), + ElementsAre(10, 10))); + EXPECT_THAT(river_v, AnyOf(ElementsAre(1, 10), ElementsAre(2, 20), ElementsAre(3, 30), + ElementsAre(4, 40), ElementsAre(5, 50), ElementsAre(6, 60), + ElementsAre(7, 70), ElementsAre(8, 80), ElementsAre(9, 90), + ElementsAre(10, 100))); + } + } + } + } +} + +TEST(MetricCollectorTest, CollectWithMetricFilterTestAttributesTest2) +{ + auto context = std::shared_ptr(new MeterContext(ViewRegistryFactory::Create())); + auto scope = InstrumentationScope::Create("CollectWithMetricFilterTestAttributesTest2"); + auto meter = std::shared_ptr(new Meter(context, std::move(scope))); + context->AddMeter(meter); + + auto test_attributes_fn = + [](const InstrumentationScope &scope, nostd::string_view name, const InstrumentType &type, + nostd::string_view unit, + const PointAttributes &attributes) -> MetricFilter::AttributesFilterResult { + if (attributes.GetAttributes().find("stream") != attributes.GetAttributes().end()) + { + auto stream = nostd::get(attributes.GetAttributes().at("stream")); + if (std::stoi(stream) >= 4 && std::stoi(stream) <= 6) + { + return MetricFilter::AttributesFilterResult::kAccept; + } + } + return MetricFilter::AttributesFilterResult::kDrop; + }; + auto filter = MetricFilter::Create(AcceptPartialAllTestMetricFn(), test_attributes_fn); + auto reader = std::shared_ptr(new MockMetricReader()); + auto collector = context->AddMetricReader(reader, std::move(filter)); + + auto instrument_1_name = "instrument_1"; + auto instrument_1 = meter->CreateUInt64Counter(instrument_1_name); + M m_1 = {{"ocean", "1"}}; + instrument_1->Add(1, opentelemetry::common::KeyValueIterableView(m_1), + opentelemetry::context::Context{}); + + auto instrument_2_name = "instrument_2"; + auto instrument_2 = meter->CreateUInt64Counter(instrument_2_name); + M m_2 = {{"stream", "1"}, {"river", "10"}}; + instrument_2->Add(1, opentelemetry::common::KeyValueIterableView(m_2), + opentelemetry::context::Context{}); + + auto instrument_3_name = "instrument_3"; + auto instrument_3 = meter->CreateUInt64Counter(instrument_3_name); + for (int s = 1; s <= 10; ++s) + { + for (int i = 0; i < s; ++i) + { + M m_3 = {{"stream", std::to_string(s)}, {"river", std::to_string(s * 10)}}; + instrument_3->Add(1, opentelemetry::common::KeyValueIterableView(m_3), + opentelemetry::context::Context{}); + } + } + + auto resource_metrics = collector->Produce().points_; + for (const ScopeMetrics &scope_metrics : resource_metrics.scope_metric_data_) + { + for (const MetricData &metric : scope_metrics.metric_data_) + { + auto instrument_name = metric.instrument_descriptor.name_; + ASSERT_EQ(instrument_name, instrument_3_name); + EXPECT_EQ(metric.point_data_attr_.size(), 3); + for (const PointDataAttributes &pda : metric.point_data_attr_) + { + auto sum_point_data = nostd::get(pda.point_data); + auto value = nostd::get(sum_point_data.value_); + auto stream = + std::stoi(nostd::get(pda.attributes.GetAttributes().at("stream"))); + auto river = std::stoi(nostd::get(pda.attributes.GetAttributes().at("river"))); + std::vector stream_v = {value, stream}; + std::vector river_v = {value, river}; + EXPECT_THAT(stream_v, AnyOf(ElementsAre(4, 4), ElementsAre(5, 5), ElementsAre(6, 6))); + EXPECT_THAT(river_v, AnyOf(ElementsAre(4, 40), ElementsAre(5, 50), ElementsAre(6, 60))); + } + } + } +} diff --git a/third_party/opentelemetry-proto b/third_party/opentelemetry-proto index 2bd940b2b7..0adf6aac00 160000 --- a/third_party/opentelemetry-proto +++ b/third_party/opentelemetry-proto @@ -1 +1 @@ -Subproject commit 2bd940b2b77c1ab57c27166af21384906da7bb2b +Subproject commit 0adf6aac004578b28267394514b2e55ee9cc012f