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..3e6a0bcd9b 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_metric + 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..635e743f62 --- /dev/null +++ b/exporters/memory/test/in_memory_metric_exporter_test.cc @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/exporters/memory/in_memory_metric_exporter.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::common::ExportResult; +using opentelemetry::sdk::metrics::AggregationTemporality; +using opentelemetry::sdk::metrics::InstrumentType; +using opentelemetry::sdk::metrics::ScopeMetrics; +using opentelemetry::sdk::resource::Resource; + +class InMemoryMetricExporterTest : public ::testing::Test +{ +protected: + InMemoryMetricExporter exporter_{[](auto) { return AggregationTemporality::kCumulative; }}; + + Resource resource_ = Resource::GetEmpty(); + opentelemetry::sdk::metrics::ResourceMetrics resource_metrics_{ + &resource_, + std::vector{}, + }; +}; + +TEST_F(InMemoryMetricExporterTest, Export) +{ + EXPECT_EQ(exporter_.Export(resource_metrics_), ExportResult::kSuccess); + + auto &data = exporter_.GetData(); + EXPECT_EQ(data.size(), 1); + EXPECT_EQ(data.begin()->resource_, &resource_); +} + +TEST_F(InMemoryMetricExporterTest, ForceFlush) +{ + EXPECT_TRUE(exporter_.ForceFlush()); +} + +TEST_F(InMemoryMetricExporterTest, Shutdown) +{ + EXPECT_TRUE(exporter_.Shutdown()); + EXPECT_EQ(exporter_.Export(resource_metrics_), ExportResult::kFailure); +} + +TEST_F(InMemoryMetricExporterTest, TemporalitySelector) +{ + EXPECT_EQ(exporter_.GetAggregationTemporality(InstrumentType::kCounter), + AggregationTemporality::kCumulative); +}