diff --git a/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h b/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h new file mode 100644 index 0000000000..49ae1ed56c --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/data/exemplar_data.h @@ -0,0 +1,84 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/context/context.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/data/metric_data.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +/** + * A sample input measurement. + * + * Exemplars also hold information about the environment when the measurement was recorded, for + * example the span and trace ID of the active span when the exemplar was recorded. + */ +class ExemplarData +{ +public: + static ExemplarData Create(std::shared_ptr context, + const opentelemetry::common::SystemTimestamp ×tamp, + const PointDataAttributes &point_data_attr) + { + return ExemplarData(context, timestamp, point_data_attr); + } + + /** + * The set of key/value pairs that were filtered out by the aggregator, but recorded alongside + * the original measurement. Only key/value pairs that were filtered out by the aggregator + * should be included + */ + MetricAttributes GetFilteredAttributes() { return MetricAttributes{}; } + + /** Returns the timestamp in nanos when measurement was collected. */ + opentelemetry::common::SystemTimestamp GetEpochNanos() { return timestamp_; } + + /** + * Returns the SpanContext associated with this exemplar. If the exemplar was not recorded + * inside a sampled trace, the Context will be invalid. + */ + const trace::SpanContext &GetSpanContext() const noexcept { return context_; } + + static PointType CreateSumPointData(ValueType value) + { + SumPointData sum_point_data{}; + sum_point_data.value_ = value; + return sum_point_data; + } + + static PointType CreateLastValuePointData(ValueType value) + { + LastValuePointData last_value_point_data{}; + last_value_point_data.value_ = value; + last_value_point_data.is_lastvalue_valid_ = true; + last_value_point_data.sample_ts_ = opentelemetry::common::SystemTimestamp{}; + return last_value_point_data; + } + + static PointType CreateDropPointData() { return DropPointData{}; } + +private: + ExemplarData(std::shared_ptr context, + opentelemetry::common::SystemTimestamp timestamp, + const PointDataAttributes &point_data_attr) + : context_(*context.get()), timestamp_(timestamp), point_data_attr_(point_data_attr) + {} + + trace::SpanContext context_; + opentelemetry::common::SystemTimestamp timestamp_; + PointDataAttributes point_data_attr_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h index c21b4cc84a..366488b2b6 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h @@ -14,12 +14,6 @@ namespace metrics class AlwaysSampleFilter final : public ExemplarFilter { public: - static nostd::shared_ptr GetAlwaysSampleFilter() - { - static nostd::shared_ptr alwaysSampleFilter{new AlwaysSampleFilter{}}; - return alwaysSampleFilter; - } - bool ShouldSampleMeasurement( long /* value */, const MetricAttributes & /* attributes */, @@ -36,7 +30,6 @@ class AlwaysSampleFilter final : public ExemplarFilter return true; } -private: explicit AlwaysSampleFilter() = default; }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h deleted file mode 100644 index 14eac62499..0000000000 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once -#ifndef ENABLE_METRICS_PREVIEW -# include "opentelemetry/common/timestamp.h" -# include "opentelemetry/context/context.h" -# include "opentelemetry/sdk/common/attribute_utils.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace metrics -{ -using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; -/** - * A sample input measurement. - * - * Exemplars also hold information about the environment when the measurement was recorded, for - * example the span and trace ID of the active span when the exemplar was recorded. - */ -class ExemplarData -{ -public: - /** - * The set of key/value pairs that were filtered out by the aggregator, but recorded alongside the - * original measurement. Only key/value pairs that were filtered out by the aggregator should be - * included - */ - MetricAttributes GetFilteredAttributes(); - - /** Returns the timestamp in nanos when measurement was collected. */ - opentelemetry::common::SystemTimestamp GetEpochNanos(); - - /** - * Returns the SpanContext associated with this exemplar. If the exemplar was not recorded - * inside a sampled trace, the Context will be invalid. - */ - opentelemetry::context::Context GetSpanContext(); -}; - -} // namespace metrics -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE -#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h index 4b512e1317..87e27b81c2 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h @@ -31,6 +31,10 @@ class ExemplarFilter const opentelemetry::context::Context &context) noexcept = 0; virtual ~ExemplarFilter() = default; + + static std::shared_ptr GetNeverSampleFilter() noexcept; + static std::shared_ptr GetAlwaysSampleFilter() noexcept; + static std::shared_ptr GetWithTraceSampleFilter() noexcept; }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h new file mode 100644 index 0000000000..2db5532883 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h @@ -0,0 +1,65 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/exemplar/filter.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class FilteredExemplarReservoir final : public ExemplarReservoir +{ + +public: + FilteredExemplarReservoir(std::shared_ptr filter, + std::shared_ptr reservoir) + : filter_(filter), reservoir_(reservoir) + {} + + void OfferMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept override + { + if (filter_->ShouldSampleMeasurement(value, attributes, context)) + { + reservoir_->OfferMeasurement(value, attributes, context, timestamp); + } + } + + void OfferMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept override + { + if (filter_->ShouldSampleMeasurement(value, attributes, context)) + { + reservoir_->OfferMeasurement(value, attributes, context, timestamp); + } + } + + std::vector> CollectAndReset( + const MetricAttributes &pointAttributes) noexcept override + { + return reservoir_->CollectAndReset(pointAttributes); + } + +private: + explicit FilteredExemplarReservoir() = default; + std::shared_ptr filter_; + std::shared_ptr reservoir_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.h new file mode 100644 index 0000000000..1a0abeb6b6 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.h @@ -0,0 +1,102 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/function_ref.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class FixedSizeExemplarReservoir : public ExemplarReservoir +{ + +public: + FixedSizeExemplarReservoir(size_t size, + std::shared_ptr reservoir_cell_selector, + std::shared_ptr (ReservoirCell::*map_and_reset_cell)( + const common::OrderedAttributeMap &attributes)) + : storage_(size), + reservoir_cell_selector_(reservoir_cell_selector), + map_and_reset_cell_(map_and_reset_cell) + {} + + void OfferMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept override + { + if (!reservoir_cell_selector_) + { + return; + } + auto idx = + reservoir_cell_selector_->ReservoirCellIndexFor(storage_, value, attributes, context); + if (idx != -1) + { + storage_[idx].RecordDoubleMeasurement(value, attributes, context); + } + } + + void OfferMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept override + { + if (!reservoir_cell_selector_) + { + return; + } + auto idx = + reservoir_cell_selector_->ReservoirCellIndexFor(storage_, value, attributes, context); + if (idx != -1) + { + storage_[idx].RecordDoubleMeasurement(value, attributes, context); + } + } + + std::vector> CollectAndReset( + const MetricAttributes &pointAttributes) noexcept override + { + std::vector> results; + if (!reservoir_cell_selector_) + { + return results; + } + if (!map_and_reset_cell_) + { + reservoir_cell_selector_.reset(); + return results; + } + for (auto reservoirCell : storage_) + { + auto result = (reservoirCell.*(map_and_reset_cell_))(pointAttributes); + results.push_back(result); + } + reservoir_cell_selector_.reset(); + return results; + } + +private: + explicit FixedSizeExemplarReservoir() = default; + std::vector storage_; + std::shared_ptr reservoir_cell_selector_; + std::shared_ptr (ReservoirCell::*map_and_reset_cell_)( + const common::OrderedAttributeMap &attributes); +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h new file mode 100644 index 0000000000..83f410f815 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h @@ -0,0 +1,80 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/data/exemplar_data.h" +# include "opentelemetry/sdk/metrics/exemplar/filter.h" +# include "opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class HistogramExemplarReservoir : public FixedSizeExemplarReservoir +{ + +public: + static std::shared_ptr GetHistogramCellSelector( + const std::vector &boundaries = std::vector{1.0, 2.0, 3.0, 4.0, 5.0}) + { + return std::shared_ptr{new HistogramCellSelector(boundaries)}; + } + + HistogramExemplarReservoir(size_t size, + std::shared_ptr reservoir_cell_selector, + std::shared_ptr (ReservoirCell::*map_and_reset_cell)( + const common::OrderedAttributeMap &attributes)) + : FixedSizeExemplarReservoir(size, reservoir_cell_selector, map_and_reset_cell) + {} + + class HistogramCellSelector : public ReservoirCellSelector + { + public: + HistogramCellSelector(const std::vector &boundaries) : boundaries_(boundaries) {} + + int ReservoirCellIndexFor(const std::vector &cells, + long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) override + { + return ReservoirCellIndexFor(cells, (double)value, attributes, context); + } + + int ReservoirCellIndexFor(const std::vector &cells, + double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) override + { + for (size_t i = 0; i < boundaries_.size(); ++i) + { + if (value <= boundaries_[i]) + { + return i; + } + } + return -1; + } + + private: + void reset() override + { + // Do nothing + } + std::vector boundaries_; + }; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h index 53d38142c1..844bf72e10 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h @@ -14,12 +14,6 @@ namespace metrics class NeverSampleFilter final : public ExemplarFilter { public: - static nostd::shared_ptr GetNeverSampleFilter() - { - nostd::shared_ptr neverSampleFilter{new NeverSampleFilter{}}; - return neverSampleFilter; - } - bool ShouldSampleMeasurement( long /* value */, const MetricAttributes & /* attributes */, @@ -36,7 +30,6 @@ class NeverSampleFilter final : public ExemplarFilter return false; } -private: explicit NeverSampleFilter() = default; }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h index 5ea1233c10..38c6c74c60 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h @@ -18,11 +18,6 @@ class NoExemplarReservoir final : public ExemplarReservoir { public: - static nostd::shared_ptr GetNoExemplarReservoir() - { - return nostd::shared_ptr{new NoExemplarReservoir{}}; - } - void OfferMeasurement( long /* value */, const MetricAttributes & /* attributes */, @@ -41,13 +36,12 @@ class NoExemplarReservoir final : public ExemplarReservoir // Stores nothing. } - std::vector CollectAndReset( + std::vector> CollectAndReset( const MetricAttributes & /* pointAttributes */) noexcept override { - return std::vector{}; + return std::vector>{}; } -private: explicit NoExemplarReservoir() = default; }; diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h index 25e8421d6b..db8a37c260 100644 --- a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h @@ -4,7 +4,9 @@ #pragma once #ifndef ENABLE_METRICS_PREVIEW # include -# include "opentelemetry/sdk/metrics/exemplar/data.h" +# include "opentelemetry/sdk/metrics/data/exemplar_data.h" +# include "opentelemetry/sdk/metrics/exemplar/filter.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -45,8 +47,20 @@ class ExemplarReservoir * @return A vector of sampled exemplars for this point. Implementers are expected to * filter out pointAttributes from the original recorded attributes. */ - virtual std::vector CollectAndReset( + virtual std::vector> CollectAndReset( const MetricAttributes &pointAttributes) noexcept = 0; + + static nostd::shared_ptr GetFilteredExemplarReservoir( + std::shared_ptr filter, + std::shared_ptr reservoir); + + static nostd::shared_ptr GetHistogramExemplarReservoir( + size_t size, + std::shared_ptr reservoir_cell_selector, + std::shared_ptr (ReservoirCell::*map_and_reset_cell)( + const common::OrderedAttributeMap &attributes)); + + static nostd::shared_ptr GetNoExemplarReservoir(); }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h new file mode 100644 index 0000000000..9a84155f2a --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell.h @@ -0,0 +1,152 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/data/exemplar_data.h" +# include "opentelemetry/sdk/metrics/exemplar/filter.h" +# include "opentelemetry/trace/context.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +/** + * A Reservoir cell pre-allocated memories for Exemplar data. + */ +class ReservoirCell +{ +public: + ReservoirCell() = default; + + /** + * Record the long measurement to the cell. + */ + void RecordLongMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) + { + value_ = value; + offerMeasurement(attributes, context); + } + + /** + * Record the long measurement to the cell. + */ + void RecordDoubleMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) + { + value_ = value; + offerMeasurement(attributes, context); + } + + /** + * Retrieve the cell's {@link ExemplarData}. + * + *

Must be used in tandem with {@link #recordLongMeasurement(long, Attributes, Context)}. + */ + std::shared_ptr GetAndResetLong(const MetricAttributes &point_attributes) + { + if (!context_) + { + return nullptr; + } + auto attributes = attributes_; + PointDataAttributes point_data_attributes; + point_data_attributes.attributes = filtered(attributes, point_attributes); + if (nostd::holds_alternative(value_)) + { + point_data_attributes.point_data = ExemplarData::CreateSumPointData(nostd::get(value_)); + } + std::shared_ptr result{ + new ExemplarData{ExemplarData::Create(context_, record_time_, point_data_attributes)}}; + reset(); + return result; + } + + /** + * Retrieve the cell's {@link ExemplarData}. + * + *

Must be used in tandem with {@link #recordDoubleMeasurement(double, Attributes, Context)}. + */ + std::shared_ptr GetAndResetDouble(const MetricAttributes &point_attributes) + { + if (!context_) + { + return nullptr; + } + auto attributes = attributes_; + PointDataAttributes point_data_attributes; + point_data_attributes.attributes = filtered(attributes, point_attributes); + if (nostd::holds_alternative(value_)) + { + point_data_attributes.point_data = + ExemplarData::CreateSumPointData(nostd::get(value_)); + } + std::shared_ptr result{ + new ExemplarData{ExemplarData::Create(context_, record_time_, point_data_attributes)}}; + reset(); + return result; + } + + void reset() + { + value_ = 0.0; + record_time_ = opentelemetry::common::SystemTimestamp{}; + } + +private: + /** Returns filtered attributes for exemplars. */ + static MetricAttributes filtered(const MetricAttributes &original, + const MetricAttributes &metric_point) + { + auto res = original; + for (const auto &kv : metric_point) + { + auto it = res.find(kv.first); + if (it != res.end()) + { + res.erase(it); + } + } + return res; + } + + void offerMeasurement(const MetricAttributes &attributes, + const opentelemetry::context::Context &context) + { + attributes_ = attributes; + record_time_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); + auto span = opentelemetry::trace::GetSpan(context); + if (span) + { + auto current_ctx = span->GetContext(); + if (current_ctx.IsValid()) + { + context_.reset(new trace::SpanContext{current_ctx}); + } + } + } + + // Cell stores either long or double values, but must not store both + std::shared_ptr context_; + nostd::variant value_; + opentelemetry::common::SystemTimestamp record_time_; + MetricAttributes attributes_; + // For testing + friend class ReservoirCellTestPeer; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h new file mode 100644 index 0000000000..13e149f0aa --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir_cell_selector.h @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class ReservoirCellSelector +{ + +public: + virtual ~ReservoirCellSelector() = default; + + /** Determine the index of the {@code cells} to record the measurement to. */ + virtual int ReservoirCellIndexFor(const std::vector &cells, + long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) = 0; + + /** Determine the index of the {@code cells} to record the measurement to. */ + virtual int ReservoirCellIndexFor(const std::vector &cells, + double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) = 0; + + /** Called when {@link FixedSizeExemplarReservoir#CollectAndReset(Attributes)}. */ + virtual void reset() = 0; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/exemplar/with_trace_sample_filter.h b/sdk/include/opentelemetry/sdk/metrics/exemplar/with_trace_sample_filter.h new file mode 100644 index 0000000000..b81ccbccb2 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/exemplar/with_trace_sample_filter.h @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/filter.h" +# include "opentelemetry/trace/context.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class WithTraceSampleFilter final : public ExemplarFilter +{ +public: + bool ShouldSampleMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept override + { + return hasSampledTrace(context); + } + + bool ShouldSampleMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept override + { + return hasSampledTrace(context); + } + + explicit WithTraceSampleFilter() = default; + +private: + static bool hasSampledTrace(const opentelemetry::context::Context &context) + { + return opentelemetry::trace::GetSpan(context)->GetContext().IsValid() && + opentelemetry::trace::GetSpan(context)->GetContext().IsSampled(); + } +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/src/metrics/CMakeLists.txt b/sdk/src/metrics/CMakeLists.txt index dbbb465183..80c1bff335 100644 --- a/sdk/src/metrics/CMakeLists.txt +++ b/sdk/src/metrics/CMakeLists.txt @@ -13,6 +13,8 @@ add_library( aggregation/histogram_aggregation.cc aggregation/lastvalue_aggregation.cc aggregation/sum_aggregation.cc + exemplar/filter.cc + exemplar/reservoir.cc sync_instruments.cc) set_target_properties(opentelemetry_metrics PROPERTIES EXPORT_NAME metrics) diff --git a/sdk/src/metrics/exemplar/filter.cc b/sdk/src/metrics/exemplar/filter.cc new file mode 100644 index 0000000000..89d89b37a3 --- /dev/null +++ b/sdk/src/metrics/exemplar/filter.cc @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/filter.h" +# include "opentelemetry/sdk/metrics/exemplar/always_sample_filter.h" +# include "opentelemetry/sdk/metrics/exemplar/never_sample_filter.h" +# include "opentelemetry/sdk/metrics/exemplar/with_trace_sample_filter.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +std::shared_ptr ExemplarFilter::GetNeverSampleFilter() noexcept +{ + static std::shared_ptr neverSampleFilter{new NeverSampleFilter{}}; + return neverSampleFilter; +} + +std::shared_ptr ExemplarFilter::GetAlwaysSampleFilter() noexcept +{ + static std::shared_ptr alwaysSampleFilter{new AlwaysSampleFilter{}}; + return alwaysSampleFilter; +} + +std::shared_ptr ExemplarFilter::GetWithTraceSampleFilter() noexcept +{ + std::shared_ptr withTraceSampleFilter{new WithTraceSampleFilter{}}; + return withTraceSampleFilter; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/src/metrics/exemplar/reservoir.cc b/sdk/src/metrics/exemplar/reservoir.cc new file mode 100644 index 0000000000..6209d91e80 --- /dev/null +++ b/sdk/src/metrics/exemplar/reservoir.cc @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/reservoir.h" +# include +# include "opentelemetry/sdk/metrics/exemplar/filtered_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +nostd::shared_ptr ExemplarReservoir::GetFilteredExemplarReservoir( + std::shared_ptr filter, + std::shared_ptr reservoir) +{ + return nostd::shared_ptr{new FilteredExemplarReservoir{filter, reservoir}}; +} + +nostd::shared_ptr ExemplarReservoir::GetHistogramExemplarReservoir( + size_t size, + std::shared_ptr reservoir_cell_selector, + std::shared_ptr (ReservoirCell::*map_and_reset_cell)( + const common::OrderedAttributeMap &attributes)) +{ + return nostd::shared_ptr{ + new HistogramExemplarReservoir{size, reservoir_cell_selector, map_and_reset_cell}}; +} + +nostd::shared_ptr ExemplarReservoir::GetNoExemplarReservoir() +{ + return nostd::shared_ptr{new NoExemplarReservoir{}}; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index 4d6595dd7f..27b8044cdb 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -6,7 +6,7 @@ # include "opentelemetry/metrics/noop.h" # include "opentelemetry/nostd/shared_ptr.h" # include "opentelemetry/sdk/metrics/async_instruments.h" -# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h" # include "opentelemetry/sdk/metrics/state/multi_metric_storage.h" # include "opentelemetry/sdk/metrics/state/observable_registry.h" # include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" @@ -233,7 +233,7 @@ std::unique_ptr Meter::RegisterSyncMetricStorage( auto storage = std::shared_ptr(new SyncMetricStorage( view_instr_desc, view.GetAggregationType(), &view.GetAttributesProcessor(), - NoExemplarReservoir::GetNoExemplarReservoir(), view.GetAggregationConfig())); + ExemplarReservoir::GetNoExemplarReservoir(), view.GetAggregationConfig())); storage_registry_[instrument_descriptor.name_] = storage; multi_storage->AddStorage(storage); return true; diff --git a/sdk/test/metrics/exemplar/BUILD b/sdk/test/metrics/exemplar/BUILD index 6481f679d2..1009999d02 100644 --- a/sdk/test/metrics/exemplar/BUILD +++ b/sdk/test/metrics/exemplar/BUILD @@ -10,6 +10,24 @@ cc_test( deps = [ "//api", "//sdk:headers", + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "with_trace_sample_filter_test", + srcs = [ + "with_trace_sample_filter_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/metrics", "@com_google_googletest//:gtest_main", ], ) @@ -26,6 +44,7 @@ cc_test( deps = [ "//api", "//sdk:headers", + "//sdk/src/metrics", "@com_google_googletest//:gtest_main", ], ) @@ -42,6 +61,41 @@ cc_test( deps = [ "//api", "//sdk:headers", + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "reservoir_cell_test", + srcs = [ + "reservoir_cell_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "histogram_exemplar_reservoir_test", + srcs = [ + "histogram_exemplar_reservoir_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/metrics", "@com_google_googletest//:gtest_main", ], ) diff --git a/sdk/test/metrics/exemplar/CMakeLists.txt b/sdk/test/metrics/exemplar/CMakeLists.txt index 303294761a..33d04e913c 100644 --- a/sdk/test/metrics/exemplar/CMakeLists.txt +++ b/sdk/test/metrics/exemplar/CMakeLists.txt @@ -1,8 +1,12 @@ -foreach(testname no_exemplar_reservoir_test never_sample_filter_test - always_sample_filter_test) +foreach( + testname + no_exemplar_reservoir_test never_sample_filter_test always_sample_filter_test + histogram_exemplar_reservoir_test reservoir_cell_test + with_trace_sample_filter_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_metrics opentelemetry_metrics) gtest_add_tests( TARGET ${testname} TEST_PREFIX metrics. diff --git a/sdk/test/metrics/exemplar/always_sample_filter_test.cc b/sdk/test/metrics/exemplar/always_sample_filter_test.cc index cf4e449957..5f9716f60d 100644 --- a/sdk/test/metrics/exemplar/always_sample_filter_test.cc +++ b/sdk/test/metrics/exemplar/always_sample_filter_test.cc @@ -2,14 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 #ifndef ENABLE_METRICS_PREVIEW -# include "opentelemetry/sdk/metrics/exemplar/always_sample_filter.h" # include +# include "opentelemetry/sdk/metrics/exemplar/filter.h" using namespace opentelemetry::sdk::metrics; TEST(AlwaysSampleFilter, SampleMeasurement) { - auto filter = opentelemetry::sdk::metrics::AlwaysSampleFilter::GetAlwaysSampleFilter(); + auto filter = opentelemetry::sdk::metrics::ExemplarFilter::GetAlwaysSampleFilter(); ASSERT_TRUE( filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); ASSERT_TRUE( diff --git a/sdk/test/metrics/exemplar/histogram_exemplar_reservoir_test.cc b/sdk/test/metrics/exemplar/histogram_exemplar_reservoir_test.cc new file mode 100644 index 0000000000..1f7b3100ea --- /dev/null +++ b/sdk/test/metrics/exemplar/histogram_exemplar_reservoir_test.cc @@ -0,0 +1,35 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class HistogramExemplarReservoirTestPeer : public ::testing::Test +{ +public: +}; + +TEST_F(HistogramExemplarReservoirTestPeer, OfferMeasurement) +{ + std::vector boundaries{1, 5.0, 10, 15, 20}; + auto histogram_exemplar_reservoir = ExemplarReservoir::GetHistogramExemplarReservoir( + boundaries.size(), HistogramExemplarReservoir::GetHistogramCellSelector(boundaries), nullptr); + histogram_exemplar_reservoir->OfferMeasurement( + 1.0, MetricAttributes{}, opentelemetry::context::Context{}, std::chrono::system_clock::now()); + histogram_exemplar_reservoir->OfferMeasurement( + 1l, MetricAttributes{}, opentelemetry::context::Context{}, std::chrono::system_clock::now()); + auto exemplar_data = histogram_exemplar_reservoir->CollectAndReset(MetricAttributes{}); + ASSERT_TRUE(exemplar_data.empty()); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/test/metrics/exemplar/never_sample_filter_test.cc b/sdk/test/metrics/exemplar/never_sample_filter_test.cc index 930c572205..4e40015e8f 100644 --- a/sdk/test/metrics/exemplar/never_sample_filter_test.cc +++ b/sdk/test/metrics/exemplar/never_sample_filter_test.cc @@ -4,13 +4,13 @@ #include "opentelemetry/context/context.h" #ifndef ENABLE_METRICS_PREVIEW # include -# include "opentelemetry/sdk/metrics/exemplar/never_sample_filter.h" +# include "opentelemetry/sdk/metrics/exemplar/filter.h" using namespace opentelemetry::sdk::metrics; TEST(NeverSampleFilter, SampleMeasurement) { - auto filter = opentelemetry::sdk::metrics::NeverSampleFilter::GetNeverSampleFilter(); + auto filter = opentelemetry::sdk::metrics::ExemplarFilter::GetNeverSampleFilter(); ASSERT_FALSE( filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); ASSERT_FALSE( diff --git a/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc b/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc index 72341de9de..a0f645fee1 100644 --- a/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc +++ b/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc @@ -9,7 +9,7 @@ using namespace opentelemetry::sdk::metrics; TEST(NoExemplarReservoir, OfferMeasurement) { - auto reservoir = opentelemetry::sdk::metrics::NoExemplarReservoir::GetNoExemplarReservoir(); + auto reservoir = opentelemetry::sdk::metrics::ExemplarReservoir::GetNoExemplarReservoir(); reservoir->OfferMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{}, std::chrono::system_clock::now()); reservoir->OfferMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{}, diff --git a/sdk/test/metrics/exemplar/reservoir_cell_test.cc b/sdk/test/metrics/exemplar/reservoir_cell_test.cc new file mode 100644 index 0000000000..a42f11151a --- /dev/null +++ b/sdk/test/metrics/exemplar/reservoir_cell_test.cc @@ -0,0 +1,73 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/reservoir_cell.h" +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class ReservoirCellTestPeer : public ::testing::Test +{ +public: + long GetLongVal(const opentelemetry::sdk::metrics::ReservoirCell &reservoir_cell) + { + return nostd::get(reservoir_cell.value_); + } + + double GetDoubleVal(const opentelemetry::sdk::metrics::ReservoirCell &reservoir_cell) + { + return nostd::get(reservoir_cell.value_); + } + + opentelemetry::common::SystemTimestamp GetRecordTime( + const opentelemetry::sdk::metrics::ReservoirCell &reservoir_cell) + { + return reservoir_cell.record_time_; + } + + void FilteredTest() + { + MetricAttributes original{{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}; + MetricAttributes metric_point{{"k2", "v2"}, {"k4", "v4"}}; + MetricAttributes expected_result{{"k1", "v1"}, {"k3", "v3"}}; + auto result = opentelemetry::sdk::metrics::ReservoirCell::filtered(original, metric_point); + ASSERT_TRUE(result == expected_result); + } +}; + +TEST_F(ReservoirCellTestPeer, recordMeasurement) +{ + opentelemetry::sdk::metrics::ReservoirCell reservoir_cell; + reservoir_cell.RecordLongMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{}); + ASSERT_TRUE(GetLongVal(reservoir_cell) == 1); + + reservoir_cell.RecordDoubleMeasurement(1.5, MetricAttributes{}, + opentelemetry::context::Context{}); + ASSERT_TRUE(GetDoubleVal(reservoir_cell) == 1.5); +} + +TEST_F(ReservoirCellTestPeer, GetAndReset) +{ + opentelemetry::sdk::metrics::ReservoirCell reservoir_cell; + auto double_data = reservoir_cell.GetAndResetDouble(MetricAttributes{}); + ASSERT_TRUE(GetRecordTime(reservoir_cell) == opentelemetry::common::SystemTimestamp{}); + ASSERT_TRUE(double_data == nullptr); + + auto long_data = reservoir_cell.GetAndResetLong(MetricAttributes{}); + ASSERT_TRUE(GetRecordTime(reservoir_cell) == opentelemetry::common::SystemTimestamp{}); + ASSERT_TRUE(long_data == nullptr); +} + +TEST_F(ReservoirCellTestPeer, Filtered) +{ + FilteredTest(); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc b/sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc new file mode 100644 index 0000000000..1c46dcddd6 --- /dev/null +++ b/sdk/test/metrics/exemplar/with_trace_sample_filter_test.cc @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/context/context.h" +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/exemplar/filter.h" + +using namespace opentelemetry::sdk::metrics; + +TEST(WithTraceSampleFilter, SampleMeasurement) +{ + auto filter = opentelemetry::sdk::metrics::ExemplarFilter::GetWithTraceSampleFilter(); + ASSERT_FALSE( + filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); + ASSERT_FALSE( + filter->ShouldSampleMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{})); +} + +#endif diff --git a/sdk/test/metrics/sync_metric_storage_counter_test.cc b/sdk/test/metrics/sync_metric_storage_counter_test.cc index 9690e977d0..555ae8ce09 100644 --- a/sdk/test/metrics/sync_metric_storage_counter_test.cc +++ b/sdk/test/metrics/sync_metric_storage_counter_test.cc @@ -5,7 +5,7 @@ #ifndef ENABLE_METRICS_PREVIEW # include "opentelemetry/common/key_value_iterable_view.h" # include "opentelemetry/nostd/shared_ptr.h" -# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.h" # include "opentelemetry/sdk/metrics/instruments.h" # include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" # include "opentelemetry/sdk/metrics/view/attributes_processor.h" @@ -53,7 +53,7 @@ TEST_P(WritableMetricStorageTestFixture, LongSumAggregation) new DefaultAttributesProcessor{}}; opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor.get(), - NoExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), std::shared_ptr{}); storage.RecordLong(10l, KeyValueIterableView>(attributes_get), @@ -188,7 +188,7 @@ TEST_P(WritableMetricStorageTestFixture, DoubleSumAggregation) new DefaultAttributesProcessor{}}; opentelemetry::sdk::metrics::SyncMetricStorage storage( instr_desc, AggregationType::kSum, default_attributes_processor.get(), - NoExemplarReservoir::GetNoExemplarReservoir(), + ExemplarReservoir::GetNoExemplarReservoir(), std::shared_ptr{}); storage.RecordDouble(10.0,