From 8a93801ecc63266230b5da44ca4f8e7284e01a73 Mon Sep 17 00:00:00 2001 From: Punya Biswal Date: Sat, 31 Aug 2024 17:12:48 -0400 Subject: [PATCH] Implement in-memory metric exporter Fixes #1405 --- CHANGELOG.md | 3 + exporters/memory/BUILD | 31 ++++++++++ exporters/memory/CMakeLists.txt | 27 +++++++++ .../memory/in_memory_metric_exporter.h | 56 ++++++++++++++++++ .../memory/src/in_memory_metric_exporter.cc | 53 +++++++++++++++++ .../test/in_memory_metric_exporter_test.cc | 59 +++++++++++++++++++ 6 files changed, 229 insertions(+) create mode 100644 exporters/memory/include/opentelemetry/exporters/memory/in_memory_metric_exporter.h create mode 100644 exporters/memory/src/in_memory_metric_exporter.cc create mode 100644 exporters/memory/test/in_memory_metric_exporter_test.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index bce84c84df..66d60f3513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ Increment the: * [REMOVAL] Remove build option `WITH_DEPRECATED_SDK_FACTORY` [#2717](https://github.com/open-telemetry/opentelemetry-cpp/pull/2717) +* [EXPORTER] Add in-memory metric exporter + [#3043](https://github.com/open-telemetry/opentelemetry-cpp/pull/3043) + Breaking changes: * [REMOVAL] Remove build option `WITH_DEPRECATED_SDK_FACTORY` diff --git a/exporters/memory/BUILD b/exporters/memory/BUILD index a65066480c..01ab3c7ba5 100644 --- a/exporters/memory/BUILD +++ b/exporters/memory/BUILD @@ -3,6 +3,37 @@ package(default_visibility = ["//visibility:public"]) +cc_library( + name = "in_memory_metric_exporter", + srcs = [ + "src/in_memory_metric_exporter.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/memory/in_memory_metric_exporter.h", + ], + strip_include_prefix = "include", + tags = [ + "memory", + "test", + ], + deps = [ + "//sdk/src/metrics", + ], +) + +cc_test( + name = "in_memory_metric_exporter_test", + srcs = ["test/in_memory_metric_exporter_test.cc"], + tags = [ + "memory", + "test", + ], + deps = [ + ":in_memory_metric_exporter", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "in_memory_span_data", hdrs = [ diff --git a/exporters/memory/CMakeLists.txt b/exporters/memory/CMakeLists.txt index 3dff044018..6ef39c326a 100644 --- a/exporters/memory/CMakeLists.txt +++ b/exporters/memory/CMakeLists.txt @@ -16,9 +16,25 @@ set_target_version(opentelemetry_exporter_in_memory) target_link_libraries(opentelemetry_exporter_in_memory PUBLIC opentelemetry_trace) +add_library(opentelemetry_exporter_in_memory_metric + src/in_memory_metric_exporter.cc) + +target_include_directories( + opentelemetry_exporter_in_memory_metric + PUBLIC "$" + "$") + +set_target_properties(opentelemetry_exporter_in_memory_metric + PROPERTIES EXPORT_NAME in_memory_metric_exporter) +set_target_version(opentelemetry_exporter_in_memory_metric) + +target_link_libraries(opentelemetry_exporter_in_memory + PUBLIC opentelemetry_metrics) + if(OPENTELEMETRY_INSTALL) install( TARGETS opentelemetry_exporter_in_memory + opentelemetry_exporter_in_memory_metric EXPORT "${PROJECT_NAME}-target" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -35,6 +51,8 @@ if(BUILD_TESTING) add_executable(in_memory_span_data_test test/in_memory_span_data_test.cc) add_executable(in_memory_span_exporter_test test/in_memory_span_exporter_test.cc) + add_executable(in_memory_metric_exporter_test + test/in_memory_metric_exporter_test.cc) target_link_libraries( in_memory_span_data_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} @@ -45,6 +63,11 @@ if(BUILD_TESTING) ${CMAKE_THREAD_LIBS_INIT} opentelemetry_exporter_in_memory opentelemetry_resources) + target_link_libraries( + in_memory_metric_exporter_test ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_exporter_in_memory_metric + opentelemetry_resources) + gtest_add_tests( TARGET in_memory_span_data_test TEST_PREFIX exporter. @@ -53,4 +76,8 @@ if(BUILD_TESTING) TARGET in_memory_span_exporter_test TEST_PREFIX exporter. TEST_LIST in_memory_span_exporter_test) + gtest_add_tests( + TARGET in_memory_metric_exporter_test + TEST_PREFIX exporter. + TEST_LIST in_memory_metric_exporter_test) endif() diff --git a/exporters/memory/include/opentelemetry/exporters/memory/in_memory_metric_exporter.h b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_metric_exporter.h new file mode 100644 index 0000000000..c0940246af --- /dev/null +++ b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_metric_exporter.h @@ -0,0 +1,56 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/sdk/metrics/export/metric_producer.h" +#include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/push_metric_exporter.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace memory +{ + +/// A Push Metric Exporter which accumulates metrics data in memory and allows it to be inspected. +/// It is not thread-safe. +class InMemoryMetricExporter final : public sdk::metrics::PushMetricExporter +{ + using AggregationTemporalityMap = + std::map; + +public: + /// @param temporality Output temporality as a function of instrument kind. + InMemoryMetricExporter(const sdk::metrics::AggregationTemporalitySelector &temporality) + : temporality_(temporality) + {} + + InMemoryMetricExporter(const InMemoryMetricExporter &) = delete; + InMemoryMetricExporter(const InMemoryMetricExporter &&) = delete; + void operator=(const InMemoryMetricExporter &) = delete; + void operator=(const InMemoryMetricExporter &&) = delete; + ~InMemoryMetricExporter() override = default; + + sdk::common::ExportResult Export(const sdk::metrics::ResourceMetrics &data) noexcept override; + sdk::metrics::AggregationTemporality GetAggregationTemporality( + sdk::metrics::InstrumentType instrument_type) const noexcept override; + bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override; + + const std::vector &GetData() const; + +private: + std::vector data_{}; + std::atomic is_shutdown_{false}; + sdk::metrics::AggregationTemporalitySelector temporality_; +}; + +} // namespace memory +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/memory/src/in_memory_metric_exporter.cc b/exporters/memory/src/in_memory_metric_exporter.cc new file mode 100644 index 0000000000..1da117fc62 --- /dev/null +++ b/exporters/memory/src/in_memory_metric_exporter.cc @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/exporters/memory/in_memory_metric_exporter.h" +#include "opentelemetry/sdk/common/global_log_handler.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace memory +{ + +using sdk::common::ExportResult; +using sdk::metrics::AggregationTemporality; +using sdk::metrics::InstrumentType; +using sdk::metrics::ResourceMetrics; + +ExportResult InMemoryMetricExporter::Export(const ResourceMetrics &data) noexcept +{ + if (is_shutdown_) + { + OTEL_INTERNAL_LOG_ERROR("[In Memory Metric Exporter] Exporting failed, exporter is shutdown"); + return ExportResult::kFailure; + } + data_.push_back(data); + return ExportResult::kSuccess; +} + +AggregationTemporality InMemoryMetricExporter::GetAggregationTemporality( + InstrumentType instrument_type) const noexcept +{ + return temporality_(instrument_type); +} + +bool InMemoryMetricExporter::ForceFlush(std::chrono::microseconds /* timeout */) noexcept +{ + return true; +} + +bool InMemoryMetricExporter::Shutdown(std::chrono::microseconds /* timeout */) noexcept +{ + is_shutdown_ = true; + return true; +} + +const std::vector &InMemoryMetricExporter::GetData() const +{ + return data_; +} + +} // namespace memory +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/memory/test/in_memory_metric_exporter_test.cc b/exporters/memory/test/in_memory_metric_exporter_test.cc new file mode 100644 index 0000000000..151bdb804f --- /dev/null +++ b/exporters/memory/test/in_memory_metric_exporter_test.cc @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/exporters/memory/in_memory_metric_exporter.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/export/metric_producer.h" +#include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/resource/resource.h" + +#include + +using opentelemetry::exporter::memory::InMemoryMetricExporter; +using opentelemetry::sdk::instrumentationscope::InstrumentationScope; +using opentelemetry::sdk::metrics::AggregationTemporality; +using opentelemetry::sdk::metrics::InstrumentType; +using opentelemetry::sdk::metrics::InstrumentValueType; +using opentelemetry::sdk::metrics::MetricData; +using opentelemetry::sdk::metrics::ScopeMetrics; +using opentelemetry::sdk::metrics::SumPointData; +using opentelemetry::sdk::resource::Resource; + +TEST(InMemoryMetricExporter, Instantiate) +{ + InMemoryMetricExporter exporter([](auto) { return AggregationTemporality::kCumulative; }); + + auto &resource = Resource::GetEmpty(); + auto instrumentation_scope = InstrumentationScope::Create("test"); + exporter.Export({ + &resource, + std::vector{{ + instrumentation_scope.get(), + std::vector{{ + .instrument_descriptor{ + .name_ = "test-metric", + .description_ = "test-description", + .unit_ = "test-unit", + .type_ = InstrumentType::kCounter, + .value_type_ = InstrumentValueType::kDouble, + }, + .aggregation_temporality = AggregationTemporality::kCumulative, + .start_ts{}, + .end_ts{}, + .point_data_attr_{{ + .attributes = {{"hello", "world"}}, + .point_data = + SumPointData{ + .value_ = 4.2, + .is_monotonic_ = true, + }, + }}, + }}, + }}, + }); + + auto &data = exporter.GetData(); + EXPECT_EQ(data.size(), 1); + EXPECT_EQ(data.begin()->resource_, &resource); +}