Skip to content

Commit

Permalink
Implement in-memory metric exporter
Browse files Browse the repository at this point in the history
Fixes #1405
  • Loading branch information
punya committed Sep 3, 2024
1 parent 7f785b5 commit 3acf07c
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
46 changes: 45 additions & 1 deletion exporters/memory/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,58 @@
package(default_visibility = ["//visibility:public"])

cc_library(
name = "in_memory_span_data",
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 = [
":in_memory_data",
"//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_data",
hdrs = [
"include/opentelemetry/exporters/memory/in_memory_data.h",
],
strip_include_prefix = "include",
tags = ["memory"],
deps = [
"//sdk:headers",
],
)

cc_library(
name = "in_memory_span_data",
hdrs = [
"include/opentelemetry/exporters/memory/in_memory_span_data.h",
],
strip_include_prefix = "include",
tags = ["memory"],
deps = [
":in_memory_data",
"//api",
"//sdk/src/resource",
"//sdk/src/trace",
Expand Down
27 changes: 27 additions & 0 deletions exporters/memory/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:include>")

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}
Expand All @@ -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}
Expand All @@ -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.
Expand All @@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <atomic>
#include <vector>

#include "opentelemetry/exporters/memory/in_memory_data.h"
#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
{

using InMemoryMetricData = InMemoryData<sdk::metrics::ResourceMetrics>;

/// 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<sdk::metrics::InstrumentType, sdk::metrics::AggregationTemporality>;

public:
/// @param buffer_size a required value that sets the size of the CircularBuffer
/// @param temporality Output temporality as a function of instrument kind.
InMemoryMetricExporter(size_t buffer_size,
const sdk::metrics::AggregationTemporalitySelector &temporality)
: data_(std::make_shared<InMemoryMetricData>(buffer_size)), 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::shared_ptr<InMemoryMetricData> &GetData() const;

private:
std::shared_ptr<InMemoryMetricData> data_;
std::atomic<bool> is_shutdown_{false};
sdk::metrics::AggregationTemporalitySelector temporality_;
};

/// A factory for InMemoryMetricExporter
class InMemoryMetricExporterFactory
{
public:
/// Create a InMemorySpanExporter with a default buffer size and aggregation
/// temporality selector.
/// @param [out] data the InMemorySpanData the exporter will write to,
/// for the caller to inspect
/// @param [in] buffer_size number of entries to save in the circular buffer
/// @param [in] temporality output temporality as a function of instrument kind
static std::unique_ptr<opentelemetry::sdk::metrics::PushMetricExporter> Create(
std::shared_ptr<InMemoryMetricData> &data,
size_t buffer_size = 100,
const sdk::metrics::AggregationTemporalitySelector &temporality = [](auto) {
return sdk::metrics::AggregationTemporality::kCumulative;
});
};

} // namespace memory
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
65 changes: 65 additions & 0 deletions exporters/memory/src/in_memory_metric_exporter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 opentelemetry::sdk::metrics::PushMetricExporter;
using sdk::common::ExportResult;
using sdk::metrics::AggregationTemporality;
using sdk::metrics::AggregationTemporalitySelector;
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_->Add(std::make_unique<ResourceMetrics>(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::shared_ptr<InMemoryMetricData> &InMemoryMetricExporter::GetData() const
{
return data_;
}

std::unique_ptr<PushMetricExporter> InMemoryMetricExporterFactory::Create(
std::shared_ptr<InMemoryMetricData> &data,
size_t buffer_size,
const AggregationTemporalitySelector &temporality)
{
auto result = std::make_unique<InMemoryMetricExporter>(buffer_size, temporality);
data = result->GetData();
return result;
}

} // namespace memory
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
58 changes: 58 additions & 0 deletions exporters/memory/test/in_memory_metric_exporter_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 <gtest/gtest.h>

using opentelemetry::exporter::memory::InMemoryMetricData;
using opentelemetry::exporter::memory::InMemoryMetricExporter;
using opentelemetry::exporter::memory::InMemoryMetricExporterFactory;
using opentelemetry::sdk::common::ExportResult;
using opentelemetry::sdk::metrics::AggregationTemporality;
using opentelemetry::sdk::metrics::InstrumentType;
using opentelemetry::sdk::metrics::PushMetricExporter;
using opentelemetry::sdk::metrics::ResourceMetrics;
using opentelemetry::sdk::metrics::ScopeMetrics;
using opentelemetry::sdk::resource::Resource;

class InMemoryMetricExporterTest : public ::testing::Test
{
protected:
InMemoryMetricExporterTest() { exporter_ = InMemoryMetricExporterFactory::Create(data_); }

std::unique_ptr<PushMetricExporter> exporter_;
std::shared_ptr<InMemoryMetricData> data_;

Resource resource_ = Resource::GetEmpty();
ResourceMetrics resource_metrics_{&resource_, std::vector<ScopeMetrics>{}};
};

TEST_F(InMemoryMetricExporterTest, Export)
{
EXPECT_EQ(exporter_->Export(resource_metrics_), ExportResult::kSuccess);

auto data = data_->Get();
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);
}

0 comments on commit 3acf07c

Please sign in to comment.