From 0aebd6e84107d166d952148cbc2bafc9e9b2c4dc Mon Sep 17 00:00:00 2001 From: WenTao Ou Date: Mon, 16 May 2022 23:26:41 +0800 Subject: [PATCH] Merge main into async changes (#1395) --- .github/.codecov.yaml | 7 + .github/workflows/codeql-analysis.yml | 6 +- .github/workflows/dependencies_image.yml | 6 +- CMakeLists.txt | 5 +- api/include/opentelemetry/baggage/baggage.h | 22 +- .../opentelemetry/baggage/baggage_context.h | 7 +- .../baggage/propagation/baggage_propagator.h | 16 +- .../opentelemetry/common/kv_properties.h | 22 +- .../opentelemetry/common/string_util.h | 4 +- api/include/opentelemetry/context/context.h | 4 +- .../propagation/composite_propagator.h | 2 + .../opentelemetry/context/runtime_context.h | 2 +- api/include/opentelemetry/trace/context.h | 4 +- .../opentelemetry/trace/default_span.h | 8 +- api/include/opentelemetry/trace/noop.h | 9 +- .../trace/propagation/http_trace_context.h | 6 +- .../opentelemetry/trace/span_context.h | 2 +- api/include/opentelemetry/trace/trace_state.h | 9 +- .../opentelemetry/trace/tracer_provider.h | 2 +- .../propagation/baggage_propagator_test.cc | 29 ++ .../propagation/http_text_format_test.cc | 8 +- api/test/trace/provider_test.cc | 2 +- ci/do_ci.sh | 4 +- examples/CMakeLists.txt | 3 + examples/metrics_simple/metrics_ostream.cc | 6 +- examples/prometheus/BUILD | 14 + examples/prometheus/CMakeLists.txt | 5 + examples/prometheus/main.cc | 118 +++++++ examples/prometheus/prometheus.yml | 16 + examples/prometheus/run.sh | 1 + exporters/CMakeLists.txt | 2 +- exporters/elasticsearch/CMakeLists.txt | 3 +- .../opentelemetry/exporters/etw/LICENSE | 23 ++ .../exporters/etw/TraceLoggingDynamic.h | 19 +- .../opentelemetry/exporters/etw/etw_tracer.h | 6 +- .../opentelemetry/exporters/etw/utils.h | 7 +- .../exporters/ostream/metric_exporter.h | 4 +- exporters/ostream/src/metric_exporter.cc | 33 +- exporters/otlp/CMakeLists.txt | 3 +- exporters/otlp/src/otlp_recordable_utils.cc | 18 +- exporters/prometheus/BUILD | 73 ++++- exporters/prometheus/CMakeLists.txt | 99 ++++-- .../exporters/prometheus/collector.h | 87 +++++ .../exporters/prometheus/exporter.h | 126 +++++++ .../exporters/prometheus/exporter_utils.h | 116 +++++++ exporters/prometheus/src/collector.cc | 89 +++++ exporters/prometheus/src/exporter.cc | 100 ++++++ exporters/prometheus/src/exporter_utils.cc | 308 ++++++++++++++++++ exporters/prometheus/test/CMakeLists.txt | 24 +- .../metrics/aggregation/default_aggregation.h | 48 +++ .../metrics/aggregation/drop_aggregation.h | 6 +- .../aggregation/histogram_aggregation.h | 10 +- .../aggregation/lastvalue_aggregation.h | 6 +- .../sdk/metrics/aggregation/sum_aggregation.h | 10 +- .../sdk/metrics/aggregator/aggregator.h | 24 -- .../opentelemetry/sdk/metrics/meter_context.h | 18 +- .../sdk/metrics/meter_provider.h | 13 - .../sdk/metrics/metric_exporter.h | 2 +- .../opentelemetry/sdk/metrics/recordable.h | 26 -- .../sdk/metrics/state/async_metric_storage.h | 40 ++- .../sdk/metrics/state/sync_metric_storage.h | 16 +- .../metrics/state/temporal_metric_storage.h | 50 +++ .../opentelemetry/sdk/metrics/view/view.h | 2 +- .../sdk/metrics/view/view_registry.h | 2 +- sdk/src/metrics/CMakeLists.txt | 1 + .../aggregation/histogram_aggregation.cc | 8 + .../aggregation/lastvalue_aggregation.cc | 10 + .../metrics/aggregation/sum_aggregation.cc | 4 + sdk/src/metrics/meter.cc | 16 +- sdk/src/metrics/meter_context.cc | 60 ++-- sdk/src/metrics/meter_provider.cc | 11 +- sdk/src/metrics/state/sync_metric_storage.cc | 99 +----- .../metrics/state/temporal_metric_storage.cc | 131 ++++++++ sdk/test/metrics/async_metric_storage_test.cc | 116 +++++-- sdk/test/metrics/meter_provider_sdk_test.cc | 14 +- sdk/test/metrics/metric_reader_test.cc | 5 +- sdk/test/metrics/view_registry_test.cc | 16 +- 77 files changed, 1795 insertions(+), 458 deletions(-) create mode 100644 examples/prometheus/BUILD create mode 100644 examples/prometheus/CMakeLists.txt create mode 100644 examples/prometheus/main.cc create mode 100644 examples/prometheus/prometheus.yml create mode 100644 examples/prometheus/run.sh create mode 100644 exporters/etw/include/opentelemetry/exporters/etw/LICENSE create mode 100644 exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h create mode 100644 exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h create mode 100644 exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h create mode 100644 exporters/prometheus/src/collector.cc create mode 100644 exporters/prometheus/src/exporter.cc create mode 100644 exporters/prometheus/src/exporter_utils.cc delete mode 100644 sdk/include/opentelemetry/sdk/metrics/aggregator/aggregator.h delete mode 100644 sdk/include/opentelemetry/sdk/metrics/recordable.h create mode 100644 sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h create mode 100644 sdk/src/metrics/state/temporal_metric_storage.cc diff --git a/.github/.codecov.yaml b/.github/.codecov.yaml index 2b3003ff57..62d96e70f1 100644 --- a/.github/.codecov.yaml +++ b/.github/.codecov.yaml @@ -33,3 +33,10 @@ comment: # to coverage report file paths. fixes: - "/home/runner/::" + +ignore: + - "docs/**/*" + - "docker/**/*" + - "examples/**/*" + - "**/test/**/*" + - "**.md" \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b40b8e5a73..000a0eb0d0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,10 +23,10 @@ jobs: sudo ./ci/setup_cmake.sh sudo ./ci/setup_ci_environment.sh - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: cpp - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/dependencies_image.yml b/.github/workflows/dependencies_image.yml index aa7d923071..3a55a0b4e8 100644 --- a/.github/workflows/dependencies_image.yml +++ b/.github/workflows/dependencies_image.yml @@ -14,14 +14,14 @@ jobs: uses: actions/checkout@v2 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Build Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: builder: ${{ steps.buildx.outputs.name }} context: ci/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 128b7bf132..fc4e2b0a66 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,7 +275,10 @@ if(WITH_PROMETHEUS) if(NOT prometheus-cpp_FOUND) message("Trying to use local prometheus-cpp from submodule") if(EXISTS ${PROJECT_SOURCE_DIR}/third_party/prometheus-cpp/.git) + set(SAVED_ENABLE_TESTING ${ENABLE_TESTING}) + set(ENABLE_TESTING OFF) add_subdirectory(third_party/prometheus-cpp) + set(ENABLE_TESTING ${SAVED_ENABLE_TESTING}) else() message( FATAL_ERROR @@ -362,7 +365,7 @@ if(BUILD_TESTING) ${CMAKE_BINARY_DIR}/lib/libgmock.a) elseif(WIN32) # Make sure we are always bootsrapped with vcpkg on Windows - find_package(GTest) + find_package(GTest REQUIRED) if(NOT (GTEST_FOUND OR GTest_FOUND)) install_windows_deps() find_package(GTest REQUIRED) diff --git a/api/include/opentelemetry/baggage/baggage.h b/api/include/opentelemetry/baggage/baggage.h index 7f904f09a7..eb5e4dcc7c 100644 --- a/api/include/opentelemetry/baggage/baggage.h +++ b/api/include/opentelemetry/baggage/baggage.h @@ -25,11 +25,12 @@ class Baggage static constexpr char kMembersSeparator = ','; static constexpr char kMetadataSeparator = ';'; - Baggage() : kv_properties_(new opentelemetry::common::KeyValueProperties()) {} - Baggage(size_t size) : kv_properties_(new opentelemetry::common::KeyValueProperties(size)){}; + Baggage() noexcept : kv_properties_(new opentelemetry::common::KeyValueProperties()) {} + Baggage(size_t size) noexcept + : kv_properties_(new opentelemetry::common::KeyValueProperties(size)){}; template - Baggage(const T &keys_and_values) + Baggage(const T &keys_and_values) noexcept : kv_properties_(new opentelemetry::common::KeyValueProperties(keys_and_values)) {} @@ -42,7 +43,7 @@ class Baggage /* Get value for key in the baggage @returns true if key is found, false otherwise */ - bool GetValue(nostd::string_view key, std::string &value) const + bool GetValue(nostd::string_view key, std::string &value) const noexcept { return kv_properties_->GetValue(key, value); } @@ -50,7 +51,8 @@ class Baggage /* Returns shared_ptr of new baggage object which contains new key-value pair. If key or value is invalid, copy of current baggage is returned */ - nostd::shared_ptr Set(const nostd::string_view &key, const nostd::string_view &value) + nostd::shared_ptr Set(const nostd::string_view &key, + const nostd::string_view &value) noexcept { nostd::shared_ptr baggage(new Baggage(kv_properties_->Size() + 1)); @@ -89,7 +91,7 @@ class Baggage // if key does not exist, copy of current baggage is returned. // Validity of key is not checked as invalid keys should never be populated in baggage in the // first place. - nostd::shared_ptr Delete(nostd::string_view key) + nostd::shared_ptr Delete(nostd::string_view key) noexcept { // keeping size of baggage same as key might not be found in it nostd::shared_ptr baggage(new Baggage(kv_properties_->Size())); @@ -103,7 +105,7 @@ class Baggage } // Returns shared_ptr of baggage after extracting key-value pairs from header - static nostd::shared_ptr FromHeader(nostd::string_view header) + static nostd::shared_ptr FromHeader(nostd::string_view header) noexcept { if (header.size() > kMaxSize) { @@ -158,7 +160,7 @@ class Baggage } // Creates string from baggage object. - std::string ToHeader() const + std::string ToHeader() const noexcept { std::string header_s; bool first = true; @@ -249,7 +251,9 @@ class Baggage }; auto from_hex = [](char c) -> char { - return std::isdigit(c) ? c - '0' : std::toupper(c) - 'A' + 10; + // c - '0' produces integer type which could trigger error/warning when casting to char, + // but the cast is safe here. + return static_cast(std::isdigit(c) ? c - '0' : std::toupper(c) - 'A' + 10); }; std::string ret; diff --git a/api/include/opentelemetry/baggage/baggage_context.h b/api/include/opentelemetry/baggage/baggage_context.h index b2b61703f8..9a92bac77f 100644 --- a/api/include/opentelemetry/baggage/baggage_context.h +++ b/api/include/opentelemetry/baggage/baggage_context.h @@ -16,7 +16,7 @@ namespace baggage static const std::string kBaggageHeader = "baggage"; inline nostd::shared_ptr GetBaggage( - const opentelemetry::context::Context &context) + const opentelemetry::context::Context &context) noexcept { context::ContextValue context_value = context.GetValue(kBaggageHeader); if (nostd::holds_alternative>(context_value)) @@ -28,8 +28,9 @@ inline nostd::shared_ptr GetBaggage( return empty_baggage; } -inline context::Context SetBaggage(opentelemetry::context::Context &context, - nostd::shared_ptr baggage) +inline context::Context SetBaggage( + opentelemetry::context::Context &context, + nostd::shared_ptr baggage) noexcept { return context.SetValue(kBaggageHeader, baggage); } diff --git a/api/include/opentelemetry/baggage/propagation/baggage_propagator.h b/api/include/opentelemetry/baggage/propagation/baggage_propagator.h index 11abd3107e..3de60860b2 100644 --- a/api/include/opentelemetry/baggage/propagation/baggage_propagator.h +++ b/api/include/opentelemetry/baggage/propagation/baggage_propagator.h @@ -21,7 +21,11 @@ class BaggagePropagator : public opentelemetry::context::propagation::TextMapPro const opentelemetry::context::Context &context) noexcept override { auto baggage = opentelemetry::baggage::GetBaggage(context); - carrier.Set(kBaggageHeader, baggage->ToHeader()); + auto header = baggage->ToHeader(); + if (header.size()) + { + carrier.Set(kBaggageHeader, header); + } } context::Context Extract(const opentelemetry::context::propagation::TextMapCarrier &carrier, @@ -29,7 +33,15 @@ class BaggagePropagator : public opentelemetry::context::propagation::TextMapPro { nostd::string_view baggage_str = carrier.Get(opentelemetry::baggage::kBaggageHeader); auto baggage = opentelemetry::baggage::Baggage::FromHeader(baggage_str); - return opentelemetry::baggage::SetBaggage(context, baggage); + + if (baggage->ToHeader().size()) + { + return opentelemetry::baggage::SetBaggage(context, baggage); + } + else + { + return context; + } } bool Fields(nostd::function_ref callback) const noexcept override diff --git a/api/include/opentelemetry/common/kv_properties.h b/api/include/opentelemetry/common/kv_properties.h index 799fd293c7..7ac747a733 100644 --- a/api/include/opentelemetry/common/kv_properties.h +++ b/api/include/opentelemetry/common/kv_properties.h @@ -33,7 +33,7 @@ class KeyValueStringTokenizer public: KeyValueStringTokenizer( nostd::string_view str, - const KeyValueStringTokenizerOptions &opts = KeyValueStringTokenizerOptions()) + const KeyValueStringTokenizerOptions &opts = KeyValueStringTokenizerOptions()) noexcept : str_(str), opts_(opts), index_(0) {} @@ -48,7 +48,7 @@ class KeyValueStringTokenizer // @param key : key in kv pair // @param key : value in kv pair // @returns true if next kv pair was found, false otherwise. - bool next(bool &valid_kv, nostd::string_view &key, nostd::string_view &value) + bool next(bool &valid_kv, nostd::string_view &key, nostd::string_view &value) noexcept { valid_kv = true; while (index_ < str_.size()) @@ -170,13 +170,13 @@ class KeyValueProperties } // Gets the key associated with this entry. - nostd::string_view GetKey() const { return key_.get(); } + nostd::string_view GetKey() const noexcept { return key_.get(); } // Gets the value associated with this entry. - nostd::string_view GetValue() const { return value_.get(); } + nostd::string_view GetValue() const noexcept { return value_.get(); } // Sets the value for this entry. This overrides the previous value. - void SetValue(nostd::string_view value) { value_ = CopyStringToPointer(value); } + void SetValue(nostd::string_view value) noexcept { value_ = CopyStringToPointer(value); } private: // Store key and value as raw char pointers to avoid using std::string. @@ -206,15 +206,15 @@ class KeyValueProperties public: // Create Key-value list of given size // @param size : Size of list. - KeyValueProperties(size_t size) + KeyValueProperties(size_t size) noexcept : num_entries_(0), max_num_entries_(size), entries_(new Entry[size]) {} // Create Empty Key-Value list - KeyValueProperties() : num_entries_(0), max_num_entries_(0), entries_(nullptr) {} + KeyValueProperties() noexcept : num_entries_(0), max_num_entries_(0), entries_(nullptr) {} template ::value>::type> - KeyValueProperties(const T &keys_and_values) + KeyValueProperties(const T &keys_and_values) noexcept : num_entries_(0), max_num_entries_(keys_and_values.size()), entries_(new Entry[max_num_entries_]) @@ -227,7 +227,7 @@ class KeyValueProperties } // Adds new kv pair into kv properties - void AddEntry(nostd::string_view key, nostd::string_view value) + void AddEntry(nostd::string_view key, nostd::string_view value) noexcept { if (num_entries_ < max_num_entries_) { @@ -238,7 +238,7 @@ class KeyValueProperties // Returns all kv pair entries bool GetAllEntries( - nostd::function_ref callback) const + nostd::function_ref callback) const noexcept { for (size_t i = 0; i < num_entries_; i++) { @@ -252,7 +252,7 @@ class KeyValueProperties } // Return value for key if exists, return false otherwise - bool GetValue(nostd::string_view key, std::string &value) const + bool GetValue(nostd::string_view key, std::string &value) const noexcept { for (size_t i = 0; i < num_entries_; i++) { diff --git a/api/include/opentelemetry/common/string_util.h b/api/include/opentelemetry/common/string_util.h index 00f80db992..ffee86c96b 100644 --- a/api/include/opentelemetry/common/string_util.h +++ b/api/include/opentelemetry/common/string_util.h @@ -26,7 +26,7 @@ namespace common class StringUtil { public: - static nostd::string_view Trim(nostd::string_view str, size_t left, size_t right) + static nostd::string_view Trim(nostd::string_view str, size_t left, size_t right) noexcept { while (str[static_cast(left)] == ' ' && left <= right) { @@ -39,7 +39,7 @@ class StringUtil return str.substr(left, 1 + right - left); } - static nostd::string_view Trim(nostd::string_view str) + static nostd::string_view Trim(nostd::string_view str) noexcept { if (str.empty()) { diff --git a/api/include/opentelemetry/context/context.h b/api/include/opentelemetry/context/context.h index 50cfde3c4f..923b396c7f 100644 --- a/api/include/opentelemetry/context/context.h +++ b/api/include/opentelemetry/context/context.h @@ -24,14 +24,14 @@ class Context // Creates a context object from a map of keys and identifiers, this will // hold a shared_ptr to the head of the DataList linked list template - Context(const T &keys_and_values) + Context(const T &keys_and_values) noexcept { head_ = nostd::shared_ptr{new DataList(keys_and_values)}; } // Creates a context object from a key and value, this will // hold a shared_ptr to the head of the DataList linked list - Context(nostd::string_view key, ContextValue value) + Context(nostd::string_view key, ContextValue value) noexcept { head_ = nostd::shared_ptr{new DataList(key, value)}; } diff --git a/api/include/opentelemetry/context/propagation/composite_propagator.h b/api/include/opentelemetry/context/propagation/composite_propagator.h index 85ad97b0fc..d7a6cbda17 100644 --- a/api/include/opentelemetry/context/propagation/composite_propagator.h +++ b/api/include/opentelemetry/context/propagation/composite_propagator.h @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#pragma once + #include #include #include diff --git a/api/include/opentelemetry/context/runtime_context.h b/api/include/opentelemetry/context/runtime_context.h index 74fa2151ea..167a928f10 100644 --- a/api/include/opentelemetry/context/runtime_context.h +++ b/api/include/opentelemetry/context/runtime_context.h @@ -173,7 +173,7 @@ class RuntimeContext } }; -inline Token::~Token() +inline Token::~Token() noexcept { context::RuntimeContext::Detach(*this); } diff --git a/api/include/opentelemetry/trace/context.h b/api/include/opentelemetry/trace/context.h index 165f1540b9..963c187177 100644 --- a/api/include/opentelemetry/trace/context.h +++ b/api/include/opentelemetry/trace/context.h @@ -12,7 +12,7 @@ namespace trace { // Get Span from explicit context -inline nostd::shared_ptr GetSpan(const opentelemetry::context::Context &context) +inline nostd::shared_ptr GetSpan(const opentelemetry::context::Context &context) noexcept { context::ContextValue span = context.GetValue(kSpanKey); if (nostd::holds_alternative>(span)) @@ -24,7 +24,7 @@ inline nostd::shared_ptr GetSpan(const opentelemetry::context::Context &co // Set Span into explicit context inline context::Context SetSpan(opentelemetry::context::Context &context, - nostd::shared_ptr span) + nostd::shared_ptr span) noexcept { return context.SetValue(kSpanKey, span); } diff --git a/api/include/opentelemetry/trace/default_span.h b/api/include/opentelemetry/trace/default_span.h index 6755704291..fadd92386f 100644 --- a/api/include/opentelemetry/trace/default_span.h +++ b/api/include/opentelemetry/trace/default_span.h @@ -50,13 +50,13 @@ class DefaultSpan : public Span void End(const EndSpanOptions & /* options */ = {}) noexcept {} - nostd::string_view ToString() { return "DefaultSpan"; } + nostd::string_view ToString() const noexcept { return "DefaultSpan"; } - DefaultSpan(SpanContext span_context) : span_context_(span_context) {} + DefaultSpan(SpanContext span_context) noexcept : span_context_(span_context) {} // movable and copiable - DefaultSpan(DefaultSpan &&spn) : span_context_(spn.GetContext()) {} - DefaultSpan(const DefaultSpan &spn) : span_context_(spn.GetContext()) {} + DefaultSpan(DefaultSpan &&spn) noexcept : span_context_(spn.GetContext()) {} + DefaultSpan(const DefaultSpan &spn) noexcept : span_context_(spn.GetContext()) {} private: SpanContext span_context_; diff --git a/api/include/opentelemetry/trace/noop.h b/api/include/opentelemetry/trace/noop.h index 1257ebd279..e7784c4be6 100644 --- a/api/include/opentelemetry/trace/noop.h +++ b/api/include/opentelemetry/trace/noop.h @@ -100,14 +100,15 @@ class NoopTracer final : public Tracer, public std::enable_shared_from_this( new opentelemetry::trace::NoopTracer)} {} - nostd::shared_ptr GetTracer(nostd::string_view library_name, - nostd::string_view library_version, - nostd::string_view schema_url) override + nostd::shared_ptr GetTracer( + nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept override { return tracer_; } diff --git a/api/include/opentelemetry/trace/propagation/http_trace_context.h b/api/include/opentelemetry/trace/propagation/http_trace_context.h index 91905b794e..9440960be3 100644 --- a/api/include/opentelemetry/trace/propagation/http_trace_context.h +++ b/api/include/opentelemetry/trace/propagation/http_trace_context.h @@ -100,7 +100,11 @@ class HttpTraceContext : public opentelemetry::context::propagation::TextMapProp nostd::span{&trace_parent[kTraceIdSize + kSpanIdSize + 5], 2}); carrier.Set(kTraceParent, nostd::string_view(trace_parent, sizeof(trace_parent))); - carrier.Set(kTraceState, span_context.trace_state()->ToHeader()); + const auto trace_state = span_context.trace_state()->ToHeader(); + if (!trace_state.empty()) + { + carrier.Set(kTraceState, trace_state); + } } static SpanContext ExtractContextFromTraceHeaders(nostd::string_view trace_parent, diff --git a/api/include/opentelemetry/trace/span_context.h b/api/include/opentelemetry/trace/span_context.h index 569a3c1e71..ca13a8df60 100644 --- a/api/include/opentelemetry/trace/span_context.h +++ b/api/include/opentelemetry/trace/span_context.h @@ -26,7 +26,7 @@ class SpanContext final * sampled * @param is_remote true if this context was propagated from a remote parent. */ - SpanContext(bool sampled_flag, bool is_remote) + SpanContext(bool sampled_flag, bool is_remote) noexcept : trace_id_(), span_id_(), trace_flags_(opentelemetry::trace::TraceFlags((uint8_t)sampled_flag)), diff --git a/api/include/opentelemetry/trace/trace_state.h b/api/include/opentelemetry/trace/trace_state.h index 1dc0457356..0343637cfa 100644 --- a/api/include/opentelemetry/trace/trace_state.h +++ b/api/include/opentelemetry/trace/trace_state.h @@ -53,7 +53,7 @@ class TraceState * the W3C Trace Context specification https://www.w3.org/TR/trace-context/ * @return TraceState A new TraceState instance or DEFAULT */ - static nostd::shared_ptr FromHeader(nostd::string_view header) + static nostd::shared_ptr FromHeader(nostd::string_view header) noexcept { common::KeyValueStringTokenizer kv_str_tokenizer(header); @@ -89,7 +89,7 @@ class TraceState /** * Creates a w3c tracestate header from TraceState object */ - std::string ToHeader() + std::string ToHeader() const noexcept { std::string header_s; bool first = true; @@ -135,7 +135,8 @@ class TraceState * * If the existing object has maximum list members, it's copy is returned. */ - nostd::shared_ptr Set(const nostd::string_view &key, const nostd::string_view &value) + nostd::shared_ptr Set(const nostd::string_view &key, + const nostd::string_view &value) noexcept { auto curr_size = kv_properties_->Size(); if (!IsValidKey(key) || !IsValidValue(value)) @@ -168,7 +169,7 @@ class TraceState * @returns empty TraceState object if key is invalid * @returns copy of original TraceState object if key is not present (??) */ - nostd::shared_ptr Delete(const nostd::string_view &key) + nostd::shared_ptr Delete(const nostd::string_view &key) noexcept { if (!IsValidKey(key)) { diff --git a/api/include/opentelemetry/trace/tracer_provider.h b/api/include/opentelemetry/trace/tracer_provider.h index 540a2f6b24..2dae74ce14 100644 --- a/api/include/opentelemetry/trace/tracer_provider.h +++ b/api/include/opentelemetry/trace/tracer_provider.h @@ -25,7 +25,7 @@ class TracerProvider */ virtual nostd::shared_ptr GetTracer(nostd::string_view library_name, nostd::string_view library_version = "", - nostd::string_view schema_url = "") = 0; + nostd::string_view schema_url = "") noexcept = 0; }; } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/baggage/propagation/baggage_propagator_test.cc b/api/test/baggage/propagation/baggage_propagator_test.cc index 0f1f8f338c..94fe8b005b 100644 --- a/api/test/baggage/propagation/baggage_propagator_test.cc +++ b/api/test/baggage/propagation/baggage_propagator_test.cc @@ -82,3 +82,32 @@ TEST(BaggagePropagatorTest, ExtractAndInjectBaggage) EXPECT_EQ(fields[0], baggage::kBaggageHeader.data()); } } + +TEST(BaggagePropagatorTest, InjectEmptyHeader) +{ + // Test Missing baggage from context + BaggageCarrierTest carrier; + context::Context ctx = context::Context{}; + format.Inject(carrier, ctx); + EXPECT_EQ(carrier.headers_.find(baggage::kBaggageHeader), carrier.headers_.end()); + + { + // Test empty baggage in context + BaggageCarrierTest carrier1; + carrier1.headers_[baggage::kBaggageHeader.data()] = ""; + context::Context ctx1 = context::Context{}; + context::Context ctx2 = format.Extract(carrier1, ctx1); + format.Inject(carrier, ctx2); + EXPECT_EQ(carrier.headers_.find(baggage::kBaggageHeader), carrier.headers_.end()); + } + { + // Invalid baggage in context + BaggageCarrierTest carrier1; + carrier1.headers_[baggage::kBaggageHeader.data()] = "InvalidBaggageData"; + context::Context ctx1 = context::Context{}; + context::Context ctx2 = format.Extract(carrier1, ctx1); + + format.Inject(carrier, ctx2); + EXPECT_EQ(carrier.headers_.find(baggage::kBaggageHeader), carrier.headers_.end()); + } +} diff --git a/api/test/trace/propagation/http_text_format_test.cc b/api/test/trace/propagation/http_text_format_test.cc index d42d508860..8fa0e44ed2 100644 --- a/api/test/trace/propagation/http_text_format_test.cc +++ b/api/test/trace/propagation/http_text_format_test.cc @@ -55,8 +55,8 @@ TEST(TextMapPropagatorTest, NoSendEmptyTraceState) context::Context ctx2 = format.Extract(carrier, ctx1); TextMapCarrierTest carrier2; format.Inject(carrier2, ctx2); - EXPECT_TRUE(carrier.headers_.count("traceparent") > 0); - EXPECT_FALSE(carrier.headers_.count("tracestate") > 0); + EXPECT_TRUE(carrier2.headers_.count("traceparent") > 0); + EXPECT_FALSE(carrier2.headers_.count("tracestate") > 0); } TEST(TextMapPropagatorTest, PropogateTraceState) @@ -72,8 +72,8 @@ TEST(TextMapPropagatorTest, PropogateTraceState) TextMapCarrierTest carrier2; format.Inject(carrier2, ctx2); - EXPECT_TRUE(carrier.headers_.count("traceparent") > 0); - EXPECT_TRUE(carrier.headers_.count("tracestate") > 0); + EXPECT_TRUE(carrier2.headers_.count("traceparent") > 0); + EXPECT_TRUE(carrier2.headers_.count("tracestate") > 0); EXPECT_EQ(carrier2.headers_["tracestate"], "congo=t61rcWkgMzE"); } diff --git a/api/test/trace/provider_test.cc b/api/test/trace/provider_test.cc index 3c4bd1e74e..9e5a7aad1d 100644 --- a/api/test/trace/provider_test.cc +++ b/api/test/trace/provider_test.cc @@ -16,7 +16,7 @@ class TestProvider : public TracerProvider { nostd::shared_ptr GetTracer(nostd::string_view library_name, nostd::string_view library_version, - nostd::string_view schema_url) override + nostd::string_view schema_url) noexcept override { return nostd::shared_ptr(nullptr); } diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 2fa202af89..5258dfe74e 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -235,8 +235,8 @@ elif [[ "$1" == "bazel.legacy.test" ]]; then elif [[ "$1" == "bazel.noexcept" ]]; then # there are some exceptions and error handling code from the Prometheus and Jaeger Clients # that make this test always fail. ignore Prometheus and Jaeger exporters in the noexcept here. - bazel $BAZEL_STARTUP_OPTIONS build --copt=-fno-exceptions --build_tag_filters=-jaeger $BAZEL_OPTIONS_ASYNC -- //... -//exporters/prometheus/... -//exporters/jaeger/... - bazel $BAZEL_STARTUP_OPTIONS test --copt=-fno-exceptions --build_tag_filters=-jaeger $BAZEL_TEST_OPTIONS_ASYNC -- //... -//exporters/prometheus/... -//exporters/jaeger/... + bazel $BAZEL_STARTUP_OPTIONS build --copt=-fno-exceptions --build_tag_filters=-jaeger $BAZEL_OPTIONS_ASYNC -- //... -//exporters/prometheus/... -//exporters/jaeger/... -//examples/prometheus/... + bazel $BAZEL_STARTUP_OPTIONS test --copt=-fno-exceptions --build_tag_filters=-jaeger $BAZEL_TEST_OPTIONS_ASYNC -- //... -//exporters/prometheus/... -//exporters/jaeger/... -//examples/prometheus/... exit 0 elif [[ "$1" == "bazel.nortti" ]]; then # there are some exceptions and error handling code from the Prometheus and Jaeger Clients diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index db0fe159be..2367918c7e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,6 +15,9 @@ endif() if(WITH_ZIPKIN) add_subdirectory(zipkin) endif() +if(WITH_PROMETHEUS AND NOT WITH_METRICS_PREVIEW) + add_subdirectory(prometheus) +endif() add_subdirectory(plugin) add_subdirectory(simple) add_subdirectory(batch) diff --git a/examples/metrics_simple/metrics_ostream.cc b/examples/metrics_simple/metrics_ostream.cc index d0e4dccfeb..ebb8b34b8a 100644 --- a/examples/metrics_simple/metrics_ostream.cc +++ b/examples/metrics_simple/metrics_ostream.cc @@ -30,7 +30,6 @@ namespace void initMetrics(const std::string &name) { std::unique_ptr exporter{new exportermetrics::OStreamMetricExporter}; - std::vector> exporters; std::string version{"1.2.0"}; std::string schema{"https://opentelemetry.io/schemas/1.2.0"}; @@ -41,9 +40,8 @@ void initMetrics(const std::string &name) options.export_timeout_millis = std::chrono::milliseconds(500); std::unique_ptr reader{ new metric_sdk::PeriodicExportingMetricReader(std::move(exporter), options)}; - auto provider = std::shared_ptr( - new metric_sdk::MeterProvider(std::move(exporters))); - auto p = std::static_pointer_cast(provider); + auto provider = std::shared_ptr(new metric_sdk::MeterProvider()); + auto p = std::static_pointer_cast(provider); p->AddMetricReader(std::move(reader)); // counter view diff --git a/examples/prometheus/BUILD b/examples/prometheus/BUILD new file mode 100644 index 0000000000..edbfde61e6 --- /dev/null +++ b/examples/prometheus/BUILD @@ -0,0 +1,14 @@ +cc_binary( + name = "prometheus_example", + srcs = [ + "main.cc", + ], + linkopts = ["-pthread"], + tags = ["ostream"], + deps = [ + "//api", + "//examples/common/metrics_foo_library:common_metrics_foo_library", + "//exporters/prometheus:prometheus_exporter", + "//sdk/src/metrics", + ], +) diff --git a/examples/prometheus/CMakeLists.txt b/examples/prometheus/CMakeLists.txt new file mode 100644 index 0000000000..b377920dee --- /dev/null +++ b/examples/prometheus/CMakeLists.txt @@ -0,0 +1,5 @@ +include_directories(${CMAKE_SOURCE_DIR}/exporters/prometheus/include) +add_executable(prometheus_example main.cc) +target_link_libraries( + prometheus_example ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics + prometheus_exporter opentelemetry_resources common_metrics_foo_library) diff --git a/examples/prometheus/main.cc b/examples/prometheus/main.cc new file mode 100644 index 0000000000..77a648f55d --- /dev/null +++ b/examples/prometheus/main.cc @@ -0,0 +1,118 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include "opentelemetry/exporters/prometheus/exporter.h" +# include "opentelemetry/metrics/provider.h" +# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" +# include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h" +# include "opentelemetry/sdk/metrics/meter.h" +# include "opentelemetry/sdk/metrics/meter_provider.h" + +# ifdef BAZEL_BUILD +# include "examples/common/metrics_foo_library/foo_library.h" +# else +# include "metrics_foo_library/foo_library.h" +# endif + +namespace metrics_sdk = opentelemetry::sdk::metrics; +namespace nostd = opentelemetry::nostd; +namespace common = opentelemetry::common; +namespace metrics_exporter = opentelemetry::exporter::metrics; +namespace metrics_api = opentelemetry::metrics; + +namespace +{ + +void initMetrics(const std::string &name, const std::string &addr) +{ + metrics_exporter::PrometheusExporterOptions opts; + if (!addr.empty()) + { + opts.url = addr; + } + std::puts("PrometheusExporter example program running ..."); + + std::unique_ptr exporter{ + new metrics_exporter::PrometheusExporter(opts)}; + + std::string version{"1.2.0"}; + std::string schema{"https://opentelemetry.io/schemas/1.2.0"}; + + // Initialize and set the global MeterProvider + metrics_sdk::PeriodicExportingMetricReaderOptions options; + options.export_interval_millis = std::chrono::milliseconds(1000); + options.export_timeout_millis = std::chrono::milliseconds(500); + std::unique_ptr reader{ + new metrics_sdk::PeriodicExportingMetricReader(std::move(exporter), options)}; + auto provider = std::shared_ptr(new metrics_sdk::MeterProvider()); + auto p = std::static_pointer_cast(provider); + p->AddMetricReader(std::move(reader)); + + // counter view + std::string counter_name = name + "_counter"; + std::unique_ptr instrument_selector{ + new metrics_sdk::InstrumentSelector(metrics_sdk::InstrumentType::kCounter, counter_name)}; + std::unique_ptr meter_selector{ + new metrics_sdk::MeterSelector(name, version, schema)}; + std::unique_ptr sum_view{ + new metrics_sdk::View{name, "description", metrics_sdk::AggregationType::kSum}}; + p->AddView(std::move(instrument_selector), std::move(meter_selector), std::move(sum_view)); + + // histogram view + std::string histogram_name = name + "_histogram"; + std::unique_ptr histogram_instrument_selector{ + new metrics_sdk::InstrumentSelector(metrics_sdk::InstrumentType::kHistogram, histogram_name)}; + std::unique_ptr histogram_meter_selector{ + new metrics_sdk::MeterSelector(name, version, schema)}; + std::unique_ptr histogram_view{ + new metrics_sdk::View{name, "description", metrics_sdk::AggregationType::kHistogram}}; + p->AddView(std::move(histogram_instrument_selector), std::move(histogram_meter_selector), + std::move(histogram_view)); + metrics_api::Provider::SetMeterProvider(provider); +} +} // namespace + +int main(int argc, char **argv) +{ + std::string example_type; + std::string addr{"localhost:8080"}; + if (argc == 1) + { + std::puts("usage: $prometheus_example "); + } + + if (argc >= 2) + { + example_type = argv[1]; + } + if (argc > 2) + { + addr = argv[2]; + } + + std::string name{"prometheus_metric_example"}; + initMetrics(name, addr); + + if (example_type == "counter") + { + foo_library::counter_example(name); + } + else if (example_type == "histogram") + { + foo_library::histogram_example(name); + } + else + { + std::thread counter_example{&foo_library::counter_example, name}; + std::thread histogram_example{&foo_library::histogram_example, name}; + counter_example.join(); + histogram_example.join(); + } +} +#else +int main() {} +#endif diff --git a/examples/prometheus/prometheus.yml b/examples/prometheus/prometheus.yml new file mode 100644 index 0000000000..3f415d39dc --- /dev/null +++ b/examples/prometheus/prometheus.yml @@ -0,0 +1,16 @@ +global: + scrape_interval: 5s + scrape_timeout: 2s + evaluation_interval: 5s +alerting: + alertmanagers: + - follow_redirects: true + scheme: http + timeout: 5s + api_version: v2 + static_configs: + - targets: [localhost:8080] +scrape_configs: + - job_name: otel + static_configs: + - targets: ['localhost:8080'] \ No newline at end of file diff --git a/examples/prometheus/run.sh b/examples/prometheus/run.sh new file mode 100644 index 0000000000..412c6ef454 --- /dev/null +++ b/examples/prometheus/run.sh @@ -0,0 +1 @@ +docker run -p 9090:9090 -v $(pwd):/etc/prometheus --network="host" prom/prometheus \ No newline at end of file diff --git a/exporters/CMakeLists.txt b/exporters/CMakeLists.txt index 2ae8fd92bb..862d2c779e 100644 --- a/exporters/CMakeLists.txt +++ b/exporters/CMakeLists.txt @@ -19,7 +19,7 @@ endif() add_subdirectory(ostream) add_subdirectory(memory) -if(WITH_PROMETHEUS AND WITH_METRICS_PREVIEW) +if(WITH_PROMETHEUS) add_subdirectory(prometheus) endif() diff --git a/exporters/elasticsearch/CMakeLists.txt b/exporters/elasticsearch/CMakeLists.txt index b21dae400a..ed6488316c 100644 --- a/exporters/elasticsearch/CMakeLists.txt +++ b/exporters/elasticsearch/CMakeLists.txt @@ -10,7 +10,8 @@ target_include_directories( target_link_libraries( opentelemetry_exporter_elasticsearch_logs - PUBLIC opentelemetry_trace opentelemetry_logs opentelemetry_http_client_curl) + PUBLIC opentelemetry_trace opentelemetry_logs opentelemetry_http_client_curl + nlohmann_json::nlohmann_json) install( TARGETS opentelemetry_exporter_elasticsearch_logs diff --git a/exporters/etw/include/opentelemetry/exporters/etw/LICENSE b/exporters/etw/include/opentelemetry/exporters/etw/LICENSE new file mode 100644 index 0000000000..cfc21fd2cc --- /dev/null +++ b/exporters/etw/include/opentelemetry/exporters/etw/LICENSE @@ -0,0 +1,23 @@ +TraceLogging Dynamic for Windows + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h b/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h index 5d493b319a..17ee108c64 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h @@ -691,7 +691,7 @@ namespace tld } void AddBytes( - _In_bytecount_(cb) void const* p, + _In_reads_bytes_(cb) void const* p, unsigned cb) { auto pb = static_cast(p); @@ -2064,7 +2064,7 @@ namespace tld void AddTrait( ProviderTraitType type, - _In_bytecount_(cbData) void const* pData, + _In_reads_bytes_(cbData) void const* pData, unsigned cbData) { this->AddU16(static_cast(cbData + 3)); @@ -2232,6 +2232,7 @@ namespace tld return this->BaseEnd(); } + // Note: Do not create structs with 0 fields. template EventMetadataBuilder AddStruct( _In_z_ CharTy const* szUtfStructName, @@ -2242,6 +2243,7 @@ namespace tld return EventMetadataBuilder(this->GetBuffer(), bookmark); } + // Note: Do not create structs with 0 fields. template UINT32 AddStructRaw( _In_z_ CharTy const* szUtfStructName, @@ -2252,6 +2254,7 @@ namespace tld return bookmark; } + // Note: Do not create structs with 0 fields. template EventMetadataBuilder AddStructArray( _In_z_ CharTy const* szUtfStructName, @@ -2262,6 +2265,8 @@ namespace tld return EventMetadataBuilder(this->GetBuffer(), bookmark); } + // Note: Do not use 0 for itemCount. + // Note: Do not create structs with 0 fields. template EventMetadataBuilder AddStructFixedArray( _In_z_ CharTy const* szUtfStructName, @@ -2294,6 +2299,7 @@ namespace tld AddFieldInfo(InMetaVcount, type, fieldTags); } + // Note: Do not use 0 for itemCount. template void AddFieldFixedArray( _In_z_ CharTy const* szUtfFieldName, @@ -2400,7 +2406,7 @@ namespace tld Note: should only be used for blittable POD types with no padding. */ template - void AddValues(_In_count_(cValues) T const* pValues, unsigned cValues) + void AddValues(_In_reads_(cValues) T const* pValues, unsigned cValues) { AddBytes(pValues, sizeof(T) * cValues); } @@ -2917,6 +2923,7 @@ namespace tld of the nested struct. Note: do not call any Add methods on this builder object until you are done calling Add methods on the nested builder object. + Note: Do not create structs with 0 fields. */ template EventBuilder AddStruct( @@ -2933,6 +2940,7 @@ namespace tld of the nested struct. Note: do not call any Add methods on this builder object until you are done calling Add methods on the nested builder object. + Note: Do not create structs with 0 fields. */ template EventBuilder AddStructArray( @@ -2949,6 +2957,8 @@ namespace tld of the nested struct. Note: do not call any Add methods on this builder object until you are done calling Add methods on the nested builder object. + Note: Do not use 0 for itemCount. + Note: Do not create structs with 0 fields. */ template EventBuilder AddStructFixedArray( @@ -2992,6 +3002,7 @@ namespace tld Adds a fixed-length array field to the event's metadata. The length (item count) is encoded in the metadata, so it does not need to be included in the event's payload. + Note: Do not use 0 for itemCount. */ template void AddFieldFixedArray( @@ -3061,7 +3072,7 @@ namespace tld e.g. INT32, FILETIME, GUID, not for strings or structs. */ template - void AddValues(_In_count_(cValues) T const* pValues, unsigned cValues) + void AddValues(_In_reads_(cValues) T const* pValues, unsigned cValues) { m_dataBuilder.AddValues(pValues, cValues); } diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 65fe2770f2..5db31a7a75 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -761,10 +761,10 @@ class Span : public opentelemetry::trace::Span const opentelemetry::trace::StartSpanOptions &options, Span *parent = nullptr) noexcept : opentelemetry::trace::Span(), + start_time_(std::chrono::system_clock::now()), owner_(owner), parent_(parent), - context_(CreateContext()), - start_time_(std::chrono::system_clock::now()) + context_(CreateContext()) { name_ = name; UNREFERENCED_PARAMETER(options); @@ -980,7 +980,7 @@ class TracerProvider : public opentelemetry::trace::TracerProvider nostd::shared_ptr GetTracer( nostd::string_view name, nostd::string_view args = "", - nostd::string_view schema_url = "") override + nostd::string_view schema_url = "") noexcept override { UNREFERENCED_PARAMETER(args); UNREFERENCED_PARAMETER(schema_url); diff --git a/exporters/etw/include/opentelemetry/exporters/etw/utils.h b/exporters/etw/include/opentelemetry/exporters/etw/utils.h index 8b0a015821..4d38c1fb6f 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/utils.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/utils.h @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -24,6 +23,8 @@ # pragma comment(lib, "Rpcrt4.lib") # include # pragma comment(lib, "Ole32.Lib") +#else +# include #endif OPENTELEMETRY_BEGIN_NAMESPACE @@ -193,8 +194,8 @@ static inline GUID GetProviderGuid(const char *providerName) guid.Data4[6] = buffer2[14]; guid.Data4[7] = buffer2[15]; - delete buffer; - delete buffer2; + delete[] buffer; + delete[] buffer2; return guid; } diff --git a/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h b/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h index 465ae02bb6..56b6c577b0 100644 --- a/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h +++ b/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h @@ -7,11 +7,9 @@ # include # include # include "opentelemetry/common/spin_lock_mutex.h" -# include "opentelemetry/nostd/span.h" # include "opentelemetry/sdk/metrics/data/metric_data.h" # include "opentelemetry/sdk/metrics/instruments.h" # include "opentelemetry/sdk/metrics/metric_exporter.h" -# include "opentelemetry/sdk/metrics/recordable.h" # include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -28,7 +26,7 @@ class OStreamMetricExporter final : public opentelemetry::sdk::metrics::MetricEx public: /** * Create an OStreamMetricExporter. This constructor takes in a reference to an ostream that the - * export() function will send span data into. + * export() function will send metrics data into. * The default ostream is set to stdout */ explicit OStreamMetricExporter(std::ostream &sout = std::cout) noexcept; diff --git a/exporters/ostream/src/metric_exporter.cc b/exporters/ostream/src/metric_exporter.cc index 5cdd3fe78d..bf97db04c2 100644 --- a/exporters/ostream/src/metric_exporter.cc +++ b/exporters/ostream/src/metric_exporter.cc @@ -14,7 +14,34 @@ namespace std::string timeToString(opentelemetry::common::SystemTimestamp time_stamp) { std::time_t epoch_time = std::chrono::system_clock::to_time_t(time_stamp); - return std::ctime(&epoch_time); + + struct tm *tm_ptr = nullptr; +# if defined(_MSC_VER) + struct tm buf_tm; + if (!gmtime_s(&buf_tm, &epoch_time)) + { + tm_ptr = &buf_tm; + } +# else + tm_ptr = std::gmtime(&epoch_time); +# endif + + char buf[100]; + char *date_str = nullptr; + if (tm_ptr == nullptr) + { + OTEL_INTERNAL_LOG_ERROR("[OStream Metric] gmtime failed for " << epoch_time); + } + else if (std::strftime(buf, sizeof(buf), "%c", tm_ptr) > 0) + { + date_str = buf; + } + else + { + OTEL_INTERNAL_LOG_ERROR("[OStream Metric] strftime failed for " << epoch_time); + } + + return std::string{date_str}; } } // namespace @@ -68,8 +95,8 @@ void OStreamMetricExporter::printInstrumentationInfoMetricData( for (const auto &record : info_metric.metric_data_) { sout_ << "\n start time\t: " << timeToString(record.start_ts) - << " end time\t: " << timeToString(record.end_ts) - << " description\t: " << record.instrument_descriptor.description_ + << "\n end time\t: " << timeToString(record.end_ts) + << "\n description\t: " << record.instrument_descriptor.description_ << "\n unit\t\t: " << record.instrument_descriptor.unit_; for (const auto &pd : record.point_data_attr_) diff --git a/exporters/otlp/CMakeLists.txt b/exporters/otlp/CMakeLists.txt index 1b1b200504..b5dfa1e1ab 100755 --- a/exporters/otlp/CMakeLists.txt +++ b/exporters/otlp/CMakeLists.txt @@ -100,8 +100,7 @@ install( DIRECTORY include/opentelemetry/exporters/otlp DESTINATION include/opentelemetry/exporters FILES_MATCHING - PATTERN "*.h" - PATTERN "otlp_recordable.h" EXCLUDE) + PATTERN "*.h") if(BUILD_TESTING) add_executable(otlp_recordable_test test/otlp_recordable_test.cc) diff --git a/exporters/otlp/src/otlp_recordable_utils.cc b/exporters/otlp/src/otlp_recordable_utils.cc index 483f01f905..b981448de1 100644 --- a/exporters/otlp/src/otlp_recordable_utils.cc +++ b/exporters/otlp/src/otlp_recordable_utils.cc @@ -259,25 +259,19 @@ void OtlpRecordableUtils::PopulateRequest( return; } - auto resource_span = request->add_resource_spans(); - auto instrumentation_lib = resource_span->add_instrumentation_library_spans(); - bool first_pass = true; - for (auto &recordable : spans) { auto rec = std::unique_ptr(static_cast(recordable.release())); + auto resource_span = request->add_resource_spans(); + auto instrumentation_lib = resource_span->add_instrumentation_library_spans(); + *instrumentation_lib->add_spans() = std::move(rec->span()); *instrumentation_lib->mutable_instrumentation_library() = rec->GetProtoInstrumentationLibrary(); - if (first_pass) - { - instrumentation_lib->set_schema_url(rec->GetInstrumentationLibrarySchemaURL()); + instrumentation_lib->set_schema_url(rec->GetInstrumentationLibrarySchemaURL()); - *resource_span->mutable_resource() = rec->ProtoResource(); - resource_span->set_schema_url(rec->GetResourceSchemaURL()); - - first_pass = false; - } + *resource_span->mutable_resource() = rec->ProtoResource(); + resource_span->set_schema_url(rec->GetResourceSchemaURL()); } } diff --git a/exporters/prometheus/BUILD b/exporters/prometheus/BUILD index e92f9412eb..47c9bbab16 100644 --- a/exporters/prometheus/BUILD +++ b/exporters/prometheus/BUILD @@ -15,7 +15,7 @@ package(default_visibility = ["//visibility:public"]) cc_library( - name = "prometheus_exporter", + name = "prometheus_exporter_deprecated", srcs = [ "src/prometheus_exporter.cc", ], @@ -25,8 +25,8 @@ cc_library( strip_include_prefix = "include", tags = ["prometheus"], deps = [ - ":prometheus_collector", - ":prometheus_exporter_utils", + ":prometheus_collector_deprecated", + ":prometheus_exporter_utils_deprecated", "//api", "//sdk:headers", "@com_github_jupp0r_prometheus_cpp//core", @@ -35,7 +35,7 @@ cc_library( ) cc_library( - name = "prometheus_exporter_utils", + name = "prometheus_exporter_utils_deprecated", srcs = [ "src/prometheus_exporter_utils.cc", ], @@ -53,7 +53,7 @@ cc_library( ) cc_library( - name = "prometheus_collector", + name = "prometheus_collector_deprecated", srcs = [ "src/prometheus_collector.cc", ], @@ -63,7 +63,7 @@ cc_library( strip_include_prefix = "include", tags = ["prometheus"], deps = [ - ":prometheus_exporter_utils", + ":prometheus_exporter_utils_deprecated", "//api", "//sdk:headers", "@com_github_jupp0r_prometheus_cpp//core", @@ -72,7 +72,7 @@ cc_library( ) cc_test( - name = "prometheus_exporter_test", + name = "prometheus_exporter_test_deprecated", srcs = [ "test/prometheus_exporter_test.cc", ], @@ -81,7 +81,64 @@ cc_test( "test", ], deps = [ - ":prometheus_exporter", + ":prometheus_exporter_deprecated", "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "prometheus_exporter", + srcs = [ + "src/exporter.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/prometheus/exporter.h", + ], + strip_include_prefix = "include", + tags = ["prometheus"], + deps = [ + ":prometheus_collector", + ":prometheus_exporter_utils", + "//api", + "//sdk:headers", + "@com_github_jupp0r_prometheus_cpp//core", + "@com_github_jupp0r_prometheus_cpp//pull", + ], +) + +cc_library( + name = "prometheus_exporter_utils", + srcs = [ + "src/exporter_utils.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/prometheus/exporter_utils.h", + ], + strip_include_prefix = "include", + tags = ["prometheus"], + deps = [ + "//api", + "//sdk:headers", + "@com_github_jupp0r_prometheus_cpp//core", + "@com_github_jupp0r_prometheus_cpp//pull", + ], +) + +cc_library( + name = "prometheus_collector", + srcs = [ + "src/collector.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/prometheus/collector.h", + ], + strip_include_prefix = "include", + tags = ["prometheus"], + deps = [ + ":prometheus_exporter_utils", + "//api", + "//sdk:headers", + "@com_github_jupp0r_prometheus_cpp//core", + "@com_github_jupp0r_prometheus_cpp//pull", + ], +) diff --git a/exporters/prometheus/CMakeLists.txt b/exporters/prometheus/CMakeLists.txt index 753fa18cac..56523ec848 100755 --- a/exporters/prometheus/CMakeLists.txt +++ b/exporters/prometheus/CMakeLists.txt @@ -16,40 +16,75 @@ include_directories(include) if(NOT TARGET prometheus-cpp::core) find_package(prometheus-cpp CONFIG REQUIRED) endif() +if(WITH_METRICS_PREVIEW) + add_library( + prometheus_exporter_deprecated + src/prometheus_exporter.cc src/prometheus_collector.cc + src/prometheus_exporter_utils.cc) + target_include_directories( + prometheus_exporter_deprecated + PUBLIC "$" + "$") -add_library( - prometheus_exporter_deprecated - src/prometheus_exporter.cc src/prometheus_collector.cc - src/prometheus_exporter_utils.cc) -target_include_directories( - prometheus_exporter_deprecated - PUBLIC "$" - "$") + set(PROMETHEUS_EXPORTER_TARGETS_DEPRECATED prometheus_exporter_deprecated) + if(TARGET pull) + list(APPEND PROMETHEUS_EXPORTER_TARGETS_DEPRECATED pull) + endif() + if(TARGET core) + list(APPEND PROMETHEUS_EXPORTER_TARGETS_DEPRECATED core) + endif() + target_link_libraries( + prometheus_exporter_deprecated + PUBLIC opentelemetry_metrics_deprecated prometheus-cpp::pull + prometheus-cpp::core) + install( + TARGETS ${PROMETHEUS_EXPORTER_TARGETS_DEPRECATED} + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -set(PROMETHEUS_EXPORTER_TARGETS prometheus_exporter_deprecated) -if(TARGET pull) - list(APPEND PROMETHEUS_EXPORTER_TARGETS pull) -endif() -if(TARGET core) - list(APPEND PROMETHEUS_EXPORTER_TARGETS core) -endif() -target_link_libraries( - prometheus_exporter_deprecated - PUBLIC opentelemetry_metrics_deprecated prometheus-cpp::pull - prometheus-cpp::core) -install( - TARGETS ${PROMETHEUS_EXPORTER_TARGETS} - EXPORT "${PROJECT_NAME}-target" - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install( + DIRECTORY include/opentelemetry/exporters/prometheus + DESTINATION include/opentelemetry/exporters/ + FILES_MATCHING + PATTERN "*.h") + if(BUILD_TESTING) + add_subdirectory(test) + endif() +else() + + add_library(prometheus_exporter src/exporter.cc src/collector.cc + src/exporter_utils.cc) + target_include_directories( + prometheus_exporter + PUBLIC "$" + "$") + + set(PROMETHEUS_EXPORTER_TARGETS prometheus_exporter) + if(TARGET pull) + list(APPEND PROMETHEUS_EXPORTER_TARGETS pull) + endif() + if(TARGET core) + list(APPEND PROMETHEUS_EXPORTER_TARGETS core) + endif() + target_link_libraries( + prometheus_exporter PUBLIC opentelemetry_metrics prometheus-cpp::pull + prometheus-cpp::core) + install( + TARGETS ${PROMETHEUS_EXPORTER_TARGETS} + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install( - DIRECTORY include/opentelemetry/exporters/prometheus - DESTINATION include/opentelemetry/exporters - FILES_MATCHING - PATTERN "*.h") + install( + DIRECTORY include/opentelemetry/exporters/prometheus + DESTINATION include/opentelemetry/exporters/ + FILES_MATCHING + PATTERN "*.h") -if(BUILD_TESTING) - add_subdirectory(test) + if(BUILD_TESTING) + add_subdirectory(test) + endif() endif() diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h new file mode 100644 index 0000000000..68f50d29fa --- /dev/null +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h @@ -0,0 +1,87 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW + +# include +# include +# include + +# include +# include +# include "opentelemetry/exporters/prometheus/exporter_utils.h" + +namespace prometheus_client = ::prometheus; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace metrics +{ +/** + * The Prometheus Collector maintains the intermediate collection in Prometheus Exporter + */ +class PrometheusCollector : public prometheus_client::Collectable +{ +public: + /** + * Default Constructor. + * + * This constructor initializes the collection for metrics to export + * in this class with default capacity + */ + explicit PrometheusCollector(size_t max_collection_size = 2048); + + /** + * Collects all metrics data from metricsToCollect collection. + * + * @return all metrics in the metricsToCollect snapshot + */ + std::vector Collect() const override; + + /** + * This function is called by export() function and add the collection of + * records to the metricsToCollect collection + * + * @param records a collection of records to add to the metricsToCollect collection + */ + void AddMetricData(const sdk::metrics::ResourceMetrics &data); + + /** + * Get the current collection in the collector. + * + * @return the current metricsToCollect collection + */ + std::vector> &GetCollection(); + + /** + * Gets the maximum size of the collection. + * + * @return max collection size + */ + int GetMaxCollectionSize() const; + +private: + /** + * Collection of metrics data from the export() function, and to be export + * to user when they send a pull request. This collection is a pointer + * to a collection so Collect() is able to clear the collection, even + * though it is a const function. + */ + mutable std::vector> metrics_to_collect_; + + /** + * Maximum size of the metricsToCollect collection. + */ + size_t max_collection_size_; + + /* + * Lock when operating the metricsToCollect collection + */ + mutable std::mutex collection_lock_; +}; +} // namespace metrics +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h new file mode 100644 index 0000000000..151244928f --- /dev/null +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h @@ -0,0 +1,126 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include + +# include +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/exporters/prometheus/collector.h" +# include "opentelemetry/nostd/span.h" +# include "opentelemetry/sdk/common/env_variables.h" +# include "opentelemetry/sdk/metrics/metric_exporter.h" +# include "opentelemetry/version.h" + +/** + * This class is an implementation of the MetricsExporter interface and + * exports Prometheus metrics data. Functions in this class should be + * called by the Controller in our data pipeline. + */ + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace exporter +{ +namespace metrics +{ + +inline const std::string GetOtlpDefaultHttpEndpoint() +{ + constexpr char kPrometheusEndpointEnv[] = "PROMETHEUS_EXPORTER_ENDPOINT"; + constexpr char kPrometheusEndpointDefault[] = "localhost:8080"; + + auto endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kPrometheusEndpointEnv); + return endpoint.size() ? endpoint : kPrometheusEndpointDefault; +} + +/** + * Struct to hold Prometheus exporter options. + */ +struct PrometheusExporterOptions +{ + // The endpoint the Prometheus backend can collect metrics from + std::string url = GetOtlpDefaultHttpEndpoint(); +}; + +class PrometheusExporter : public sdk::metrics::MetricExporter +{ +public: + /** + * Constructor - binds an exposer and collector to the exporter + * @param options: options for an exposer that exposes + * an HTTP endpoint for the exporter to connect to + */ + PrometheusExporter(const PrometheusExporterOptions &options); + + /** + * Exports a batch of Metric Records. + * @param records: a collection of records to export + * @return: returns a ReturnCode detailing a success, or type of failure + */ + sdk::common::ExportResult Export(const sdk::metrics::ResourceMetrics &data) noexcept override; + + /** + * Force flush the exporter. + */ + bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + + /** + * Shuts down the exporter and does cleanup. + * Since Prometheus is a pull based interface, + * we cannot serve data remaining in the intermediate + * collection to to client an HTTP request being sent, + * so we flush the data. + */ + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override; + + /** + * @return: returns a shared_ptr to + * the PrometheusCollector instance + */ + std::shared_ptr &GetCollector(); + + /** + * @return: Gets the shutdown status of the exporter + */ + bool IsShutdown() const; + +private: + // The configuration options associated with this exporter. + const PrometheusExporterOptions options_; + /** + * exporter shutdown status + */ + bool is_shutdown_; + + /** + * Pointer to a + * PrometheusCollector instance + */ + std::shared_ptr collector_; + + /** + * Pointer to an + * Exposer instance + */ + std::unique_ptr<::prometheus::Exposer> exposer_; + + /** + * friend class for testing + */ + friend class PrometheusExporterTest; + + /** + * PrometheusExporter constructor with no parameters + * Used for testing only + */ + PrometheusExporter(); +}; +} // namespace metrics +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif // ENABLE_METRICS_PREVIEW diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h new file mode 100644 index 0000000000..cfbbb9c48b --- /dev/null +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h @@ -0,0 +1,116 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifndef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include "opentelemetry/metrics/provider.h" +# include "opentelemetry/sdk/metrics/meter.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace metrics +{ +/** + * The Prometheus Utils contains utility functions for Prometheus Exporter + */ +class PrometheusExporterUtils +{ +public: + /** + * Helper function to convert OpenTelemetry metrics data collection + * to Prometheus metrics data collection + * + * @param records a collection of metrics in OpenTelemetry + * @return a collection of translated metrics that is acceptable by Prometheus + */ + static std::vector<::prometheus::MetricFamily> TranslateToPrometheus( + const std::vector> &data); + +private: + /** + * Sanitize the given metric name or label according to Prometheus rule. + * + * This function is needed because names in OpenTelemetry can contain + * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the + * name should only contain alphanumeric characters and '_'. + */ + static std::string SanitizeNames(std::string name); + + static opentelemetry::sdk::metrics::AggregationType getAggregationType( + const opentelemetry::sdk::metrics::PointType &point_type); + + /** + * Translate the OTel metric type to Prometheus metric type + */ + static ::prometheus::MetricType TranslateType(opentelemetry::sdk::metrics::AggregationType kind); + + /** + * Set metric data for: + * Counter => Prometheus Counter + */ + template + static void SetData(std::vector values, + const std::string &labels, + ::prometheus::MetricType type, + std::chrono::nanoseconds time, + ::prometheus::MetricFamily *metric_family); + + /** + * Set metric data for: + * Histogram => Prometheus Histogram + */ + template + static void SetData(std::vector values, + const opentelemetry::sdk::metrics::ListType &boundaries, + const std::vector &counts, + const std::string &labels, + std::chrono::nanoseconds time, + ::prometheus::MetricFamily *metric_family); + + /** + * Set time and labels to metric data + */ + static void SetMetricBasic(::prometheus::ClientMetric &metric, + std::chrono::nanoseconds time, + const std::string &labels); + + /** + * Parse a string of labels (key:value) into a vector of pairs + * {,} + * {l1:v1,l2:v2,...,} + */ + static std::vector> ParseLabel(std::string labels); + + /** + * Handle Counter and Gauge. + */ + template + static void SetValue(std::vector values, + ::prometheus::MetricType type, + ::prometheus::ClientMetric *metric); + + /** + * Handle Gauge from MinMaxSumCount + */ + static void SetValue(double value, ::prometheus::ClientMetric *metric); + + /** + * Handle Histogram + */ + template + static void SetValue(std::vector values, + const std::list &boundaries, + const std::vector &counts, + ::prometheus::ClientMetric *metric); +}; +} // namespace metrics +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/exporters/prometheus/src/collector.cc b/exporters/prometheus/src/collector.cc new file mode 100644 index 0000000000..03793a8eed --- /dev/null +++ b/exporters/prometheus/src/collector.cc @@ -0,0 +1,89 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/exporters/prometheus/collector.h" + +namespace metric_sdk = opentelemetry::sdk::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace metrics +{ +/** + * Default Constructor. + * + * This constructor initializes the collection for metrics to export + * in this class with default capacity + */ +PrometheusCollector::PrometheusCollector(size_t max_collection_size) + : max_collection_size_(max_collection_size) +{} + +/** + * Collects all metrics data from metricsToCollect collection. + * + * @return all metrics in the metricsToCollect snapshot + */ +std::vector PrometheusCollector::Collect() const +{ + this->collection_lock_.lock(); + if (metrics_to_collect_.empty()) + { + this->collection_lock_.unlock(); + return {}; + } + + std::vector result; + + // copy the intermediate collection, and then clear it + std::vector> copied_data; + copied_data.swap(metrics_to_collect_); + this->collection_lock_.unlock(); + + result = PrometheusExporterUtils::TranslateToPrometheus(copied_data); + return result; +} + +/** + * This function is called by export() function and add the collection of + * records to the metricsToCollect collection + * + * @param records a collection of records to add to the metricsToCollect collection + */ +void PrometheusCollector::AddMetricData(const sdk::metrics::ResourceMetrics &data) +{ + collection_lock_.lock(); + if (metrics_to_collect_.size() + 1 <= max_collection_size_) + { + metrics_to_collect_.emplace_back(new sdk::metrics::ResourceMetrics{data}); + } + collection_lock_.unlock(); +} + +/** + * Get the current collection in the collector. + * + * @return the current metrics_to_collect collection + */ +std::vector> &PrometheusCollector::GetCollection() +{ + return metrics_to_collect_; +} + +/** + * Gets the maximum size of the collection. + * + * @return max collection size + */ +int PrometheusCollector::GetMaxCollectionSize() const +{ + return max_collection_size_; +} + +} // namespace metrics +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/exporters/prometheus/src/exporter.cc b/exporters/prometheus/src/exporter.cc new file mode 100644 index 0000000000..a0bd9e27ab --- /dev/null +++ b/exporters/prometheus/src/exporter.cc @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/exporters/prometheus/exporter.h" + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace exporter +{ +namespace metrics +{ +/** + * Constructor - binds an exposer and collector to the exporter + * @param address: an address for an exposer that exposes + * an HTTP endpoint for the exporter to connect to + */ +PrometheusExporter::PrometheusExporter(const PrometheusExporterOptions &options) + : options_(options), is_shutdown_(false) +{ + exposer_ = std::unique_ptr<::prometheus::Exposer>(new ::prometheus::Exposer{options_.url}); + collector_ = std::shared_ptr(new PrometheusCollector); + + exposer_->RegisterCollectable(collector_); +} + +/** + * PrometheusExporter constructor with no parameters + * Used for testing only + */ +PrometheusExporter::PrometheusExporter() : is_shutdown_(false) +{ + collector_ = std::unique_ptr(new PrometheusCollector); +} + +/** + * Exports a batch of Metric Records. + * @param records: a collection of records to export + * @return: returns a ReturnCode detailing a success, or type of failure + */ +sdk::common::ExportResult PrometheusExporter::Export( + const sdk::metrics::ResourceMetrics &data) noexcept +{ + if (is_shutdown_) + { + return sdk::common::ExportResult::kFailure; + } + else if (collector_->GetCollection().size() + 1 > (size_t)collector_->GetMaxCollectionSize()) + { + return sdk::common::ExportResult::kFailureFull; + } + else + { + collector_->AddMetricData(data); + return sdk::common::ExportResult::kSuccess; + } + return sdk::common::ExportResult::kSuccess; +} + +bool PrometheusExporter::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return true; +} + +/** + * Shuts down the exporter and does cleanup. + * Since Prometheus is a pull based interface, + * we cannot serve data remaining in the intermediate + * collection to to client an HTTP request being sent, + * so we flush the data. + */ +bool PrometheusExporter::Shutdown(std::chrono::microseconds timeout) noexcept +{ + is_shutdown_ = true; + return true; + + collector_->GetCollection().clear(); +} + +/** + * @return: returns a shared_ptr to + * the PrometheusCollector instance + */ +std::shared_ptr &PrometheusExporter::GetCollector() +{ + return collector_; +} + +/** + * @return: Gets the shutdown status of the exporter + */ +bool PrometheusExporter::IsShutdown() const +{ + return is_shutdown_; +} + +} // namespace metrics +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif // ENABLE_METRICS_PREVIEW diff --git a/exporters/prometheus/src/exporter_utils.cc b/exporters/prometheus/src/exporter_utils.cc new file mode 100644 index 0000000000..9979c9bebd --- /dev/null +++ b/exporters/prometheus/src/exporter_utils.cc @@ -0,0 +1,308 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include + +# include +# include "opentelemetry/exporters/prometheus/exporter_utils.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" + +namespace prometheus_client = ::prometheus; +namespace metric_sdk = opentelemetry::sdk::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace metrics +{ +/** + * Helper function to convert OpenTelemetry metrics data collection + * to Prometheus metrics data collection + * + * @param records a collection of metrics in OpenTelemetry + * @return a collection of translated metrics that is acceptable by Prometheus + */ +std::vector PrometheusExporterUtils::TranslateToPrometheus( + const std::vector> &data) +{ + if (data.empty()) + { + return {}; + } + + // initialize output vector + std::vector output; + + // iterate through the vector and set result data into it + for (const auto &r : data) + { + for (const auto &instrumentation_info : r->instrumentation_info_metric_data_) + { + for (const auto &metric_data : instrumentation_info.metric_data_) + { + auto origin_name = metric_data.instrument_descriptor.name_; + auto sanitized = SanitizeNames(origin_name); + prometheus_client::MetricFamily metric_family; + metric_family.name = sanitized; + metric_family.help = metric_data.instrument_descriptor.description_; + auto time = metric_data.start_ts.time_since_epoch(); + for (const auto &point_data_attr : metric_data.point_data_attr_) + { + auto kind = getAggregationType(point_data_attr.point_data); + const prometheus_client::MetricType type = TranslateType(kind); + metric_family.type = type; + if (type == prometheus_client::MetricType::Histogram) // Histogram + { + auto histogram_point_data = + nostd::get(point_data_attr.point_data); + auto boundaries = histogram_point_data.boundaries_; + auto counts = histogram_point_data.counts_; + SetData(std::vector{nostd::get(histogram_point_data.sum_), + (double)histogram_point_data.count_}, + boundaries, counts, "", time, &metric_family); + } + else // Counter, Untyped + { + auto sum_point_data = + nostd::get(point_data_attr.point_data); + std::vector values{sum_point_data.value_}; + SetData(values, "", type, time, &metric_family); + } + } + output.emplace_back(metric_family); + } + } + } + return output; +} + +/** + * Sanitize the given metric name or label according to Prometheus rule. + * + * This function is needed because names in OpenTelemetry can contain + * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the + * name should only contain alphanumeric characters and '_'. + */ +std::string PrometheusExporterUtils::SanitizeNames(std::string name) +{ + // replace all '.' and '-' with '_' + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '-', '_'); + + return name; +} + +metric_sdk::AggregationType PrometheusExporterUtils::getAggregationType( + const metric_sdk::PointType &point_type) +{ + if (nostd::holds_alternative(point_type)) + { + return metric_sdk::AggregationType::kSum; + } + else if (nostd::holds_alternative(point_type)) + { + return metric_sdk::AggregationType::kDrop; + } + else if (nostd::holds_alternative(point_type)) + { + return metric_sdk::AggregationType::kHistogram; + } + else if (nostd::holds_alternative(point_type)) + { + return metric_sdk::AggregationType::kLastValue; + } + return metric_sdk::AggregationType::kDefault; +} + +/** + * Translate the OTel metric type to Prometheus metric type + */ +prometheus_client::MetricType PrometheusExporterUtils::TranslateType( + metric_sdk::AggregationType kind) +{ + switch (kind) + { + case metric_sdk::AggregationType::kSum: + return prometheus_client::MetricType::Counter; + case metric_sdk::AggregationType::kHistogram: + return prometheus_client::MetricType::Histogram; + default: + return prometheus_client::MetricType::Untyped; + } +} + +/** + * Set metric data for: + * sum => Prometheus Counter + */ +template +void PrometheusExporterUtils::SetData(std::vector values, + const std::string &labels, + prometheus_client::MetricType type, + std::chrono::nanoseconds time, + prometheus_client::MetricFamily *metric_family) +{ + metric_family->metric.emplace_back(); + prometheus_client::ClientMetric &metric = metric_family->metric.back(); + SetMetricBasic(metric, time, labels); + SetValue(values, type, &metric); +} + +/** + * Set metric data for: + * Histogram => Prometheus Histogram + */ +template +void PrometheusExporterUtils::SetData(std::vector values, + const opentelemetry::sdk::metrics::ListType &boundaries, + const std::vector &counts, + const std::string &labels, + std::chrono::nanoseconds time, + prometheus_client::MetricFamily *metric_family) +{ + metric_family->metric.emplace_back(); + prometheus_client::ClientMetric &metric = metric_family->metric.back(); + SetMetricBasic(metric, time, labels); + if (nostd::holds_alternative>(boundaries)) + { + SetValue(values, nostd::get>(boundaries), counts, &metric); + } + else + { + SetValue(values, nostd::get>(boundaries), counts, &metric); + } +} + +/** + * Set time and labels to metric data + */ +void PrometheusExporterUtils::SetMetricBasic(prometheus_client::ClientMetric &metric, + std::chrono::nanoseconds time, + const std::string &labels) +{ + metric.timestamp_ms = time.count() / 1000000; + + auto label_pairs = ParseLabel(labels); + if (!label_pairs.empty()) + { + metric.label.resize(label_pairs.size()); + for (size_t i = 0; i < label_pairs.size(); ++i) + { + auto origin_name = label_pairs[i].first; + auto sanitized = SanitizeNames(origin_name); + metric.label[i].name = sanitized; + metric.label[i].value = label_pairs[i].second; + } + } +}; + +/** + * Parse a string of labels (key:value) into a vector of pairs + * {,} + * {l1:v1,l2:v2,...,} + */ +std::vector> PrometheusExporterUtils::ParseLabel( + std::string labels) +{ + if (labels.size() < 3) + { + return {}; + } + labels = labels.substr(1, labels.size() - 2); + + std::vector paired_labels; + std::stringstream s_stream(labels); + while (s_stream.good()) + { + std::string substr; + getline(s_stream, substr, ','); // get first string delimited by comma + if (!substr.empty()) + { + paired_labels.push_back(substr); + } + } + + std::vector> result; + for (auto &paired : paired_labels) + { + std::size_t split_index = paired.find(':'); + std::string label = paired.substr(0, split_index); + std::string value = paired.substr(split_index + 1); + result.emplace_back(std::pair(label, value)); + } + + return result; +} + +/** + * Handle Counter. + */ +template +void PrometheusExporterUtils::SetValue(std::vector values, + prometheus_client::MetricType type, + prometheus_client::ClientMetric *metric) +{ + double value = 0.0; + const auto &value_var = values[0]; + if (nostd::holds_alternative(value_var)) + { + value = nostd::get(value_var); + } + else + { + value = nostd::get(value_var); + } + + switch (type) + { + case prometheus_client::MetricType::Counter: { + metric->counter.value = value; + break; + } + case prometheus_client::MetricType::Untyped: { + metric->untyped.value = value; + break; + } + default: + return; + } +} + +/** + * Handle Histogram + */ +template +void PrometheusExporterUtils::SetValue(std::vector values, + const std::list &boundaries, + const std::vector &counts, + prometheus_client::ClientMetric *metric) +{ + metric->histogram.sample_sum = values[0]; + metric->histogram.sample_count = values[1]; + int cumulative = 0; + std::vector buckets; + uint32_t idx = 0; + for (const auto &boundary : boundaries) + { + prometheus_client::ClientMetric::Bucket bucket; + cumulative += counts[idx]; + bucket.cumulative_count = cumulative; + bucket.upper_bound = boundary; + buckets.emplace_back(bucket); + ++idx; + } + prometheus_client::ClientMetric::Bucket bucket; + cumulative += counts[idx]; + bucket.cumulative_count = cumulative; + bucket.upper_bound = std::numeric_limits::infinity(); + buckets.emplace_back(bucket); + metric->histogram.bucket = buckets; +} + +} // namespace metrics +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/exporters/prometheus/test/CMakeLists.txt b/exporters/prometheus/test/CMakeLists.txt index 6c45e9299c..1a22469792 100644 --- a/exporters/prometheus/test/CMakeLists.txt +++ b/exporters/prometheus/test/CMakeLists.txt @@ -1,11 +1,13 @@ -foreach(testname prometheus_exporter_test prometheus_collector_test - prometheus_exporter_utils_test) - add_executable(${testname} "${testname}.cc") - target_link_libraries( - ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - prometheus_exporter_deprecated prometheus-cpp::pull) - gtest_add_tests( - TARGET ${testname} - TEST_PREFIX exporter. - TEST_LIST ${testname}) -endforeach() +if(WITH_METRICS_PREVIEW) + foreach(testname prometheus_exporter_test prometheus_collector_test + prometheus_exporter_utils_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + prometheus_exporter_deprecated prometheus-cpp::pull) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX exporter. + TEST_LIST ${testname}) + endforeach() +endif() diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h b/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h index b5a1283d26..887e1beb92 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h @@ -28,6 +28,7 @@ class DefaultAggregation { case InstrumentType::kCounter: case InstrumentType::kUpDownCounter: + case InstrumentType::kObservableCounter: case InstrumentType::kObservableUpDownCounter: return (instrument_descriptor.value_type_ == InstrumentValueType::kLong) ? std::move(std::unique_ptr(new LongSumAggregation())) @@ -90,6 +91,53 @@ class DefaultAggregation return DefaultAggregation::CreateAggregation(instrument_descriptor); } } + + static std::unique_ptr CloneAggregation(AggregationType aggregation_type, + InstrumentDescriptor instrument_descriptor, + const Aggregation &to_copy) + { + const PointType point_data = to_copy.ToPoint(); + switch (aggregation_type) + { + case AggregationType::kDrop: + return std::unique_ptr(new DropAggregation()); + case AggregationType::kHistogram: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr( + new LongHistogramAggregation(nostd::get(point_data))); + } + else + { + return std::unique_ptr( + new DoubleHistogramAggregation(nostd::get(point_data))); + } + case AggregationType::kLastValue: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr( + new LongLastValueAggregation(nostd::get(point_data))); + } + else + { + return std::unique_ptr( + new DoubleLastValueAggregation(nostd::get(point_data))); + } + case AggregationType::kSum: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr( + new LongSumAggregation(nostd::get(point_data))); + } + else + { + return std::unique_ptr( + new DoubleSumAggregation(nostd::get(point_data))); + } + default: + return DefaultAggregation::CreateAggregation(instrument_descriptor); + } + } }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h b/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h index 4e29fa2e46..6c3d89d247 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h @@ -23,16 +23,18 @@ class DropAggregation : public Aggregation public: DropAggregation() = default; + DropAggregation(const DropPointData &) {} + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} - std::unique_ptr Merge(const Aggregation &delta) const noexcept override + std::unique_ptr Merge(const Aggregation &) const noexcept override { return std::unique_ptr(new DropAggregation()); } - std::unique_ptr Diff(const Aggregation &next) const noexcept override + std::unique_ptr Diff(const Aggregation &) const noexcept override { return std::unique_ptr(new DropAggregation()); } diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h b/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h index b5cc2c349e..e2a55fba58 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h @@ -19,6 +19,7 @@ class LongHistogramAggregation : public Aggregation public: LongHistogramAggregation(); LongHistogramAggregation(HistogramPointData &&); + LongHistogramAggregation(const HistogramPointData &); void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override; @@ -26,14 +27,14 @@ class LongHistogramAggregation : public Aggregation /* Returns the result of merge of the existing aggregation with delta aggregation with same * boundaries */ - virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; /* Returns the new delta aggregation by comparing existing aggregation with next aggregation with * same boundaries. Data points for `next` aggregation (sum , bucket-counts) should be more than * the current aggregation - which is the normal scenario as measurements values are monotonic * increasing. */ - virtual std::unique_ptr Diff(const Aggregation &next) const noexcept override; + std::unique_ptr Diff(const Aggregation &next) const noexcept override; PointType ToPoint() const noexcept override; @@ -47,6 +48,7 @@ class DoubleHistogramAggregation : public Aggregation public: DoubleHistogramAggregation(); DoubleHistogramAggregation(HistogramPointData &&); + DoubleHistogramAggregation(const HistogramPointData &); void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} @@ -54,14 +56,14 @@ class DoubleHistogramAggregation : public Aggregation /* Returns the result of merge of the existing aggregation with delta aggregation with same * boundaries */ - virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; /* Returns the new delta aggregation by comparing existing aggregation with next aggregation with * same boundaries. Data points for `next` aggregation (sum , bucket-counts) should be more than * the current aggregation - which is the normal scenario as measurements values are monotonic * increasing. */ - virtual std::unique_ptr Diff(const Aggregation &next) const noexcept override; + std::unique_ptr Diff(const Aggregation &next) const noexcept override; PointType ToPoint() const noexcept override; diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h b/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h index 7f185d51a1..3b2c08f8ce 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h @@ -18,14 +18,15 @@ class LongLastValueAggregation : public Aggregation public: LongLastValueAggregation(); LongLastValueAggregation(LastValuePointData &&); + LongLastValueAggregation(const LastValuePointData &); void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override; void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} - virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; - virtual std::unique_ptr Diff(const Aggregation &next) const noexcept override; + std::unique_ptr Diff(const Aggregation &next) const noexcept override; PointType ToPoint() const noexcept override; @@ -39,6 +40,7 @@ class DoubleLastValueAggregation : public Aggregation public: DoubleLastValueAggregation(); DoubleLastValueAggregation(LastValuePointData &&); + DoubleLastValueAggregation(const LastValuePointData &); void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h b/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h index b0f0169b24..14f13bd727 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h @@ -19,14 +19,15 @@ class LongSumAggregation : public Aggregation public: LongSumAggregation(); LongSumAggregation(SumPointData &&); + LongSumAggregation(const SumPointData &); void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override; void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} - virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; - virtual std::unique_ptr Diff(const Aggregation &next) const noexcept override; + std::unique_ptr Diff(const Aggregation &next) const noexcept override; PointType ToPoint() const noexcept override; @@ -40,14 +41,15 @@ class DoubleSumAggregation : public Aggregation public: DoubleSumAggregation(); DoubleSumAggregation(SumPointData &&); + DoubleSumAggregation(const SumPointData &); void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override; - virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; - virtual std::unique_ptr Diff(const Aggregation &next) const noexcept override; + std::unique_ptr Diff(const Aggregation &next) const noexcept override; PointType ToPoint() const noexcept override; diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregator/aggregator.h b/sdk/include/opentelemetry/sdk/metrics/aggregator/aggregator.h deleted file mode 100644 index 416cea5adf..0000000000 --- a/sdk/include/opentelemetry/sdk/metrics/aggregator/aggregator.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once -#ifndef ENABLE_METRICS_PREVIEW -# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" -# include "opentelemetry/version.h" -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace metrics -{ -class Aggregator -{}; - -class NoOpAggregator : public Aggregator -{ - // TBD -}; - -} // namespace metrics -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE -#endif \ No newline at end of file diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_context.h b/sdk/include/opentelemetry/sdk/metrics/meter_context.h index 74f67a1c46..e35700175d 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_context.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_context.h @@ -24,7 +24,6 @@ namespace metrics // forward declaration class Meter; -class MetricExporter; class MetricReader; /** @@ -36,13 +35,11 @@ class MeterContext : public std::enable_shared_from_this public: /** * Initialize a new meter provider - * @param exporters The exporters to be configured with meter context * @param readers The readers to be configured with meter context. * @param views The views to be configured with meter context. * @param resource The resource for this meter context. */ MeterContext( - std::vector> &&exporters, std::unique_ptr views = std::unique_ptr(new ViewRegistry()), opentelemetry::sdk::resource::Resource resource = opentelemetry::sdk::resource::Resource::Create({})) noexcept; @@ -77,16 +74,6 @@ class MeterContext : public std::enable_shared_from_this */ opentelemetry::common::SystemTimestamp GetSDKStartTime() noexcept; - /** - * Attaches a metric exporter to list of configured exporters for this Meter context. - * @param exporter The metric exporter for this meter context. This - * must not be a nullptr. - * - * Note: This exporter 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. - */ - void AddMetricExporter(std::unique_ptr exporter) noexcept; - /** * Attaches a metric reader to list of configured readers for this Meter context. * @param reader The metric reader for this meter context. This @@ -118,20 +105,19 @@ class MeterContext : public std::enable_shared_from_this void AddMeter(std::shared_ptr meter); /** - * Force all active Exporters and Collectors to flush any buffered meter data + * Force all active Collectors to flush any buffered meter data * within the given timeout. */ bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; /** - * Shutdown the Exporters and Collectors associated with this meter provider. + * Shutdown the Collectors associated with this meter provider. */ bool Shutdown() noexcept; private: opentelemetry::sdk::resource::Resource resource_; - std::vector> exporters_; std::vector> collectors_; std::unique_ptr views_; opentelemetry::common::SystemTimestamp sdk_start_ts_; diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h index c6efba6222..685f43e747 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h @@ -22,7 +22,6 @@ namespace metrics // forward declaration class MetricCollector; -class MetricExporter; class MetricReader; class MeterProvider final : public opentelemetry::metrics::MeterProvider @@ -30,12 +29,10 @@ class MeterProvider final : public opentelemetry::metrics::MeterProvider public: /** * Initialize a new meter provider - * @param exporters The span exporters for this meter provider * @param views The views for this meter provider * @param resource The resources for this meter provider. */ MeterProvider( - std::vector> &&exporters, std::unique_ptr views = std::unique_ptr(new ViewRegistry()), sdk::resource::Resource resource = sdk::resource::Resource::Create({})) noexcept; @@ -56,16 +53,6 @@ class MeterProvider final : public opentelemetry::metrics::MeterProvider */ const sdk::resource::Resource &GetResource() const noexcept; - /** - * Attaches a metric exporter to list of configured exporters for this Meter provider. - * @param exporter The metric exporter for this meter provider. This - * must not be a nullptr. - * - * Note: This exporter 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. - */ - void AddMetricExporter(std::unique_ptr exporter) noexcept; - /** * Attaches a metric reader to list of configured readers for this Meter providers. * @param reader The metric reader for this meter provider. This diff --git a/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h b/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h index 127ef468ac..3217b83df5 100644 --- a/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h +++ b/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h @@ -29,7 +29,7 @@ class MetricExporter virtual ~MetricExporter() = default; /** - * Exports a batch of metrics recordables. This method must not be called + * Exports a batch of metrics data. This method must not be called * concurrently for the same exporter instance. * @param data metrics data */ diff --git a/sdk/include/opentelemetry/sdk/metrics/recordable.h b/sdk/include/opentelemetry/sdk/metrics/recordable.h deleted file mode 100644 index d7e7f5756e..0000000000 --- a/sdk/include/opentelemetry/sdk/metrics/recordable.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once -#ifndef ENABLE_METRICS_PREVIEW -# include "opentelemetry/nostd/string_view.h" -# include "opentelemetry/version.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace metrics -{ -class Recordable -{ -public: - virtual ~Recordable() = default; - nostd::string_view GetName() { return name_; } - // TBD -private: - nostd::string_view name_; -}; -} // namespace metrics -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE; -#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h index cfbf521538..e4c20e4010 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h @@ -10,6 +10,7 @@ # include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" # include "opentelemetry/sdk/metrics/state/metric_collector.h" # include "opentelemetry/sdk/metrics/state/metric_storage.h" +# include "opentelemetry/sdk/metrics/state/temporal_metric_storage.h" # include "opentelemetry/sdk/metrics/view/attributes_processor.h" # include @@ -32,7 +33,8 @@ class AsyncMetricStorage : public MetricStorage aggregation_type_{aggregation_type}, measurement_collection_callback_{measurement_callback}, attributes_processor_{attributes_processor}, - active_attributes_hashmap_(new AttributesHashMap()) + cumulative_hash_map_(new AttributesHashMap()), + temporal_metric_storage_(instrument_descriptor) {} bool Collect(CollectorHandle *collector, @@ -45,22 +47,33 @@ class AsyncMetricStorage : public MetricStorage // read the measurement using configured callback measurement_collection_callback_(ob_res); - + std::shared_ptr delta_hash_map(new AttributesHashMap()); // process the read measurements - aggregate and store in hashmap for (auto &measurement : ob_res.GetMeasurements()) { - auto agg = DefaultAggregation::CreateAggregation(aggregation_type_, instrument_descriptor_); - agg->Aggregate(measurement.second); - active_attributes_hashmap_->Set(measurement.first, std::move(agg)); + auto aggr = DefaultAggregation::CreateAggregation(aggregation_type_, instrument_descriptor_); + aggr->Aggregate(measurement.second); + auto prev = cumulative_hash_map_->Get(measurement.first); + if (prev) + { + auto delta = prev->Diff(*aggr); + cumulative_hash_map_->Set(measurement.first, + DefaultAggregation::CloneAggregation( + aggregation_type_, instrument_descriptor_, *delta)); + delta_hash_map->Set(measurement.first, std::move(delta)); + } + else + { + cumulative_hash_map_->Set( + measurement.first, + DefaultAggregation::CloneAggregation(aggregation_type_, instrument_descriptor_, *aggr)); + delta_hash_map->Set(measurement.first, std::move(aggr)); + } } - // TBD -> read aggregation from hashmap, and perform metric collection - MetricData metric_data; - if (metric_collection_callback(std::move(metric_data))) - { - return true; - } - return false; + return temporal_metric_storage_.buildMetrics(collector, collectors, sdk_start_ts, collection_ts, + std::move(delta_hash_map), + metric_collection_callback); } private: @@ -68,7 +81,8 @@ class AsyncMetricStorage : public MetricStorage AggregationType aggregation_type_; void (*measurement_collection_callback_)(opentelemetry::metrics::ObserverResult &); const AttributesProcessor *attributes_processor_; - std::unique_ptr active_attributes_hashmap_; + std::unique_ptr cumulative_hash_map_; + TemporalMetricStorage temporal_metric_storage_; }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h index c16f33ede2..37f485997c 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h @@ -5,16 +5,14 @@ #ifndef ENABLE_METRICS_PREVIEW # include "opentelemetry/common/key_value_iterable_view.h" # include "opentelemetry/sdk/common/attributemap_hash.h" -# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" # include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" # include "opentelemetry/sdk/metrics/exemplar/reservoir.h" # include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" # include "opentelemetry/sdk/metrics/state/metric_collector.h" # include "opentelemetry/sdk/metrics/state/metric_storage.h" +# include "opentelemetry/sdk/metrics/state/temporal_metric_storage.h" # include "opentelemetry/sdk/metrics/view/attributes_processor.h" -# include "opentelemetry/sdk/metrics/view/view.h" -# include "opentelemetry/sdk/resource/resource.h" # include # include @@ -24,13 +22,6 @@ namespace sdk { namespace metrics { - -struct LastReportedMetrics -{ - std::unique_ptr attributes_map; - opentelemetry::common::SystemTimestamp collection_ts; -}; - class SyncMetricStorage : public MetricStorage, public WritableMetricStorage { @@ -43,7 +34,9 @@ class SyncMetricStorage : public MetricStorage, public WritableMetricStorage aggregation_type_{aggregation_type}, attributes_hashmap_(new AttributesHashMap()), attributes_processor_{attributes_processor}, - exemplar_reservoir_(exemplar_reservoir) + exemplar_reservoir_(exemplar_reservoir), + temporal_metric_storage_(instrument_descriptor) + { create_default_aggregation_ = [&]() -> std::unique_ptr { return std::move( @@ -122,6 +115,7 @@ class SyncMetricStorage : public MetricStorage, public WritableMetricStorage const AttributesProcessor *attributes_processor_; std::function()> create_default_aggregation_; nostd::shared_ptr exemplar_reservoir_; + TemporalMetricStorage temporal_metric_storage_; }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h new file mode 100644 index 0000000000..16659c14f5 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h @@ -0,0 +1,50 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" +# include "opentelemetry/sdk/metrics/state/metric_collector.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +struct LastReportedMetrics +{ + std::unique_ptr attributes_map; + opentelemetry::common::SystemTimestamp collection_ts; +}; + +class TemporalMetricStorage +{ +public: + TemporalMetricStorage(InstrumentDescriptor instrument_descriptor); + + bool buildMetrics(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + std::shared_ptr delta_metrics, + nostd::function_ref callback) noexcept; + +private: + InstrumentDescriptor instrument_descriptor_; + + // unreported metrics stash for all the collectors + std::unordered_map>> + unreported_metrics_; + // last reported metrics stash for all the collectors. + std::unordered_map last_reported_metrics_; + + // Lock while building metrics + mutable opentelemetry::common::SpinLockMutex lock_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/view/view.h b/sdk/include/opentelemetry/sdk/metrics/view/view.h index e88e7126c6..3cd9f850e1 100644 --- a/sdk/include/opentelemetry/sdk/metrics/view/view.h +++ b/sdk/include/opentelemetry/sdk/metrics/view/view.h @@ -23,7 +23,7 @@ class View public: View(const std::string &name, const std::string &description = "", - AggregationType aggregation_type = AggregationType::kDrop, + AggregationType aggregation_type = AggregationType::kDefault, std::unique_ptr attributes_processor = std::unique_ptr( new opentelemetry::sdk::metrics::DefaultAttributesProcessor())) diff --git a/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h b/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h index 87b842e46a..795049dd9a 100644 --- a/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h +++ b/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h @@ -64,7 +64,7 @@ class ViewRegistry // return default view if none found; if (!found) { - static View view(""); + static View view("otel-default-view"); if (!callback(view)) { return false; diff --git a/sdk/src/metrics/CMakeLists.txt b/sdk/src/metrics/CMakeLists.txt index b6656b5bf8..77a371a80c 100644 --- a/sdk/src/metrics/CMakeLists.txt +++ b/sdk/src/metrics/CMakeLists.txt @@ -7,6 +7,7 @@ add_library( export/periodic_exporting_metric_reader.cc state/metric_collector.cc state/sync_metric_storage.cc + state/temporal_metric_storage.cc aggregation/histogram_aggregation.cc aggregation/lastvalue_aggregation.cc aggregation/sum_aggregation.cc diff --git a/sdk/src/metrics/aggregation/histogram_aggregation.cc b/sdk/src/metrics/aggregation/histogram_aggregation.cc index 27405999c9..aa2be74713 100644 --- a/sdk/src/metrics/aggregation/histogram_aggregation.cc +++ b/sdk/src/metrics/aggregation/histogram_aggregation.cc @@ -25,6 +25,10 @@ LongHistogramAggregation::LongHistogramAggregation(HistogramPointData &&data) : point_data_{std::move(data)} {} +LongHistogramAggregation::LongHistogramAggregation(const HistogramPointData &data) + : point_data_{data} +{} + void LongHistogramAggregation::Aggregate(long value, const PointAttributes &attributes) noexcept { const std::lock_guard locked(lock_); @@ -83,6 +87,10 @@ DoubleHistogramAggregation::DoubleHistogramAggregation(HistogramPointData &&data : point_data_{std::move(data)} {} +DoubleHistogramAggregation::DoubleHistogramAggregation(const HistogramPointData &data) + : point_data_{data} +{} + void DoubleHistogramAggregation::Aggregate(double value, const PointAttributes &attributes) noexcept { const std::lock_guard locked(lock_); diff --git a/sdk/src/metrics/aggregation/lastvalue_aggregation.cc b/sdk/src/metrics/aggregation/lastvalue_aggregation.cc index 9c0252be31..a125005335 100644 --- a/sdk/src/metrics/aggregation/lastvalue_aggregation.cc +++ b/sdk/src/metrics/aggregation/lastvalue_aggregation.cc @@ -19,10 +19,15 @@ LongLastValueAggregation::LongLastValueAggregation() point_data_.is_lastvalue_valid_ = false; point_data_.value_ = 0l; } + LongLastValueAggregation::LongLastValueAggregation(LastValuePointData &&data) : point_data_{std::move(data)} {} +LongLastValueAggregation::LongLastValueAggregation(const LastValuePointData &data) + : point_data_{data} +{} + void LongLastValueAggregation::Aggregate(long value, const PointAttributes &attributes) noexcept { const std::lock_guard locked(lock_); @@ -71,10 +76,15 @@ DoubleLastValueAggregation::DoubleLastValueAggregation() point_data_.is_lastvalue_valid_ = false; point_data_.value_ = 0.0; } + DoubleLastValueAggregation::DoubleLastValueAggregation(LastValuePointData &&data) : point_data_{std::move(data)} {} +DoubleLastValueAggregation::DoubleLastValueAggregation(const LastValuePointData &data) + : point_data_{data} +{} + void DoubleLastValueAggregation::Aggregate(double value, const PointAttributes &attributes) noexcept { const std::lock_guard locked(lock_); diff --git a/sdk/src/metrics/aggregation/sum_aggregation.cc b/sdk/src/metrics/aggregation/sum_aggregation.cc index 94b871cd34..5ca786496e 100644 --- a/sdk/src/metrics/aggregation/sum_aggregation.cc +++ b/sdk/src/metrics/aggregation/sum_aggregation.cc @@ -22,6 +22,8 @@ LongSumAggregation::LongSumAggregation() LongSumAggregation::LongSumAggregation(SumPointData &&data) : point_data_{std::move(data)} {} +LongSumAggregation::LongSumAggregation(const SumPointData &data) : point_data_{data} {} + void LongSumAggregation::Aggregate(long value, const PointAttributes &attributes) noexcept { const std::lock_guard locked(lock_); @@ -64,6 +66,8 @@ DoubleSumAggregation::DoubleSumAggregation() DoubleSumAggregation::DoubleSumAggregation(SumPointData &&data) : point_data_(std::move(data)) {} +DoubleSumAggregation::DoubleSumAggregation(const SumPointData &data) : point_data_(data) {} + void DoubleSumAggregation::Aggregate(double value, const PointAttributes &attributes) noexcept { const std::lock_guard locked(lock_); diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index d5af7f9ec9..ce638c22e6 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -28,7 +28,7 @@ namespace nostd = opentelemetry::nostd; Meter::Meter(std::shared_ptr meter_context, std::unique_ptr instrumentation_library) noexcept - : meter_context_{meter_context}, instrumentation_library_{std::move(instrumentation_library)} + : instrumentation_library_{std::move(instrumentation_library)}, meter_context_{meter_context} {} nostd::shared_ptr> Meter::CreateLongCounter(nostd::string_view name, @@ -188,10 +188,16 @@ std::unique_ptr Meter::RegisterMetricStorage( auto success = view_registry->FindViews( instrument_descriptor, *instrumentation_library_, [this, &instrument_descriptor, &storages](const View &view) { - auto view_instr_desc = instrument_descriptor; - view_instr_desc.name_ = view.GetName(); - view_instr_desc.description_ = view.GetDescription(); - auto storage = std::shared_ptr(new SyncMetricStorage( + auto view_instr_desc = instrument_descriptor; + if (!view.GetName().empty()) + { + view_instr_desc.name_ = view.GetName(); + } + if (!view.GetDescription().empty()) + { + view_instr_desc.description_ = view.GetDescription(); + } + auto storage = std::shared_ptr(new SyncMetricStorage( view_instr_desc, view.GetAggregationType(), &view.GetAttributesProcessor(), NoExemplarReservoir::GetNoExemplarReservoir())); storage_registry_[instrument_descriptor.name_] = storage; diff --git a/sdk/src/metrics/meter_context.cc b/sdk/src/metrics/meter_context.cc index f23135fa0c..73721e324d 100644 --- a/sdk/src/metrics/meter_context.cc +++ b/sdk/src/metrics/meter_context.cc @@ -4,7 +4,6 @@ #ifndef ENABLE_METRICS_PREVIEW # include "opentelemetry/sdk/metrics/meter_context.h" # include "opentelemetry/sdk/common/global_log_handler.h" -# include "opentelemetry/sdk/metrics/metric_exporter.h" # include "opentelemetry/sdk/metrics/metric_reader.h" # include "opentelemetry/sdk_config.h" # include "opentelemetry/version.h" @@ -15,13 +14,9 @@ namespace sdk namespace metrics { -MeterContext::MeterContext(std::vector> &&exporters, - std::unique_ptr views, +MeterContext::MeterContext(std::unique_ptr views, opentelemetry::sdk::resource::Resource resource) noexcept - : resource_{resource}, - exporters_(std::move(exporters)), - views_(std::move(views)), - sdk_start_ts_{std::chrono::system_clock::now()} + : resource_{resource}, views_(std::move(views)), sdk_start_ts_{std::chrono::system_clock::now()} {} const resource::Resource &MeterContext::GetResource() const noexcept @@ -49,11 +44,6 @@ opentelemetry::common::SystemTimestamp MeterContext::GetSDKStartTime() noexcept return sdk_start_ts_; } -void MeterContext::AddMetricExporter(std::unique_ptr exporter) noexcept -{ - exporters_.push_back(std::move(exporter)); -} - void MeterContext::AddMetricReader(std::unique_ptr reader) noexcept { auto collector = @@ -75,51 +65,41 @@ void MeterContext::AddMeter(std::shared_ptr meter) bool MeterContext::Shutdown() noexcept { - bool return_status = true; + bool result = true; if (!shutdown_latch_.test_and_set(std::memory_order_acquire)) { - bool result_exporter = true; - bool result_reader = true; - bool result_collector = true; - for (auto &exporter : exporters_) - { - bool status = exporter->Shutdown(); - result_exporter = result_exporter && status; - } - if (!result_exporter) - { - OTEL_INTERNAL_LOG_WARN("[MeterContext::Shutdown] Unable to shutdown all metric exporters"); - } for (auto &collector : collectors_) { - bool status = std::static_pointer_cast(collector)->Shutdown(); - result_collector = result_reader && status; + bool status = std::static_pointer_cast(collector)->Shutdown(); + result = result && status; } - if (!result_collector) + if (!result) { OTEL_INTERNAL_LOG_WARN("[MeterContext::Shutdown] Unable to shutdown all metric readers"); } - return_status = result_exporter && result_collector; } - return return_status; + return result; } bool MeterContext::ForceFlush(std::chrono::microseconds timeout) noexcept { // TODO - Implement timeout logic. - const std::lock_guard locked(forceflush_lock_); - bool result_exporter = true; - for (auto &exporter : exporters_) - { - bool status = exporter->ForceFlush(timeout); - result_exporter = result_exporter && status; - } - if (!result_exporter) + bool result = true; + if (!shutdown_latch_.test_and_set(std::memory_order_acquire)) { - OTEL_INTERNAL_LOG_WARN("[MeterContext::ForceFlush] Unable to force-flush all metric exporters"); + + for (auto &collector : collectors_) + { + bool status = std::static_pointer_cast(collector)->ForceFlush(timeout); + result = result && status; + } + if (!result) + { + OTEL_INTERNAL_LOG_WARN("[MeterContext::ForceFlush] Unable to ForceFlush all metric readers"); + } } - return result_exporter; + return result; } } // namespace metrics diff --git a/sdk/src/metrics/meter_provider.cc b/sdk/src/metrics/meter_provider.cc index 8a9572c88c..788811cd61 100644 --- a/sdk/src/metrics/meter_provider.cc +++ b/sdk/src/metrics/meter_provider.cc @@ -4,7 +4,6 @@ #ifndef ENABLE_METRICS_PREVIEW # include "opentelemetry/sdk/metrics/meter_provider.h" # include "opentelemetry/metrics/meter.h" -# include "opentelemetry/sdk/metrics/metric_exporter.h" # include "opentelemetry/sdk/metrics/metric_reader.h" # include "opentelemetry/sdk/common/global_log_handler.h" @@ -23,10 +22,9 @@ namespace metrics_api = opentelemetry::metrics; MeterProvider::MeterProvider(std::shared_ptr context) noexcept : context_{context} {} -MeterProvider::MeterProvider(std::vector> &&exporters, - std::unique_ptr views, +MeterProvider::MeterProvider(std::unique_ptr views, sdk::resource::Resource resource) noexcept - : context_(std::make_shared(std::move(exporters), std::move(views), resource)) + : context_(std::make_shared(std::move(views), resource)) {} nostd::shared_ptr MeterProvider::GetMeter( @@ -61,11 +59,6 @@ const resource::Resource &MeterProvider::GetResource() const noexcept return context_->GetResource(); } -void MeterProvider::AddMetricExporter(std::unique_ptr exporter) noexcept -{ - return context_->AddMetricExporter(std::move(exporter)); -} - void MeterProvider::AddMetricReader(std::unique_ptr reader) noexcept { return context_->AddMetricReader(std::move(reader)); diff --git a/sdk/src/metrics/state/sync_metric_storage.cc b/sdk/src/metrics/state/sync_metric_storage.cc index f42de82b4b..8c79f9d7ca 100644 --- a/sdk/src/metrics/state/sync_metric_storage.cc +++ b/sdk/src/metrics/state/sync_metric_storage.cc @@ -25,104 +25,9 @@ bool SyncMetricStorage::Collect(CollectorHandle *collector, // recordings std::shared_ptr delta_metrics = std::move(attributes_hashmap_); attributes_hashmap_.reset(new AttributesHashMap); - for (auto &col : collectors) - { - unreported_metrics_[col.get()].push_back(delta_metrics); - } - // Get the unreported metrics for the `collector` from `unreported metrics stash` - // since last collection, this will also cleanup the unreported metrics for `collector` - // from the stash. - auto present = unreported_metrics_.find(collector); - if (present == unreported_metrics_.end()) - { - // no unreported metrics for the collector, return. - return true; - } - auto unreported_list = std::move(present->second); - - // Iterate over the unreporter metrics for `collector` and store result in `merged_metrics` - std::unique_ptr merged_metrics(new AttributesHashMap); - for (auto &agg_hashmap : unreported_list) - { - agg_hashmap->GetAllEnteries([&merged_metrics, this](const MetricAttributes &attributes, - Aggregation &aggregation) { - auto agg = merged_metrics->Get(attributes); - if (agg) - { - merged_metrics->Set(attributes, std::move(agg->Merge(aggregation))); - } - else - { - merged_metrics->Set( - attributes, - std::move( - DefaultAggregation::CreateAggregation(instrument_descriptor_)->Merge(aggregation))); - merged_metrics->GetAllEnteries( - [](const MetricAttributes &attr, Aggregation &aggr) { return true; }); - } - return true; - }); - } - // Get the last reported metrics for the `collector` from `last reported metrics` stash - // - If the aggregation_temporarily for the collector is cumulative - // - Merge the last reported metrics with unreported metrics (which is in merged_metrics), - // Final result of merge would be in merged_metrics. - // - Move the final merge to the `last reported metrics` stash. - // - If the aggregation_temporarily is delta - // - Store the unreported metrics for `collector` (which is in merged_mtrics) to - // `last reported metrics` stash. - - auto reported = last_reported_metrics_.find(collector); - if (reported != last_reported_metrics_.end()) - { - last_collection_ts = last_reported_metrics_[collector].collection_ts; - auto last_aggr_hashmap = std::move(last_reported_metrics_[collector].attributes_map); - if (aggregation_temporarily == AggregationTemporality::kCumulative) - { - // merge current delta to previous cumulative - last_aggr_hashmap->GetAllEnteries( - [&merged_metrics, this](const MetricAttributes &attributes, Aggregation &aggregation) { - auto agg = merged_metrics->Get(attributes); - if (agg) - { - merged_metrics->Set(attributes, agg->Merge(aggregation)); - } - else - { - merged_metrics->Set(attributes, - DefaultAggregation::CreateAggregation(instrument_descriptor_)); - } - return true; - }); - } - last_reported_metrics_[collector] = - LastReportedMetrics{std::move(merged_metrics), collection_ts}; - } - else - { - merged_metrics->GetAllEnteries( - [](const MetricAttributes &attr, Aggregation &aggr) { return true; }); - last_reported_metrics_.insert( - std::make_pair(collector, LastReportedMetrics{std::move(merged_metrics), collection_ts})); - } - - // Generate the MetricData from the final merged_metrics, and invoke callback over it. - - AttributesHashMap *result_to_export = (last_reported_metrics_[collector]).attributes_map.get(); - MetricData metric_data; - metric_data.instrument_descriptor = instrument_descriptor_; - metric_data.start_ts = last_collection_ts; - metric_data.end_ts = collection_ts; - result_to_export->GetAllEnteries( - [&metric_data](const MetricAttributes &attributes, Aggregation &aggregation) { - PointDataAttributes point_data_attr; - point_data_attr.point_data = aggregation.ToPoint(); - point_data_attr.attributes = attributes; - metric_data.point_data_attr_.push_back(point_data_attr); - return true; - }); - return callback(metric_data); + return temporal_metric_storage_.buildMetrics(collector, collectors, sdk_start_ts, collection_ts, + std::move(delta_metrics), callback); } } // namespace metrics diff --git a/sdk/src/metrics/state/temporal_metric_storage.cc b/sdk/src/metrics/state/temporal_metric_storage.cc new file mode 100644 index 0000000000..55e93e3d46 --- /dev/null +++ b/sdk/src/metrics/state/temporal_metric_storage.cc @@ -0,0 +1,131 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/sdk/metrics/state/temporal_metric_storage.h" +# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +TemporalMetricStorage::TemporalMetricStorage(InstrumentDescriptor instrument_descriptor) + : instrument_descriptor_(instrument_descriptor) +{} + +bool TemporalMetricStorage::buildMetrics(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + std::shared_ptr delta_metrics, + nostd::function_ref callback) noexcept +{ + std::lock_guard guard(lock_); + opentelemetry::common::SystemTimestamp last_collection_ts = sdk_start_ts; + auto aggregation_temporarily = collector->GetAggregationTemporality(); + for (auto &col : collectors) + { + unreported_metrics_[col.get()].push_back(delta_metrics); + } + + // Get the unreported metrics for the `collector` from `unreported metrics stash` + // since last collection, this will also cleanup the unreported metrics for `collector` + // from the stash. + auto present = unreported_metrics_.find(collector); + if (present == unreported_metrics_.end()) + { + // no unreported metrics for the collector, return. + return true; + } + auto unreported_list = std::move(present->second); + // Iterate over the unreporter metrics for `collector` and store result in `merged_metrics` + std::unique_ptr merged_metrics(new AttributesHashMap); + for (auto &agg_hashmap : unreported_list) + { + agg_hashmap->GetAllEnteries( + [&merged_metrics, this](const MetricAttributes &attributes, Aggregation &aggregation) { + auto agg = merged_metrics->Get(attributes); + if (agg) + { + merged_metrics->Set(attributes, agg->Merge(aggregation)); + } + else + { + merged_metrics->Set( + attributes, + DefaultAggregation::CreateAggregation(instrument_descriptor_)->Merge(aggregation)); + merged_metrics->GetAllEnteries( + [](const MetricAttributes &attr, Aggregation &aggr) { return true; }); + } + return true; + }); + } + // Get the last reported metrics for the `collector` from `last reported metrics` stash + // - If the aggregation_temporarily for the collector is cumulative + // - Merge the last reported metrics with unreported metrics (which is in merged_metrics), + // Final result of merge would be in merged_metrics. + // - Move the final merge to the `last reported metrics` stash. + // - If the aggregation_temporarily is delta + // - Store the unreported metrics for `collector` (which is in merged_mtrics) to + // `last reported metrics` stash. + + auto reported = last_reported_metrics_.find(collector); + if (reported != last_reported_metrics_.end()) + { + last_collection_ts = last_reported_metrics_[collector].collection_ts; + auto last_aggr_hashmap = std::move(last_reported_metrics_[collector].attributes_map); + if (aggregation_temporarily == AggregationTemporality::kCumulative) + { + // merge current delta to previous cumulative + last_aggr_hashmap->GetAllEnteries( + [&merged_metrics, this](const MetricAttributes &attributes, Aggregation &aggregation) { + auto agg = merged_metrics->Get(attributes); + if (agg) + { + merged_metrics->Set(attributes, agg->Merge(aggregation)); + } + else + { + merged_metrics->Set(attributes, + DefaultAggregation::CreateAggregation(instrument_descriptor_)); + } + return true; + }); + } + last_reported_metrics_[collector] = + LastReportedMetrics{std::move(merged_metrics), collection_ts}; + } + else + { + merged_metrics->GetAllEnteries( + [](const MetricAttributes &attr, Aggregation &aggr) { return true; }); + last_reported_metrics_.insert( + std::make_pair(collector, LastReportedMetrics{std::move(merged_metrics), collection_ts})); + } + + // Generate the MetricData from the final merged_metrics, and invoke callback over it. + + AttributesHashMap *result_to_export = (last_reported_metrics_[collector]).attributes_map.get(); + MetricData metric_data; + metric_data.instrument_descriptor = instrument_descriptor_; + metric_data.start_ts = last_collection_ts; + metric_data.end_ts = collection_ts; + result_to_export->GetAllEnteries( + [&metric_data](const MetricAttributes &attributes, Aggregation &aggregation) { + PointDataAttributes point_data_attr; + point_data_attr.point_data = aggregation.ToPoint(); + point_data_attr.attributes = attributes; + metric_data.point_data_attr_.push_back(point_data_attr); + return true; + }); + return callback(metric_data); +} + +} // namespace metrics + +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/sdk/test/metrics/async_metric_storage_test.cc b/sdk/test/metrics/async_metric_storage_test.cc index 512e24472c..a4decaaa79 100644 --- a/sdk/test/metrics/async_metric_storage_test.cc +++ b/sdk/test/metrics/async_metric_storage_test.cc @@ -18,46 +18,114 @@ using namespace opentelemetry::sdk::metrics; using namespace opentelemetry::sdk::instrumentationlibrary; using namespace opentelemetry::sdk::resource; -class MockMetricReader : public MetricReader +using namespace opentelemetry::sdk::metrics; +using namespace opentelemetry::common; +using M = std::map; + +class MockCollectorHandle : public CollectorHandle { public: - MockMetricReader(AggregationTemporality aggr_temporality) : MetricReader(aggr_temporality) {} + MockCollectorHandle(AggregationTemporality temp) : temporality(temp) {} - virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept override { return true; } + AggregationTemporality GetAggregationTemporality() noexcept override { return temporality; } - virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept override { return true; } - - virtual void OnInitialized() noexcept override {} +private: + AggregationTemporality temporality; }; -void measurement_fetch(opentelemetry::metrics::ObserverResult &observer_result) +class WritableMetricStorageTestFixture : public ::testing::TestWithParam +{}; + +class MeasurementFetcher { - observer_result.Observe(20l); - observer_result.Observe(10l); -} +public: + static void Fetcher(opentelemetry::metrics::ObserverResult &observer_result) + { + fetch_count++; + if (fetch_count == 1) + { + observer_result.Observe(20l, {{"RequestType", "GET"}}); + observer_result.Observe(10l, {{"RequestType", "PUT"}}); + number_of_get += 20l; + number_of_put += 10l; + } + else if (fetch_count == 2) + { + observer_result.Observe(40l, {{"RequestType", "GET"}}); + observer_result.Observe(20l, {{"RequestType", "PUT"}}); + number_of_get += 40l; + number_of_put += 20l; + } + } + + static void init_values() + { + fetch_count = 0; + number_of_get = 0; + number_of_put = 0; + } -TEST(AsyncMetricStorageTest, BasicTests) + static size_t fetch_count; + static long number_of_get; + static long number_of_put; + static const size_t number_of_attributes = 2; // GET , PUT +}; + +size_t MeasurementFetcher::fetch_count; +long MeasurementFetcher::number_of_get; +long MeasurementFetcher::number_of_put; +const size_t MeasurementFetcher::number_of_attributes; + +TEST_P(WritableMetricStorageTestFixture, TestAggregation) { - auto metric_callback = [](MetricData &&metric_data) { return true; }; - InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kCounter, + MeasurementFetcher::init_values(); + AggregationTemporality temporality = GetParam(); + + InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kObservableCounter, InstrumentValueType::kLong}; auto sdk_start_ts = std::chrono::system_clock::now(); // Some computation here auto collection_ts = std::chrono::system_clock::now() + std::chrono::seconds(5); - std::vector> exporters; - std::shared_ptr meter_context(new MeterContext(std::move(exporters))); - std::unique_ptr metric_reader(new MockMetricReader(AggregationTemporality::kDelta)); + std::shared_ptr collector(new MockCollectorHandle(temporality)); + std::vector> collectors; + collectors.push_back(collector); + size_t count_attributes = 0; + long value = 0; - std::shared_ptr collector = std::shared_ptr( - new MetricCollector(std::move(meter_context), std::move(metric_reader))); + MeasurementFetcher measurement_fetcher; + opentelemetry::sdk::metrics::AsyncMetricStorage storage(instr_desc, AggregationType::kSum, + MeasurementFetcher::Fetcher, + new DefaultAttributesProcessor()); - std::vector> collectors{collector}; - - opentelemetry::sdk::metrics::AsyncMetricStorage storage( - instr_desc, AggregationType::kSum, &measurement_fetch, new DefaultAttributesProcessor()); - EXPECT_NO_THROW( - storage.Collect(collector.get(), collectors, sdk_start_ts, collection_ts, metric_callback)); + storage.Collect(collector.get(), collectors, sdk_start_ts, collection_ts, + [&](const MetricData data) { + for (auto data_attr : data.point_data_attr_) + { + auto data = opentelemetry::nostd::get(data_attr.point_data); + if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "GET") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), + MeasurementFetcher::number_of_get); + } + else if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "PUT") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), + MeasurementFetcher::number_of_put); + } + count_attributes++; + } + return true; + }); + EXPECT_EQ(MeasurementFetcher::number_of_attributes, count_attributes); } + +INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestLong, + WritableMetricStorageTestFixture, + ::testing::Values(AggregationTemporality::kCumulative, + AggregationTemporality::kDelta)); + #endif \ No newline at end of file diff --git a/sdk/test/metrics/meter_provider_sdk_test.cc b/sdk/test/metrics/meter_provider_sdk_test.cc index 4a2792c3a7..b0fabe50bb 100644 --- a/sdk/test/metrics/meter_provider_sdk_test.cc +++ b/sdk/test/metrics/meter_provider_sdk_test.cc @@ -38,16 +38,19 @@ class MockMetricExporter : public MetricExporter class MockMetricReader : public MetricReader { public: + MockMetricReader(std::unique_ptr exporter) : exporter_(std::move(exporter)) {} virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept override { return true; } virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept override { return true; } virtual void OnInitialized() noexcept override {} + +private: + std::unique_ptr exporter_; }; TEST(MeterProvider, GetMeter) { - std::vector> exporters; - MeterProvider mp1(std::move(exporters)); + MeterProvider mp1; // std::unique_ptr view{std::unique_ptr()}; // MeterProvider mp1(std::move(exporters), std::move(readers), std::move(views); auto m1 = mp1.GetMeter("test"); @@ -74,11 +77,8 @@ TEST(MeterProvider, GetMeter) auto sdkMeter1 = static_cast(m1.get()); # endif ASSERT_NE(nullptr, sdkMeter1); - - std::unique_ptr exporter{new MockMetricExporter()}; - ASSERT_NO_THROW(mp1.AddMetricExporter(std::move(exporter))); - - std::unique_ptr reader{new MockMetricReader()}; + std::unique_ptr exporter(new MockMetricExporter()); + std::unique_ptr reader{new MockMetricReader(std::move(exporter))}; ASSERT_NO_THROW(mp1.AddMetricReader(std::move(reader))); std::unique_ptr view{std::unique_ptr()}; diff --git a/sdk/test/metrics/metric_reader_test.cc b/sdk/test/metrics/metric_reader_test.cc index 61a4364934..c9c30853df 100644 --- a/sdk/test/metrics/metric_reader_test.cc +++ b/sdk/test/metrics/metric_reader_test.cc @@ -27,12 +27,11 @@ TEST(MetricReaderTest, BasicTests) std::unique_ptr metric_reader1(new MockMetricReader(aggr_temporality)); EXPECT_EQ(metric_reader1->GetAggregationTemporality(), aggr_temporality); - std::vector> exporters; - std::shared_ptr meter_context1(new MeterContext(std::move(exporters))); + std::shared_ptr meter_context1(new MeterContext()); EXPECT_NO_THROW(meter_context1->AddMetricReader(std::move(metric_reader1))); std::unique_ptr metric_reader2(new MockMetricReader(aggr_temporality)); - std::shared_ptr meter_context2(new MeterContext(std::move(exporters))); + std::shared_ptr meter_context2(new MeterContext()); MetricProducer *metric_producer = new MetricCollector(std::move(meter_context2), std::move(metric_reader2)); EXPECT_NO_THROW(metric_producer->Collect([](ResourceMetrics &metric_data) { return true; })); diff --git a/sdk/test/metrics/view_registry_test.cc b/sdk/test/metrics/view_registry_test.cc index c3a9923c50..8151d37545 100644 --- a/sdk/test/metrics/view_registry_test.cc +++ b/sdk/test/metrics/view_registry_test.cc @@ -25,13 +25,15 @@ TEST(ViewRegistry, FindViewsEmptyRegistry) InstrumentationLibrary::Create("default", "1.0.0", "https://opentelemetry.io/schemas/1.7.0"); int count = 0; ViewRegistry registry; - auto status = registry.FindViews(default_instrument_descriptor, - *default_instrumentation_lib.get(), [&count](const View &view) { - count++; - EXPECT_EQ(view.GetName(), ""); - EXPECT_EQ(view.GetDescription(), ""); - return true; - }); + auto status = + registry.FindViews(default_instrument_descriptor, *default_instrumentation_lib.get(), + [&count](const View &view) { + count++; + EXPECT_EQ(view.GetName(), "otel-default-view"); + EXPECT_EQ(view.GetDescription(), ""); + EXPECT_EQ(view.GetAggregationType(), AggregationType::kDefault); + return true; + }); EXPECT_EQ(count, 1); EXPECT_EQ(status, true); }