From dee5b0bc5c5cad8a2e6cf372576dd6a91c5cb3d3 Mon Sep 17 00:00:00 2001 From: IcySteam Date: Wed, 12 Mar 2025 10:18:55 +1100 Subject: [PATCH] [SDK] Implement spec: MetricFilter (#3235) --- CHANGELOG.md | 3 + .../sdk/metrics/export/metric_filter.h | 112 +++++ .../sdk/metrics/export/metric_producer.h | 8 +- .../opentelemetry/sdk/metrics/meter_context.h | 22 +- .../sdk/metrics/meter_provider.h | 15 +- .../sdk/metrics/state/metric_collector.h | 6 +- sdk/src/metrics/meter_context.cc | 7 +- sdk/src/metrics/meter_provider.cc | 6 +- sdk/src/metrics/state/metric_collector.cc | 67 ++- sdk/test/metrics/CMakeLists.txt | 1 + sdk/test/metrics/async_metric_storage_test.cc | 1 + sdk/test/metrics/common.cc | 2 + .../histogram_aggregation_benchmark.cc | 1 + .../metrics/histogram_aggregation_test.cc | 1 + sdk/test/metrics/histogram_test.cc | 1 + sdk/test/metrics/metric_collector_test.cc | 403 ++++++++++++++++++ sdk/test/metrics/metric_reader_test.cc | 1 + .../periodic_exporting_metric_reader_test.cc | 1 + sdk/test/metrics/sum_aggregation_benchmark.cc | 1 + sdk/test/metrics/sum_aggregation_test.cc | 1 + 20 files changed, 638 insertions(+), 22 deletions(-) create mode 100644 sdk/include/opentelemetry/sdk/metrics/export/metric_filter.h create mode 100644 sdk/test/metrics/metric_collector_test.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 7028d53118..c51a88bb02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ Important changes: * All the example code has been updated to reflect the new usage. +* [SDK] Implement spec: MetricFilter + [#3235](https://github.com/open-telemetry/opentelemetry-cpp/pull/3235) + ## [1.19 2025-01-22] * [PROMETHEUS_EXPORTER] Fix default for emitting otel_scope attributes 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..f21b9e8fc3 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/export/metric_filter.h @@ -0,0 +1,112 @@ +// 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..e5faf9fbce 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,9 @@ 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 +111,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 8925059a96..468f5cdab5 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_context.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_context.h @@ -14,6 +14,7 @@ #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include "opentelemetry/sdk/metrics/state/metric_collector.h" @@ -28,6 +29,11 @@ # include "opentelemetry/sdk/metrics/exemplar/filter_type.h" #endif +namespace testing +{ +class MetricCollectorTest; +} + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -107,14 +113,18 @@ 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. * * 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; + void AddMetricReader(std::shared_ptr reader, + std::unique_ptr metric_filter = nullptr) noexcept; /** * Attaches a View to list of configured Views for this Meter context. @@ -161,6 +171,8 @@ class MeterContext : public std::enable_shared_from_this bool Shutdown(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; private: + friend class ::testing::MetricCollectorTest; + opentelemetry::sdk::resource::Resource resource_; std::vector> collectors_; std::unique_ptr views_; diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h index 5b794fa809..b2c5d36b18 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" @@ -88,14 +89,18 @@ 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. * * 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; + void 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 1aa4782c34..2b58387d48 100644 --- a/sdk/src/metrics/meter_context.cc +++ b/sdk/src/metrics/meter_context.cc @@ -18,6 +18,7 @@ #include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/meter.h" #include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" @@ -92,9 +93,11 @@ opentelemetry::common::SystemTimestamp MeterContext::GetSDKStartTime() noexcept return sdk_start_ts_; } -void MeterContext::AddMetricReader(std::shared_ptr reader) noexcept +void 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); } diff --git a/sdk/src/metrics/meter_provider.cc b/sdk/src/metrics/meter_provider.cc index 82af065f30..57a2fbaced 100644 --- a/sdk/src/metrics/meter_provider.cc +++ b/sdk/src/metrics/meter_provider.cc @@ -13,6 +13,7 @@ #include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/export/metric_filter.h" #include "opentelemetry/sdk/metrics/meter.h" #include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" @@ -112,9 +113,10 @@ const resource::Resource &MeterProvider::GetResource() const noexcept return context_->GetResource(); } -void MeterProvider::AddMetricReader(std::shared_ptr reader) noexcept +void MeterProvider::AddMetricReader(std::shared_ptr reader, + std::unique_ptr metric_filter) noexcept { - context_->AddMetricReader(std::move(reader)); + 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..162a89c3f1 100644 --- a/sdk/src/metrics/state/metric_collector.cc +++ b/sdk/src/metrics/state/metric_collector.cc @@ -9,8 +9,11 @@ #include #include "opentelemetry/nostd/function_ref.h" +#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/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 +31,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 +69,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 f82a386816..4e09af4d2e 100644 --- a/sdk/test/metrics/CMakeLists.txt +++ b/sdk/test/metrics/CMakeLists.txt @@ -29,6 +29,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/async_metric_storage_test.cc b/sdk/test/metrics/async_metric_storage_test.cc index 8df5a4ef1b..542415a90f 100644 --- a/sdk/test/metrics/async_metric_storage_test.cc +++ b/sdk/test/metrics/async_metric_storage_test.cc @@ -17,6 +17,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/span.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" diff --git a/sdk/test/metrics/common.cc b/sdk/test/metrics/common.cc index 66e8dd2c80..1e9305fb34 100644 --- a/sdk/test/metrics/common.cc +++ b/sdk/test/metrics/common.cc @@ -4,6 +4,8 @@ #include "common.h" #include +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" + using namespace opentelemetry::sdk::instrumentationscope; using namespace opentelemetry::sdk::metrics; using namespace opentelemetry::sdk::common; diff --git a/sdk/test/metrics/histogram_aggregation_benchmark.cc b/sdk/test/metrics/histogram_aggregation_benchmark.cc index 3e9951ad71..b66538042c 100644 --- a/sdk/test/metrics/histogram_aggregation_benchmark.cc +++ b/sdk/test/metrics/histogram_aggregation_benchmark.cc @@ -18,6 +18,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/shared_ptr.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" diff --git a/sdk/test/metrics/histogram_aggregation_test.cc b/sdk/test/metrics/histogram_aggregation_test.cc index 909046a31f..ee31683993 100644 --- a/sdk/test/metrics/histogram_aggregation_test.cc +++ b/sdk/test/metrics/histogram_aggregation_test.cc @@ -15,6 +15,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/shared_ptr.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" diff --git a/sdk/test/metrics/histogram_test.cc b/sdk/test/metrics/histogram_test.cc index eb6cba45b4..490e12cd7d 100644 --- a/sdk/test/metrics/histogram_test.cc +++ b/sdk/test/metrics/histogram_test.cc @@ -17,6 +17,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/variant.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/metrics/aggregation/aggregation_config.h" #include "opentelemetry/sdk/metrics/data/metric_data.h" #include "opentelemetry/sdk/metrics/data/point_data.h" diff --git a/sdk/test/metrics/metric_collector_test.cc b/sdk/test/metrics/metric_collector_test.cc new file mode 100644 index 0000000000..fe05981de5 --- /dev/null +++ b/sdk/test/metrics/metric_collector_test.cc @@ -0,0 +1,403 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "gmock/gmock.h" + +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/context/context.h" +#include "opentelemetry/metrics/sync_instruments.h" // IWYU pragma: keep +#include "opentelemetry/nostd/string_view.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_filter.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/metric_reader.h" +#include "opentelemetry/sdk/metrics/state/metric_collector.h" +#include "opentelemetry/sdk/metrics/view/view_registry.h" +#include "opentelemetry/sdk/metrics/view/view_registry_factory.h" + +#if defined(__GNUC__) || defined(__clang__) || defined(__apple_build_version__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +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 + +class testing::MetricCollectorTest : public Test +{ +public: + std::weak_ptr AddMetricReaderToMeterContext( + std::shared_ptr context, + std::shared_ptr reader, + std::unique_ptr metric_filter = nullptr) noexcept + { + auto collector = std::shared_ptr{ + new MetricCollector(context.get(), std::move(reader), std::move(metric_filter))}; + context->collectors_.push_back(collector); + return std::weak_ptr(collector); + } +}; + +TEST_F(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 = AddMetricReaderToMeterContext(context, reader, std::move(filter)).lock(); + + 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_F(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 = AddMetricReaderToMeterContext(context, reader, std::move(filter)).lock(); + + 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_F(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 = AddMetricReaderToMeterContext(context, reader, std::move(filter)).lock(); + + 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_F(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 = AddMetricReaderToMeterContext(context, reader, std::move(filter)).lock(); + + 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_F(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 = AddMetricReaderToMeterContext(context, reader, std::move(filter)).lock(); + + 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))); + } + } + } +} + +#if defined(__GNUC__) || defined(__clang__) || defined(__apple_build_version__) +# pragma GCC diagnostic pop +#endif diff --git a/sdk/test/metrics/metric_reader_test.cc b/sdk/test/metrics/metric_reader_test.cc index 7b2f398cc5..36ca6532ff 100644 --- a/sdk/test/metrics/metric_reader_test.cc +++ b/sdk/test/metrics/metric_reader_test.cc @@ -6,6 +6,7 @@ #include #include "common.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/metrics/export/metric_producer.h" #include "opentelemetry/sdk/metrics/instruments.h" #include "opentelemetry/sdk/metrics/meter_context.h" diff --git a/sdk/test/metrics/periodic_exporting_metric_reader_test.cc b/sdk/test/metrics/periodic_exporting_metric_reader_test.cc index dde23cb275..ed144aaae6 100644 --- a/sdk/test/metrics/periodic_exporting_metric_reader_test.cc +++ b/sdk/test/metrics/periodic_exporting_metric_reader_test.cc @@ -12,6 +12,7 @@ #include #include "opentelemetry/sdk/common/exporter_utils.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/metrics/export/metric_producer.h" #include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h" #include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_options.h" diff --git a/sdk/test/metrics/sum_aggregation_benchmark.cc b/sdk/test/metrics/sum_aggregation_benchmark.cc index a20a204ebd..a51c1bcb66 100644 --- a/sdk/test/metrics/sum_aggregation_benchmark.cc +++ b/sdk/test/metrics/sum_aggregation_benchmark.cc @@ -18,6 +18,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/shared_ptr.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" diff --git a/sdk/test/metrics/sum_aggregation_test.cc b/sdk/test/metrics/sum_aggregation_test.cc index bf5c16fe42..72783f6492 100644 --- a/sdk/test/metrics/sum_aggregation_test.cc +++ b/sdk/test/metrics/sum_aggregation_test.cc @@ -17,6 +17,7 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/variant.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/metrics/aggregation/aggregation_config.h" #include "opentelemetry/sdk/metrics/data/metric_data.h" #include "opentelemetry/sdk/metrics/data/point_data.h"