From a69c62d690704293e3eaef4d1d30c79e83dc82ec Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 19 Dec 2023 11:55:16 -0500 Subject: [PATCH 1/7] feat: exemplar init commit --- .../exemplar/always_off_exemplar_filter.rb | 19 +++++++++++++ .../exemplar/always_on_exemplar_filter.rb | 19 +++++++++++++ .../sdk/metrics/exemplar/exemplar.rb | 23 +++++++++++++++ .../sdk/metrics/exemplar/exemplar_filter.rb | 24 ++++++++++++++++ .../metrics/exemplar/exemplar_reservoir.rb | 28 +++++++++++++++++++ .../exemplar/fixed_size_exemplar_reservoir.rb | 20 +++++++++++++ .../exemplar/histogram_exemplar_reservoir.rb | 20 +++++++++++++ .../exemplar/trace_based_exemplar_filter.rb | 19 +++++++++++++ .../sdk/metrics/meter_provider.rb | 5 ++++ 9 files changed, 177 insertions(+) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb new file mode 100644 index 0000000000..bceb6b40b2 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class AlwaysOffExemplarFilter < ExemplarFilter + def should_sample?(value, timestamp, attributes, context) + false + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb new file mode 100644 index 0000000000..1052163bef --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class AlwaysOnExemplarFilter < ExemplarFilter + def should_sample?(value, timestamp, attributes, context) + true + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb new file mode 100644 index 0000000000..33990d0ff2 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class Exemplar + def initialize(value, time_unix_nano, attributes) + @value = value + @time_unix_nano = time_unix_nano + @attributes = attributes + @span_id = nil + @trace_id = nil + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb new file mode 100644 index 0000000000..f6ea5d3897 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class ExemplarFilter + # Returns a {Boolean} value. + # + # @param [Integer] value Value of the measurement + # @param [Hash] attributes Complete set of Attributes of the measurement + # @param [Context] context Context of the measurement, which covers the Baggage and the current active Span. + # + # @return [Boolean] + def should_sample?(value, attributes, context); end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb new file mode 100644 index 0000000000..a88eb025ba --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class ExemplarReservoir + + def initialize(value, timestamp, attributes, context) + @value = value + @timestamp = timestamp + @attributes = attributes + @context = context + end + + def offer; end + + # return Exemplar + def collect; end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb new file mode 100644 index 0000000000..1c03ba3a13 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class FixedSizeExemplarReservoir < ExemplarReservoir + MAX_BUCKET_SIZE = 20 + + def collect + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb new file mode 100644 index 0000000000..cbcf28528e --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class HistogramExemplarReservoir < ExemplarReservoir + + # return Exemplar + def collect + end + end + end + end + end +end \ No newline at end of file diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb new file mode 100644 index 0000000000..8ce3ea0af6 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class AlwaysOffExemplarFilter < ExemplarFilter + def should_sample?(value, timestamp, attributes, context) + OpenTelemetry.current_span(context).context.trace_flags.sampled? + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index 205ff5db0d..31b760941d 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -22,6 +22,7 @@ def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @stopped = false @metric_readers = [] @resource = resource + @exempler_filter = DEFAULT_EXEMPLAR_FITLER end # Returns a {Meter} instance. @@ -125,6 +126,10 @@ def register_synchronous_instrument(instrument) end end + def set_exemplar_filter(exempler_filter) + @exempler_filter = exempler_filter + end + # The type of the Instrument(s) (optional). # The name of the Instrument(s). OpenTelemetry SDK authors MAY choose to support wildcard characters, with the question mark (?) matching exactly one character and the asterisk character (*) matching zero or more characters. # The name of the Meter (optional). From 2827952a31ff5080f8e78561a6afab7bd7818de7 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 4 Jan 2024 17:09:20 -0500 Subject: [PATCH 2/7] feat: add test case and use exemplar in all synchronous counter --- .../internal/proxy_instrument.rb | 6 +- .../lib/opentelemetry/internal/proxy_meter.rb | 10 +- .../lib/opentelemetry/metrics/meter.rb | 14 +- metrics_sdk/lib/opentelemetry/sdk/metrics.rb | 1 + .../aggregation/explicit_bucket_histogram.rb | 10 +- .../sdk/metrics/aggregation/sum.rb | 13 +- .../lib/opentelemetry/sdk/metrics/exemplar.rb | 26 ++++ .../exemplar/always_off_exemplar_filter.rb | 2 +- .../exemplar/always_on_exemplar_filter.rb | 2 +- .../sdk/metrics/exemplar/exemplar.rb | 8 +- .../sdk/metrics/exemplar/exemplar_filter.rb | 2 +- .../metrics/exemplar/exemplar_reservoir.rb | 40 ++++-- .../exemplar/fixed_size_exemplar_reservoir.rb | 22 +++- .../exemplar/histogram_exemplar_reservoir.rb | 27 +++- .../exemplar/noop_exemplar_reservoir.rb | 23 ++++ .../exemplar/trace_based_exemplar_filter.rb | 6 +- .../sdk/metrics/instrument/counter.rb | 3 +- .../sdk/metrics/instrument/histogram.rb | 7 +- .../instrument/synchronous_instrument.rb | 14 +- .../sdk/metrics/instrument/up_down_counter.rb | 3 +- .../lib/opentelemetry/sdk/metrics/meter.rb | 8 +- .../sdk/metrics/meter_provider.rb | 37 +++++- .../metrics/exemplar/exemplar_filter_test.rb | 41 ++++++ .../exemplar/exemplar_reservoir_test.rb | 122 ++++++++++++++++++ metrics_sdk/test/test_helper.rb | 9 ++ 25 files changed, 406 insertions(+), 50 deletions(-) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb diff --git a/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb b/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb index 117a400778..430903afb7 100644 --- a/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb +++ b/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb @@ -8,19 +8,21 @@ module OpenTelemetry module Internal # @api private class ProxyInstrument - def initialize(kind, name, unit, desc, callable) + def initialize(kind, name, unit, desc, callable, exemplar_filter, exemplar_reservoir) @kind = kind @name = name @unit = unit @desc = desc @callable = callable + @exemplar_filter = exemplar_filter + @exemplar_reservoir = exemplar_reservoir @delegate = nil end def upgrade_with(meter) @delegate = case @kind when :counter, :histogram, :up_down_counter - meter.send("create_#{@kind}", @name, unit: @unit, description: @desc) + meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, exemplar_filter: @exemplar_filter, exemplar_reservoir: @exemplar_reservoir) when :observable_counter, :observable_gauge, :observable_up_down_counter meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, callback: @callback) end diff --git a/metrics_api/lib/opentelemetry/internal/proxy_meter.rb b/metrics_api/lib/opentelemetry/internal/proxy_meter.rb index 74d497c684..5551b3078f 100644 --- a/metrics_api/lib/opentelemetry/internal/proxy_meter.rb +++ b/metrics_api/lib/opentelemetry/internal/proxy_meter.rb @@ -38,14 +38,14 @@ def delegate=(meter) private - def create_instrument(kind, name, unit, description, callback) + def create_instrument(kind, name, unit, description, callback, exemplar_filter, exemplar_reservoir) super do - next ProxyInstrument.new(kind, name, unit, description, callback) if @delegate.nil? + next ProxyInstrument.new(kind, name, unit, description, callback, exemplar_filter, exemplar_reservoir) if @delegate.nil? case kind - when :counter then @delegate.create_counter(name, unit: unit, description: description) - when :histogram then @delegate.create_histogram(name, unit: unit, description: description) - when :up_down_counter then @delegate.create_up_down_counter(name, unit: unit, description: description) + when :counter then @delegate.create_counter(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + when :histogram then @delegate.create_histogram(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + when :up_down_counter then @delegate.create_up_down_counter(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) when :observable_counter then @delegate.create_observable_counter(name, unit: unit, description: description, callback: callback) when :observable_gauge then @delegate.create_observable_gauge(name, unit: unit, description: description, callback: callback) when :observable_up_down_counter then @delegate.create_observable_up_down_counter(name, unit: unit, description: description, callback: callback) diff --git a/metrics_api/lib/opentelemetry/metrics/meter.rb b/metrics_api/lib/opentelemetry/metrics/meter.rb index 846424995b..b7a891dbd4 100644 --- a/metrics_api/lib/opentelemetry/metrics/meter.rb +++ b/metrics_api/lib/opentelemetry/metrics/meter.rb @@ -29,16 +29,16 @@ def initialize @instrument_registry = {} end - def create_counter(name, unit: nil, description: nil) - create_instrument(:counter, name, unit, description, nil) { COUNTER } + def create_counter(name, unit: nil, description: nil, exemplar_filter: nil, exemplar_reservoir: nil) + create_instrument(:counter, name, unit, description, nil, exemplar_filter, exemplar_reservoir) { COUNTER } end - def create_histogram(name, unit: nil, description: nil) - create_instrument(:histogram, name, unit, description, nil) { HISTOGRAM } + def create_histogram(name, unit: nil, description: nil, exemplar_filter: nil, exemplar_reservoir: nil) + create_instrument(:histogram, name, unit, description, nil, exemplar_filter, exemplar_reservoir) { HISTOGRAM } end - def create_up_down_counter(name, unit: nil, description: nil) - create_instrument(:up_down_counter, name, unit, description, nil) { UP_DOWN_COUNTER } + def create_up_down_counter(name, unit: nil, description: nil, exemplar_filter: nil, exemplar_reservoir: nil) + create_instrument(:up_down_counter, name, unit, description, nil, exemplar_filter, exemplar_reservoir) { UP_DOWN_COUNTER } end def create_observable_counter(name, callback:, unit: nil, description: nil) @@ -55,7 +55,7 @@ def create_observable_up_down_counter(name, callback:, unit: nil, description: n private - def create_instrument(kind, name, unit, description, callback) + def create_instrument(kind, name, unit, description, callback, exemplar_filter, exemplar_reservoir) raise InstrumentNameError if name.nil? raise InstrumentNameError if name.empty? raise InstrumentNameError unless NAME_REGEX.match?(name) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb index 42807c708b..12e082180d 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb @@ -13,6 +13,7 @@ module Metrics end end +require 'opentelemetry/sdk/metrics/exemplar' require 'opentelemetry/sdk/metrics/aggregation' require 'opentelemetry/sdk/metrics/configuration_patch' require 'opentelemetry/sdk/metrics/export' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram.rb index 58d87ccaa4..75fd742877 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram.rb @@ -14,6 +14,10 @@ class ExplicitBucketHistogram DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze private_constant :DEFAULT_BOUNDARIES + # if no reservior pass from instrument, then use this empty reservior to avoid no method found error + DEFAULT_RESERVOIR = Metrics::Exemplar::FixedSizeExemplarReservoir.new + private_constant :DEFAULT_RESERVOIR + # The default value for boundaries represents the following buckets: # (-inf, 0], (0, 5.0], (5.0, 10.0], (10.0, 25.0], (25.0, 50.0], # (50.0, 75.0], (75.0, 100.0], (100.0, 250.0], (250.0, 500.0], @@ -21,9 +25,11 @@ class ExplicitBucketHistogram def initialize( aggregation_temporality: :delta, boundaries: DEFAULT_BOUNDARIES, - record_min_max: true + record_min_max: true, + exemplar_reservoir: DEFAULT_RESERVOIR ) @data_points = {} + @exemplar_reservoir = exemplar_reservoir @aggregation_temporality = aggregation_temporality @boundaries = boundaries && !boundaries.empty? ? boundaries.sort : nil @record_min_max = record_min_max @@ -66,7 +72,7 @@ def update(amount, attributes) 0, # :sum empty_bucket_counts, # :bucket_counts @boundaries, # :explicit_bounds - nil, # :exemplars + @exemplar_reservoir.collect(attributes: attributes, aggregation_temporality: @aggregation_temporality), # :exemplars min, # :min max # :max ) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb index 31f8ac6681..173026482e 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb @@ -8,12 +8,19 @@ module OpenTelemetry module SDK module Metrics module Aggregation + + # if no reservior pass from instrument, then use this empty reservior to avoid no method found error + DEFAULT_RESERVOIR = Metrics::Exemplar::FixedSizeExemplarReservoir.new + private_constant :DEFAULT_RESERVOIR + # Contains the implementation of the Sum aggregation # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#sum-aggregation class Sum - def initialize(aggregation_temporality: :delta) + + def initialize(aggregation_temporality: :delta, exemplar_reservoir: DEFAULT_RESERVOIR) @aggregation_temporality = aggregation_temporality @data_points = {} + @exemplar_reservoir = exemplar_reservoir end def collect(start_time, end_time) @@ -37,12 +44,14 @@ def collect(start_time, end_time) end def update(increment, attributes) + # NumberDataPoint should include exemplars ndp = @data_points[attributes] || @data_points[attributes] = NumberDataPoint.new( attributes, nil, nil, 0, - nil + # will this cause the reservoir overloaded with old exemplars? + @exemplar_reservoir.collect(attributes: attributes, aggregation_temporality: @aggregation_temporality) # exemplar ) ndp.value += increment diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar.rb new file mode 100644 index 0000000000..1abc8210c5 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + # The Exemplar module contains the OpenTelemetry metrics reference + # exemplar implementations. + module Exemplar + end + end + end +end + +require 'opentelemetry/sdk/metrics/exemplar/exemplar' +require 'opentelemetry/sdk/metrics/exemplar/exemplar_filter' +require 'opentelemetry/sdk/metrics/exemplar/exemplar_reservoir' +require 'opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter' +require 'opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter' +require 'opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter' +require 'opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir' +require 'opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir' +require 'opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb index bceb6b40b2..1510be4a7f 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb @@ -9,7 +9,7 @@ module SDK module Metrics module Exemplar class AlwaysOffExemplarFilter < ExemplarFilter - def should_sample?(value, timestamp, attributes, context) + def self.should_sample?(value, timestamp, attributes, context) false end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb index 1052163bef..7d986127f0 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb @@ -9,7 +9,7 @@ module SDK module Metrics module Exemplar class AlwaysOnExemplarFilter < ExemplarFilter - def should_sample?(value, timestamp, attributes, context) + def self.should_sample?(value, timestamp, attributes, context) true end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb index 33990d0ff2..c169ba9614 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb @@ -9,12 +9,14 @@ module SDK module Metrics module Exemplar class Exemplar - def initialize(value, time_unix_nano, attributes) + attr_reader :value, :time_unix_nano, :attributes, :span_id, :trace_id + + def initialize(value, time_unix_nano, attributes, span_id, trace_id) @value = value @time_unix_nano = time_unix_nano @attributes = attributes - @span_id = nil - @trace_id = nil + @span_id = span_id + @trace_id = trace_id end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb index f6ea5d3897..71c13bc522 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb @@ -16,7 +16,7 @@ class ExemplarFilter # @param [Context] context Context of the measurement, which covers the Baggage and the current active Span. # # @return [Boolean] - def should_sample?(value, attributes, context); end + def self.should_sample?(value, attributes, context); end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb index a88eb025ba..c13d53abb3 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb @@ -9,18 +9,42 @@ module SDK module Metrics module Exemplar class ExemplarReservoir + def initialize + @exemplars = [] + end - def initialize(value, timestamp, attributes, context) - @value = value - @timestamp = timestamp - @attributes = attributes - @context = context + # Store the info into exemplars bucket + # + # @param [Integer] value Value of the measurement + # @param [Integer] timestamp Time of recording + # @param [Hash] attributes Complete set of Attributes of the measurement + # @param [Context] context SpanContext of the measurement, which covers the Baggage and the current active Span. + # + # @return [Nil] + def offer(value: nil, timestamp: nil, attributes: nil, context: nil) + span_context = current_span_context(context) + @exemplars << Exemplar.new(value, timestamp, attributes, span_context.hex_span_id, span_context.hex_trace_id) + nil end - def offer; end + # return list of Exemplars based on given attributes + # + # @param [Hash] attributes Value of the measurement + # @param [Boolean] aggregation_temporality Should remove the original exemplars or not, default delta + # + # @return [Array] exemplars Array of exemplars + def collect(attributes: nil, aggregation_temporality: :delta) + exemplars = [] + @exemplars.each do |exemplar| + exemplars << exemplar if exemplar # TODO Addition operation on selecting exemplar + end + @exemplars.clear if aggregation_temporality == :delta + exemplars + end - # return Exemplar - def collect; end + def current_span_context(context) + ::OpenTelemetry::Trace.current_span(context).context + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb index 1c03ba3a13..948b12bd73 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb @@ -9,9 +9,27 @@ module SDK module Metrics module Exemplar class FixedSizeExemplarReservoir < ExemplarReservoir - MAX_BUCKET_SIZE = 20 + MAX_BUCKET_SIZE = 1 + + def initialize(max_size: nil) + super() + @max_size = max_size || MAX_BUCKET_SIZE + end + + # MUST use an uniformly-weighted sampling algorithm based on the number of samples the reservoir + def offer(value: nil, timestamp: nil, attributes: nil, context: nil) + span_context = current_span_context(context) + if @exemplars.size >= @max_size + rand_index = rand(0..@max_size-1) + @exemplars[rand_index] = Exemplar.new(value, timestamp, attributes, span_context.hex_span_id, span_context.hex_trace_id) + nil + else + super(value: value, timestamp: timestamp, attributes: attributes, context: context) + end + end - def collect + def collect(attributes: nil, aggregation_temporality: nil) + super(attributes: attributes, aggregation_temporality: aggregation_temporality) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb index cbcf28528e..f70519e69c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb @@ -8,10 +8,35 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # same as AlignedHistogramBucketExemplarReservoir class HistogramExemplarReservoir < ExemplarReservoir + DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze + private_constant :DEFAULT_BOUNDARIES + + def initialize(boundaries: nil) + super() + @boundaries = boundaries || DEFAULT_BOUNDARIES + end + + # TODO: align with the requirements of alignedhistogrambucketexemplarreservoir for offering + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alignedhistogrambucketexemplarreservoir + # Assumption: each boundary should have one exemplar measurement + def offer(value: nil, timestamp: nil, attributes: nil, context: nil) + bucket = find_histogram_bucket(value) + if bucket < @boundaries.size + span_context = current_span_context(context) + @exemplars[bucket] = Exemplar.new(value, timestamp, attributes, span_context.hex_span_id, span_context.hex_trace_id) + end + end + # return Exemplar - def collect + def collect(attributes: nil, aggregation_temporality: nil) + super(attributes: attributes, aggregation_temporality: aggregation_temporality) + end + + def find_histogram_bucket(value) + @boundaries.bsearch_index { |i| i >= value } || @boundaries.size end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb new file mode 100644 index 0000000000..408b6be834 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Exemplar + class NoopExemplarReservoir < ExemplarReservoir + def initialize; end + + def offer(value: nil, timestamp: nil, attributes: nil, context: nil); end + + def collect(attributes: nil, aggregation_temporality: :delta) + Array.new + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb index 8ce3ea0af6..46b5a8dcaf 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb @@ -8,9 +8,9 @@ module OpenTelemetry module SDK module Metrics module Exemplar - class AlwaysOffExemplarFilter < ExemplarFilter - def should_sample?(value, timestamp, attributes, context) - OpenTelemetry.current_span(context).context.trace_flags.sampled? + class TraceBasedExemplarFilter < ExemplarFilter + def self.should_sample?(value, timestamp, attributes, context) + ::OpenTelemetry::Trace.current_span(context).context.trace_flags.sampled? end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index 6af04b1937..ee2251ec62 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -31,6 +31,7 @@ def add(increment, attributes: {}) if increment.negative? OpenTelemetry.logger.warn("#{@name} received a negative value") else + exemplar_offer(increment, attributes) if @meter_provider.exemplar_filter update(increment, attributes) end nil @@ -42,7 +43,7 @@ def add(increment, attributes: {}) private def default_aggregation - OpenTelemetry::SDK::Metrics::Aggregation::Sum.new + OpenTelemetry::SDK::Metrics::Aggregation::Sum.new(exemplar_reservoir: @exemplar_reservoir) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb index 9cdcf60d80..1dad3bfeb4 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -25,6 +25,9 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def record(amount, attributes: nil) + + # this should probably put in SynchronousInstrument class + exemplar_offer(amount, attributes) if @meter_provider.exemplar_filter update(amount, attributes) nil rescue StandardError => e @@ -35,7 +38,9 @@ def record(amount, attributes: nil) private def default_aggregation - OpenTelemetry::SDK::Metrics::Aggregation::ExplicitBucketHistogram.new + # TODO: at this point, histogram always take the default DEFAULT_BOUNDARIES. In future, the histogram should be able to + # define the custom boundaries so for exemplar_reservoir can also takes the bounaries as parameter + OpenTelemetry::SDK::Metrics::Aggregation::ExplicitBucketHistogram.new(exemplar_reservoir: @exemplar_reservoir) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb index f5bf321f0a..d5a0ad6a41 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb @@ -11,13 +11,17 @@ module Instrument # {SynchronousInstrument} contains the common functionality shared across # the synchronous instruments SDK instruments. class SynchronousInstrument - def initialize(name, unit, description, instrumentation_scope, meter_provider) + NOOP_EXEMPLAR_RESERVOIR = Exemplar::NoopExemplarReservoir.new + + def initialize(name, unit, description, instrumentation_scope, meter_provider, exemplar_filter, exemplar_reservoir) @name = name @unit = unit @description = description @instrumentation_scope = instrumentation_scope @meter_provider = meter_provider @metric_streams = [] + @exemplar_filter = exemplar_filter || meter_provider.exemplar_filter + @exemplar_reservoir = exemplar_reservoir || NOOP_EXEMPLAR_RESERVOIR meter_provider.register_synchronous_instrument(self) end @@ -42,6 +46,14 @@ def register_with_new_metric_store(metric_store, aggregation: default_aggregatio def update(value, attributes) @metric_streams.each { |ms| ms.update(value, attributes) } end + + def exemplar_offer(value, attributes) + context = OpenTelemetry::Context.current + time = (Time.now.to_r * 1_000_000).to_i + if @exemplar_filter.should_sample?(value, time, attributes, context) + @exemplar_reservoir.offer(value: value, timestamp: time, attributes: attributes, context: context) # this create an exemplar object in reservoir + end + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb index cf2dc0d8b4..a631124b75 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -25,6 +25,7 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def add(amount, attributes: nil) + exemplar_offer(increment, attributes) if @meter_provider.exemplar_filter update(amount, attributes) nil rescue StandardError => e @@ -35,7 +36,7 @@ def add(amount, attributes: nil) private def default_aggregation - OpenTelemetry::SDK::Metrics::Aggregation::Sum.new + OpenTelemetry::SDK::Metrics::Aggregation::Sum.new(exemplar_reservoir: @exemplar_reservoir) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index faf8e9adb1..94b4777a25 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -33,14 +33,14 @@ def add_metric_reader(metric_reader) end end - def create_instrument(kind, name, unit, description, callback) + def create_instrument(kind, name, unit, description, callback, exemplar_filter, exemplar_reservoir) super do case kind - when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, @instrumentation_scope, @meter_provider) + when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, @instrumentation_scope, @meter_provider, exemplar_filter, exemplar_reservoir) when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, @instrumentation_scope, @meter_provider) - when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @instrumentation_scope, @meter_provider) + when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @instrumentation_scope, @meter_provider, exemplar_filter, exemplar_reservoir) when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, @instrumentation_scope, @meter_provider) - when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, @instrumentation_scope, @meter_provider) + when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, @instrumentation_scope, @meter_provider, exemplar_filter, exemplar_reservoir) when :observable_up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter.new(name, unit, description, callback, @instrumentation_scope, @meter_provider) end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index 31b760941d..a3a295b9d6 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -14,7 +14,7 @@ class MeterProvider < OpenTelemetry::Metrics::MeterProvider Key = Struct.new(:name, :version) private_constant(:Key) - attr_reader :resource, :metric_readers + attr_reader :resource, :metric_readers, :exemplar_filter def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @mutex = Mutex.new @@ -22,7 +22,8 @@ def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @stopped = false @metric_readers = [] @resource = resource - @exempler_filter = DEFAULT_EXEMPLAR_FITLER + + resolve_exemplar_filter end # Returns a {Meter} instance. @@ -126,8 +127,36 @@ def register_synchronous_instrument(instrument) end end - def set_exemplar_filter(exempler_filter) - @exempler_filter = exempler_filter + # Adds a new ExemplarFilter to this {MeterProvider}. Existing exemplar_filter will be replaced + # This is one way to turn on the exemplar + # + # @param exemplar_filter the new ExemplarFilter to be added. + def exemplar_filter_on(exemplar_filter: Exemplar::TraceBasedExemplarFilter) + @exemplar_filter = exemplar_filter + end + + def exemplar_filter_off + @exemplar_filter = nil + end + + # This method is for the spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#exemplar + # and this is one way to turn on the exemplar (exemplar should be turned off by default) + # + def resolve_exemplar_filter + if ENV.has_key?('OTEL_METRICS_EXEMPLAR_FILTER') + case ENV['OTEL_METRICS_EXEMPLAR_FILTER'] + when 'always_on' + @exemplar_filter = Exemplar::AlwaysOnExemplarFilter + when 'trace_based' + @exemplar_filter = Exemplar::TraceBasedExemplarFilter + when 'always_off' + @exemplar_filter = Exemplar::AlwaysOffExemplarFilter + else + @exemplar_filter = nil + end + # @exemplar_on = true if @exemplar_filter + + end end # The type of the Instrument(s) (optional). diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb new file mode 100644 index 0000000000..17ced72a12 --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::Exemplar::ExemplarFilter do + + let(:context) { ::OpenTelemetry::Trace.context_with_span( + ::OpenTelemetry::Trace.non_recording_span( + ::OpenTelemetry::Trace::SpanContext.new( + trace_id: Array("w\xCBl\xCCR-1\x06\x11M\xD6\xEC\xBBp\x03j").pack('H*'), + span_id: Array("1\xE1u\x12\x8E\xFC@\x18").pack('H*'), + trace_flags: ::OpenTelemetry::Trace::TraceFlags::DEFAULT))) + } + let(:timestamp) { 123_456_789 } + let(:attributes) { {'test': 'test'} } + + it 'always true for always on exemplar filter' do + result = OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter.should_sample?(1, timestamp, attributes, context) + _(result).must_equal true + end + + it 'always false for always off exemplar filter' do + result = OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter.should_sample?(1, timestamp, attributes, context) + _(result).must_equal false + end + + it 'filter off when trace context flag is 0' do + result = OpenTelemetry::SDK::Metrics::Exemplar::TraceBasedExemplarFilter.should_sample?(1, timestamp, attributes, context) + _(result).must_equal false + end + + it 'filter on when trace context flag is 1' do + context.instance_variable_get(:@entries).values[0].instance_variable_get(:@context).instance_variable_set(:@trace_flags, ::OpenTelemetry::Trace::TraceFlags::SAMPLED) + result = OpenTelemetry::SDK::Metrics::Exemplar::TraceBasedExemplarFilter.should_sample?(1, timestamp, attributes, context) + _(result).must_equal true + end +end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb new file mode 100644 index 0000000000..e57d66c6a9 --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::Exemplar::ExemplarReservoir do + + describe 'basic exemplar reservoir operation test' do + let(:context) { ::OpenTelemetry::Trace.context_with_span( + ::OpenTelemetry::Trace.non_recording_span( + ::OpenTelemetry::Trace::SpanContext.new( + trace_id: Array("w\xCBl\xCCR-1\x06\x11M\xD6\xEC\xBBp\x03j").pack('H*'), + span_id: Array("1\xE1u\x12\x8E\xFC@\x18").pack('H*'), + trace_flags: ::OpenTelemetry::Trace::TraceFlags::DEFAULT))) + } + let(:timestamp) { 123_456_789 } + let(:attributes) { {'test': 'test'} } + + it 'basic test for exemplar reservoir' do + exemplar = OpenTelemetry::SDK::Metrics::Exemplar::ExemplarReservoir.new + exemplar.offer(value: 1, timestamp: timestamp, attributes: attributes, context: context) + exemplars = exemplar.collect + + _(exemplars.class).must_equal Array + _(exemplars[0].class).must_equal OpenTelemetry::SDK::Metrics::Exemplar::Exemplar + _(exemplars[0].value).must_equal 1 + _(exemplars[0].time_unix_nano).must_equal 123_456_789 + _(exemplars[0].attributes[:test]).must_equal 'test' + _(exemplars[0].span_id).must_equal '11e2ec08' + _(exemplars[0].trace_id).must_equal '0b5cbd16166cb933' + end + + it 'basic test for fixed size exemplar reservoir' do + exemplar = OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) + exemplar.offer(value: 1, timestamp: timestamp, attributes: attributes, context: context) + exemplars = exemplar.collect + + _(exemplars.class).must_equal Array + _(exemplars[0].class).must_equal OpenTelemetry::SDK::Metrics::Exemplar::Exemplar + _(exemplars[0].value).must_equal 1 + _(exemplars[0].time_unix_nano).must_equal 123_456_789 + _(exemplars[0].attributes[:test]).must_equal 'test' + _(exemplars[0].span_id).must_equal '11e2ec08' + _(exemplars[0].trace_id).must_equal '0b5cbd16166cb933' + end + + it 'basic test for fixed size exemplar reservoir when more offers' do + exemplar = OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) + exemplar.offer(value: 1, timestamp: timestamp, attributes: attributes, context: context) + exemplar.offer(value: 2, timestamp: timestamp, attributes: attributes, context: context) + exemplar.offer(value: 3, timestamp: timestamp, attributes: attributes, context: context) + + exemplars = exemplar.collect + _(exemplars.class).must_equal Array + _(exemplars[0].class).must_equal OpenTelemetry::SDK::Metrics::Exemplar::Exemplar + _(exemplars.size).must_equal 2 + end + + it 'basic test for histogram exemplar reservoir' do + exemplar = OpenTelemetry::SDK::Metrics::Exemplar::HistogramExemplarReservoir.new + exemplar.offer(value: 20, timestamp: timestamp, attributes: attributes, context: context) + exemplars = exemplar.collect + + _(exemplars.class).must_equal Array + _(exemplars[0].class).must_equal OpenTelemetry::SDK::Metrics::Exemplar::Exemplar + _(exemplars[0].value).must_equal 20 + _(exemplars[0].time_unix_nano).must_equal 123_456_789 + _(exemplars[0].attributes[:test]).must_equal 'test' + _(exemplars[0].span_id).must_equal '11e2ec08' + _(exemplars[0].trace_id).must_equal '0b5cbd16166cb933' + end + end + + describe 'complex exemplar reservoir integration test always on filter' do + + let(:metric_exporter) { OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new } + let(:exemplar_filter) { OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter } + let(:exemplar_reservoir) { OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) } + + it 'integrate fixed size exemplar reservior with simple counter' do + meter = create_meter + histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'description', + exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + histogram.record(1, attributes: {'foo' => 'bar'}) + + metric_exporter.pull + last_snapshot = metric_exporter.metric_snapshots.last + _(last_snapshot[0].description).must_equal 'description' + _(last_snapshot[0].data_points[0].exemplars[0].value).must_equal 1 + _(last_snapshot[0].data_points[0].exemplars[0].attributes['foo']).must_equal 'bar' + _(last_snapshot[0].data_points[0].exemplars[0].span_id).must_equal '0000000000000000' + _(last_snapshot[0].data_points[0].exemplars[0].trace_id).must_equal '00000000000000000000000000000000' + end + end + + describe 'complex exemplar reservoir integration test always off filter' do + + let(:metric_exporter) { OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new } + let(:exemplar_filter) { OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter } + let(:exemplar_reservoir) { OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) } + + # def create_histogram(name, unit: nil, description: nil, exemplar_filter: nil, exemplar_reservoir: nil) + # create_instrument(:histogram, name, unit, description, nil, exemplar_filter, exemplar_reservoir) { HISTOGRAM } + # end + # create_histogram -> create_instrument so if no exemplar_filter and exemplar_reservoir provided, these two will be nil + # create_instrument -> OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @instrumentation_scope, @meter_provider, exemplar_filter, exemplar_reservoir) + it 'integrate fixed size exemplar reservior with simple counter' do + meter = create_meter + histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'description', + exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + histogram.record(1, attributes: {'foo' => 'bar'}) + + metric_exporter.pull + last_snapshot = metric_exporter.metric_snapshots.last + _(last_snapshot[0].description).must_equal 'description' + _(last_snapshot[0].data_points[0].exemplars.size).must_equal 0 + end + end +end diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb index 99aa4deee0..e297104a6f 100644 --- a/metrics_sdk/test/test_helper.rb +++ b/metrics_sdk/test/test_helper.rb @@ -33,3 +33,12 @@ def with_test_logger ensure OpenTelemetry.logger = original_logger end + +def create_meter + ENV['OTEL_TRACES_EXPORTER'] = 'console' + OpenTelemetry::SDK.configure + OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) + OpenTelemetry.meter_provider.exemplar_filter_on(exemplar_filter: exemplar_filter) + meter = OpenTelemetry.meter_provider.meter("SAMPLE_METER_NAME") + meter +end From ae2c636e6e07ff2140cddb1b75b85a2d79890251 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Fri, 5 Jan 2024 00:10:42 -0500 Subject: [PATCH 3/7] feat: Environment variable OTEL_METRICS_EXEMPLAR_FILTER should have higher precedence --- .../lib/opentelemetry/sdk/metrics/meter_provider.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index a3a295b9d6..f5772d81d9 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -129,9 +129,15 @@ def register_synchronous_instrument(instrument) # Adds a new ExemplarFilter to this {MeterProvider}. Existing exemplar_filter will be replaced # This is one way to turn on the exemplar + # Environment variable has higher precedence; if wants to use custom exemplar_filter, should unset OTEL_METRICS_EXEMPLAR_FILTER + # and then use meter_provider.exemplar_filter_on(exemplar_filter: custom_exemplar_filter) # # @param exemplar_filter the new ExemplarFilter to be added. def exemplar_filter_on(exemplar_filter: Exemplar::TraceBasedExemplarFilter) + if ENV.has_key?('OTEL_METRICS_EXEMPLAR_FILTER') + OpenTelemetry.logger.warn("Exemplar is on based on OTEL_METRICS_EXEMPLAR_FILTER #{ENV['OTEL_METRICS_EXEMPLAR_FILTER']}") + return + end @exemplar_filter = exemplar_filter end @@ -152,10 +158,9 @@ def resolve_exemplar_filter when 'always_off' @exemplar_filter = Exemplar::AlwaysOffExemplarFilter else + OpenTelemetry.logger.warn("OTEL_METRICS_EXEMPLAR_FILTER #{ENV['OTEL_METRICS_EXEMPLAR_FILTER']} is not part of provided exemplar filter; Exemplar is off.") @exemplar_filter = nil end - # @exemplar_on = true if @exemplar_filter - end end From 7800512b739021a58e2e73d814f5d9de1e819cc3 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 27 Feb 2024 12:52:07 -0500 Subject: [PATCH 4/7] feat: exemplar add more test and lint --- .../sdk/metrics/aggregation/sum.rb | 4 +- .../exemplar/always_off_exemplar_filter.rb | 2 + .../exemplar/always_on_exemplar_filter.rb | 1 + .../sdk/metrics/exemplar/exemplar.rb | 1 + .../sdk/metrics/exemplar/exemplar_filter.rb | 1 + .../metrics/exemplar/exemplar_reservoir.rb | 7 +-- .../exemplar/fixed_size_exemplar_reservoir.rb | 5 +- .../exemplar/histogram_exemplar_reservoir.rb | 11 ++-- .../exemplar/noop_exemplar_reservoir.rb | 3 +- .../exemplar/trace_based_exemplar_filter.rb | 1 + .../sdk/metrics/instrument/counter.rb | 2 +- .../sdk/metrics/instrument/histogram.rb | 4 +- .../instrument/synchronous_instrument.rb | 10 ++-- .../sdk/metrics/instrument/up_down_counter.rb | 2 +- .../sdk/metrics/meter_provider.rb | 53 ++++++++---------- .../metrics/exemplar/exemplar_filter_test.rb | 21 ++++--- .../exemplar/exemplar_integration_test.rb | 55 +++++++++++++++++++ .../exemplar/exemplar_reservoir_test.rb | 40 ++++++-------- .../sdk/metrics/meter_provider_test.rb | 55 +++++++++++++++++++ metrics_sdk/test/test_helper.rb | 3 +- 20 files changed, 195 insertions(+), 86 deletions(-) create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb index 08a7cefae8..40b970de3a 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb @@ -17,7 +17,7 @@ class Sum attr_reader :aggregation_temporality def initialize(aggregation_temporality: ENV.fetch('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', :delta), - exemplar_reservoir: DEFAULT_RESERVOIR) + exemplar_reservoir: DEFAULT_RESERVOIR) # TODO: the default should be :cumulative, see issue #1555 @aggregation_temporality = aggregation_temporality @data_points = {} @@ -51,7 +51,7 @@ def update(increment, attributes) nil, nil, 0, - # will this cause the reservoir overloaded with old exemplars? + # will this cause the reservoir overloaded with old exemplars? @exemplar_reservoir.collect(attributes: attributes, aggregation_temporality: @aggregation_temporality) # exemplar ) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb index 1510be4a7f..8b5b8dcad1 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_off_exemplar_filter.rb @@ -8,6 +8,8 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # AlwaysOffExemplarFilter makes no measurements eligible for being an Exemplar. + # Using this ExemplarFilter is as good as disabling Exemplar feature. class AlwaysOffExemplarFilter < ExemplarFilter def self.should_sample?(value, timestamp, attributes, context) false diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb index 7d986127f0..1b44a0fd44 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/always_on_exemplar_filter.rb @@ -8,6 +8,7 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # AlwaysOnExemplarFilter class AlwaysOnExemplarFilter < ExemplarFilter def self.should_sample?(value, timestamp, attributes, context) true diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb index c169ba9614..8434654fa7 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar.rb @@ -8,6 +8,7 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # Exemplar class Exemplar attr_reader :value, :time_unix_nano, :attributes, :span_id, :trace_id diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb index 71c13bc522..800eb8a136 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_filter.rb @@ -8,6 +8,7 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # ExemplarFilter class ExemplarFilter # Returns a {Boolean} value. # diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb index c13d53abb3..a6fe2d6e62 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir.rb @@ -8,6 +8,7 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # ExemplarReservoir class ExemplarReservoir def initialize @exemplars = [] @@ -31,13 +32,11 @@ def offer(value: nil, timestamp: nil, attributes: nil, context: nil) # # @param [Hash] attributes Value of the measurement # @param [Boolean] aggregation_temporality Should remove the original exemplars or not, default delta - # + # # @return [Array] exemplars Array of exemplars def collect(attributes: nil, aggregation_temporality: :delta) exemplars = [] - @exemplars.each do |exemplar| - exemplars << exemplar if exemplar # TODO Addition operation on selecting exemplar - end + @exemplars.each { |exemplar| exemplars << exemplar if exemplar } # TODO: Addition operation on selecting exemplar @exemplars.clear if aggregation_temporality == :delta exemplars end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb index 948b12bd73..37fe86c392 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/fixed_size_exemplar_reservoir.rb @@ -8,9 +8,10 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # FixedSizeExemplarReservoir class FixedSizeExemplarReservoir < ExemplarReservoir MAX_BUCKET_SIZE = 1 - + def initialize(max_size: nil) super() @max_size = max_size || MAX_BUCKET_SIZE @@ -20,7 +21,7 @@ def initialize(max_size: nil) def offer(value: nil, timestamp: nil, attributes: nil, context: nil) span_context = current_span_context(context) if @exemplars.size >= @max_size - rand_index = rand(0..@max_size-1) + rand_index = rand(0..@max_size - 1) @exemplars[rand_index] = Exemplar.new(value, timestamp, attributes, span_context.hex_span_id, span_context.hex_trace_id) nil else diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb index f70519e69c..c1b3328812 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/histogram_exemplar_reservoir.rb @@ -10,7 +10,6 @@ module Metrics module Exemplar # same as AlignedHistogramBucketExemplarReservoir class HistogramExemplarReservoir < ExemplarReservoir - DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze private_constant :DEFAULT_BOUNDARIES @@ -24,10 +23,10 @@ def initialize(boundaries: nil) # Assumption: each boundary should have one exemplar measurement def offer(value: nil, timestamp: nil, attributes: nil, context: nil) bucket = find_histogram_bucket(value) - if bucket < @boundaries.size - span_context = current_span_context(context) - @exemplars[bucket] = Exemplar.new(value, timestamp, attributes, span_context.hex_span_id, span_context.hex_trace_id) - end + return unless bucket < @boundaries.size + + span_context = current_span_context(context) + @exemplars[bucket] = Exemplar.new(value, timestamp, attributes, span_context.hex_span_id, span_context.hex_trace_id) end # return Exemplar @@ -42,4 +41,4 @@ def find_histogram_bucket(value) end end end -end \ No newline at end of file +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb index 408b6be834..2d33d5c67a 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/noop_exemplar_reservoir.rb @@ -8,13 +8,14 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # NoopExemplarReservoir class NoopExemplarReservoir < ExemplarReservoir def initialize; end def offer(value: nil, timestamp: nil, attributes: nil, context: nil); end def collect(attributes: nil, aggregation_temporality: :delta) - Array.new + [] end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb index 46b5a8dcaf..a3e71199dd 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/exemplar/trace_based_exemplar_filter.rb @@ -8,6 +8,7 @@ module OpenTelemetry module SDK module Metrics module Exemplar + # TraceBasedExemplarFilter class TraceBasedExemplarFilter < ExemplarFilter def self.should_sample?(value, timestamp, attributes, context) ::OpenTelemetry::Trace.current_span(context).context.trace_flags.sampled? diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index ee2251ec62..b0c9144ea8 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -31,7 +31,7 @@ def add(increment, attributes: {}) if increment.negative? OpenTelemetry.logger.warn("#{@name} received a negative value") else - exemplar_offer(increment, attributes) if @meter_provider.exemplar_filter + exemplar_offer(increment, attributes) update(increment, attributes) end nil diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb index 1dad3bfeb4..6e933ca10d 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -25,9 +25,7 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def record(amount, attributes: nil) - - # this should probably put in SynchronousInstrument class - exemplar_offer(amount, attributes) if @meter_provider.exemplar_filter + exemplar_offer(amount, attributes) update(amount, attributes) nil rescue StandardError => e diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb index d5a0ad6a41..89796f48e0 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb @@ -20,7 +20,7 @@ def initialize(name, unit, description, instrumentation_scope, meter_provider, e @instrumentation_scope = instrumentation_scope @meter_provider = meter_provider @metric_streams = [] - @exemplar_filter = exemplar_filter || meter_provider.exemplar_filter + @exemplar_filter = exemplar_filter || meter_provider.exemplar_filter @exemplar_reservoir = exemplar_reservoir || NOOP_EXEMPLAR_RESERVOIR meter_provider.register_synchronous_instrument(self) @@ -47,12 +47,14 @@ def update(value, attributes) @metric_streams.each { |ms| ms.update(value, attributes) } end + # Adding the exemplar to reservoir + # def exemplar_offer(value, attributes) context = OpenTelemetry::Context.current time = (Time.now.to_r * 1_000_000).to_i - if @exemplar_filter.should_sample?(value, time, attributes, context) - @exemplar_reservoir.offer(value: value, timestamp: time, attributes: attributes, context: context) # this create an exemplar object in reservoir - end + return unless @exemplar_filter.should_sample?(value, time, attributes, context) + + @exemplar_reservoir.offer(value: value, timestamp: time, attributes: attributes, context: context) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb index a631124b75..650c0c3a62 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -25,7 +25,7 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def add(amount, attributes: nil) - exemplar_offer(increment, attributes) if @meter_provider.exemplar_filter + exemplar_offer(amount, attributes) update(amount, attributes) nil rescue StandardError => e diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index f5772d81d9..dd9b0e4f9d 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -22,8 +22,9 @@ def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @stopped = false @metric_readers = [] @resource = resource + @exemplar_filter = Exemplar::AlwaysOffExemplarFilter - resolve_exemplar_filter + exemplar_filter_setup end # Returns a {Meter} instance. @@ -127,41 +128,35 @@ def register_synchronous_instrument(instrument) end end - # Adds a new ExemplarFilter to this {MeterProvider}. Existing exemplar_filter will be replaced - # This is one way to turn on the exemplar - # Environment variable has higher precedence; if wants to use custom exemplar_filter, should unset OTEL_METRICS_EXEMPLAR_FILTER - # and then use meter_provider.exemplar_filter_on(exemplar_filter: custom_exemplar_filter) + # spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#exemplar + # this is one way to turn on the exemplar (exemplar should be turned off by default) + # + def exemplar_filter_setup + return unless ENV.key?('OTEL_METRICS_EXEMPLAR_FILTER') + + case ENV['OTEL_METRICS_EXEMPLAR_FILTER'] + when 'always_on' + @exemplar_filter = Exemplar::AlwaysOnExemplarFilter + when 'trace_based' + @exemplar_filter = Exemplar::TraceBasedExemplarFilter + when 'always_off' + @exemplar_filter = Exemplar::AlwaysOffExemplarFilter + else + OpenTelemetry.logger.warn("OTEL_METRICS_EXEMPLAR_FILTER #{ENV['OTEL_METRICS_EXEMPLAR_FILTER']} is not part of provided exemplar filter; Exemplar is off.") + end + end + + # Adds a new exemplar_filter to replace exist exemplar_filter + # Default to TraceBasedExemplarFilter # # @param exemplar_filter the new ExemplarFilter to be added. def exemplar_filter_on(exemplar_filter: Exemplar::TraceBasedExemplarFilter) - if ENV.has_key?('OTEL_METRICS_EXEMPLAR_FILTER') - OpenTelemetry.logger.warn("Exemplar is on based on OTEL_METRICS_EXEMPLAR_FILTER #{ENV['OTEL_METRICS_EXEMPLAR_FILTER']}") - return - end @exemplar_filter = exemplar_filter end + # turn off exemplar_filter by setting the exemplar_fitler to AlwaysOffExemplarFilter def exemplar_filter_off - @exemplar_filter = nil - end - - # This method is for the spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#exemplar - # and this is one way to turn on the exemplar (exemplar should be turned off by default) - # - def resolve_exemplar_filter - if ENV.has_key?('OTEL_METRICS_EXEMPLAR_FILTER') - case ENV['OTEL_METRICS_EXEMPLAR_FILTER'] - when 'always_on' - @exemplar_filter = Exemplar::AlwaysOnExemplarFilter - when 'trace_based' - @exemplar_filter = Exemplar::TraceBasedExemplarFilter - when 'always_off' - @exemplar_filter = Exemplar::AlwaysOffExemplarFilter - else - OpenTelemetry.logger.warn("OTEL_METRICS_EXEMPLAR_FILTER #{ENV['OTEL_METRICS_EXEMPLAR_FILTER']} is not part of provided exemplar filter; Exemplar is off.") - @exemplar_filter = nil - end - end + @exemplar_filter = Exemplar::AlwaysOffExemplarFilter end # The type of the Instrument(s) (optional). diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb index 17ced72a12..9aa8a35adc 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_filter_test.rb @@ -7,16 +7,19 @@ require 'test_helper' describe OpenTelemetry::SDK::Metrics::Exemplar::ExemplarFilter do - - let(:context) { ::OpenTelemetry::Trace.context_with_span( - ::OpenTelemetry::Trace.non_recording_span( - ::OpenTelemetry::Trace::SpanContext.new( - trace_id: Array("w\xCBl\xCCR-1\x06\x11M\xD6\xEC\xBBp\x03j").pack('H*'), - span_id: Array("1\xE1u\x12\x8E\xFC@\x18").pack('H*'), - trace_flags: ::OpenTelemetry::Trace::TraceFlags::DEFAULT))) - } + let(:context) do + ::OpenTelemetry::Trace.context_with_span( + ::OpenTelemetry::Trace.non_recording_span( + ::OpenTelemetry::Trace::SpanContext.new( + trace_id: Array("w\xCBl\xCCR-1\x06\x11M\xD6\xEC\xBBp\x03j").pack('H*'), + span_id: Array("1\xE1u\x12\x8E\xFC@\x18").pack('H*'), + trace_flags: ::OpenTelemetry::Trace::TraceFlags::DEFAULT + ) + ) + ) + end let(:timestamp) { 123_456_789 } - let(:attributes) { {'test': 'test'} } + let(:attributes) { { 'test': 'test' } } it 'always true for always on exemplar filter' do result = OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter.should_sample?(1, timestamp, attributes, context) diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb new file mode 100644 index 0000000000..34a50e4e5b --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK do + describe '#exempler_integration_test' do + before { reset_metrics_sdk } + + it 'emits metrics with list of exempler' do + OpenTelemetry::SDK.configure + + metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new + OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) + + OpenTelemetry.meter_provider.exemplar_filter_on(exemplar_filter: OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter) + + exemplar_reservoir = OpenTelemetry::SDK::Metrics::Exemplar::ExemplarReservoir.new + + meter = OpenTelemetry.meter_provider.meter('test') + counter = meter.create_counter('counter', unit: 'smidgen', description: 'a small amount of something', exemplar_reservoir: exemplar_reservoir) + + counter.add(1) + counter.add(2, attributes: { 'a' => 'b' }) + counter.add(2, attributes: { 'a' => 'b' }) + counter.add(3, attributes: { 'b' => 'c' }) + counter.add(4, attributes: { 'd' => 'e' }) + + metric_exporter.pull + last_snapshot = metric_exporter.metric_snapshots.last + + _(last_snapshot).wont_be_empty + _(last_snapshot[0].name).must_equal('counter') + _(last_snapshot[0].unit).must_equal('smidgen') + _(last_snapshot[0].description).must_equal('a small amount of something') + + _(last_snapshot[0].instrumentation_scope.name).must_equal('test') + + _(last_snapshot[0].data_points[0].value).must_equal(1) + _(last_snapshot[0].data_points[0].attributes).must_equal({}) + + _(last_snapshot[0].data_points[1].value).must_equal(4) + _(last_snapshot[0].data_points[1].attributes).must_equal('a' => 'b') + + _(last_snapshot[0].data_points[0].exemplars.size).must_equal 1 + _(last_snapshot[0].data_points[0].exemplars[0].class).must_equal OpenTelemetry::SDK::Metrics::Exemplar::Exemplar + _(last_snapshot[0].data_points[0].exemplars[0].value).must_equal 1 + _(last_snapshot[0].data_points[0].exemplars[0].span_id).must_equal '0000000000000000' + _(last_snapshot[0].data_points[0].exemplars[0].trace_id).must_equal '00000000000000000000000000000000' + end + end +end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb index e57d66c6a9..f3a50751a3 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb @@ -7,17 +7,20 @@ require 'test_helper' describe OpenTelemetry::SDK::Metrics::Exemplar::ExemplarReservoir do - describe 'basic exemplar reservoir operation test' do - let(:context) { ::OpenTelemetry::Trace.context_with_span( - ::OpenTelemetry::Trace.non_recording_span( - ::OpenTelemetry::Trace::SpanContext.new( - trace_id: Array("w\xCBl\xCCR-1\x06\x11M\xD6\xEC\xBBp\x03j").pack('H*'), - span_id: Array("1\xE1u\x12\x8E\xFC@\x18").pack('H*'), - trace_flags: ::OpenTelemetry::Trace::TraceFlags::DEFAULT))) - } + let(:context) do + ::OpenTelemetry::Trace.context_with_span( + ::OpenTelemetry::Trace.non_recording_span( + ::OpenTelemetry::Trace::SpanContext.new( + trace_id: Array("w\xCBl\xCCR-1\x06\x11M\xD6\xEC\xBBp\x03j").pack('H*'), + span_id: Array("1\xE1u\x12\x8E\xFC@\x18").pack('H*'), + trace_flags: ::OpenTelemetry::Trace::TraceFlags::DEFAULT + ) + ) + ) + end let(:timestamp) { 123_456_789 } - let(:attributes) { {'test': 'test'} } + let(:attributes) { { 'test': 'test' } } it 'basic test for exemplar reservoir' do exemplar = OpenTelemetry::SDK::Metrics::Exemplar::ExemplarReservoir.new @@ -75,16 +78,15 @@ end describe 'complex exemplar reservoir integration test always on filter' do - let(:metric_exporter) { OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new } let(:exemplar_filter) { OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter } let(:exemplar_reservoir) { OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) } it 'integrate fixed size exemplar reservior with simple counter' do meter = create_meter - histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'description', - exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) - histogram.record(1, attributes: {'foo' => 'bar'}) + histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'description', + exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + histogram.record(1, attributes: { 'foo' => 'bar' }) metric_exporter.pull last_snapshot = metric_exporter.metric_snapshots.last @@ -97,21 +99,15 @@ end describe 'complex exemplar reservoir integration test always off filter' do - let(:metric_exporter) { OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new } let(:exemplar_filter) { OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter } let(:exemplar_reservoir) { OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) } - # def create_histogram(name, unit: nil, description: nil, exemplar_filter: nil, exemplar_reservoir: nil) - # create_instrument(:histogram, name, unit, description, nil, exemplar_filter, exemplar_reservoir) { HISTOGRAM } - # end - # create_histogram -> create_instrument so if no exemplar_filter and exemplar_reservoir provided, these two will be nil - # create_instrument -> OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @instrumentation_scope, @meter_provider, exemplar_filter, exemplar_reservoir) it 'integrate fixed size exemplar reservior with simple counter' do meter = create_meter - histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'description', - exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) - histogram.record(1, attributes: {'foo' => 'bar'}) + histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'description', + exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + histogram.record(1, attributes: { 'foo' => 'bar' }) metric_exporter.pull last_snapshot = metric_exporter.metric_snapshots.last diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb index f781d613b3..b2f7be0458 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -134,6 +134,61 @@ end end + describe 'exempler' do + describe '#exemplar_filter_setup' do + after do + ENV.delete('OTEL_METRICS_EXEMPLAR_FILTER') + end + + it 'without OTEL_METRICS_EXEMPLAR_FILTER' do + OpenTelemetry.meter_provider.exemplar_filter_setup + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter + end + + it 'with OTEL_METRICS_EXEMPLAR_FILTER as always_on' do + ENV['OTEL_METRICS_EXEMPLAR_FILTER'] = 'always_on' + OpenTelemetry.meter_provider.exemplar_filter_setup + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter + end + + it 'with OTEL_METRICS_EXEMPLAR_FILTER as invalid option' do + ENV['OTEL_METRICS_EXEMPLAR_FILTER'] = 'better_on' + OpenTelemetry.meter_provider.exemplar_filter_setup + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter + end + end + + describe '#exemplar_filter_off' do + it 'will turn it off' do + ENV['OTEL_METRICS_EXEMPLAR_FILTER'] = 'always_on' + OpenTelemetry.meter_provider.exemplar_filter_setup + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter + + OpenTelemetry.meter_provider.exemplar_filter_off + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter + ENV.delete('OTEL_METRICS_EXEMPLAR_FILTER') + end + end + + describe '#exemplar_filter_on' do + it 'will turn it on with default exempler filter' do + OpenTelemetry.meter_provider.exemplar_filter_setup + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter + + OpenTelemetry.meter_provider.exemplar_filter_on + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::TraceBasedExemplarFilter + end + + it 'will turn it on with customized always on' do + OpenTelemetry.meter_provider.exemplar_filter_setup + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOffExemplarFilter + + OpenTelemetry.meter_provider.exemplar_filter_on(exemplar_filter: OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter) + _(OpenTelemetry.meter_provider.exemplar_filter).must_equal OpenTelemetry::SDK::Metrics::Exemplar::AlwaysOnExemplarFilter + end + end + end + # TODO: OpenTelemetry.meter_provider.add_view describe '#add_view' do end diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb index e297104a6f..b6f6174a7c 100644 --- a/metrics_sdk/test/test_helper.rb +++ b/metrics_sdk/test/test_helper.rb @@ -39,6 +39,5 @@ def create_meter OpenTelemetry::SDK.configure OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) OpenTelemetry.meter_provider.exemplar_filter_on(exemplar_filter: exemplar_filter) - meter = OpenTelemetry.meter_provider.meter("SAMPLE_METER_NAME") - meter + OpenTelemetry.meter_provider.meter('SAMPLE_METER_NAME') end From ae5abdc2f76b453769aac3fdcc2d1af7b6878706 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 15 Jan 2025 12:08:38 -0500 Subject: [PATCH 5/7] lint --- .../sdk/metrics/exemplar/exemplar_reservoir_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb index ab68692fd4..5a9c1c3226 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb @@ -85,7 +85,7 @@ it 'integrate fixed size exemplar reservior with simple counter' do meter = create_meter histogram = meter.create_histogram('histogram_always_on_exemplar', unit: 'smidgen', description: 'description', - exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) histogram.record(1, attributes: { 'foo' => 'bar' }) metric_exporter.pull @@ -106,7 +106,7 @@ it 'integrate fixed size exemplar reservior with simple counter' do meter = create_meter histogram = meter.create_histogram('histogram_always_off_exemplar', unit: 'smidgen', description: 'description', - exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) + exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) histogram.record(1, attributes: { 'foo' => 'bar' }) metric_exporter.pull From f890dbfd47c9a0e568cf5ed680b813ecfe3ff132 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 15 Jan 2025 12:34:33 -0500 Subject: [PATCH 6/7] fix test case --- metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb | 2 ++ .../test/opentelemetry/sdk/metrics/configuration_patch_test.rb | 2 +- .../sdk/metrics/exemplar/exemplar_integration_test.rb | 2 ++ .../sdk/metrics/exemplar/exemplar_reservoir_test.rb | 2 ++ metrics_sdk/test/test_helper.rb | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index 4efa580cc4..cef0a6e376 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -10,6 +10,7 @@ module SDK # implementation. module Metrics # {MeterProvider} is the SDK implementation of {OpenTelemetry::Metrics::MeterProvider}. + # rubocop:disable Metrics/ClassLength class MeterProvider < OpenTelemetry::Metrics::MeterProvider Key = Struct.new(:name, :version) private_constant(:Key) @@ -186,6 +187,7 @@ def add_view(name, **options) nil end end + # rubocop:enable Metrics/ClassLength end end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/configuration_patch_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/configuration_patch_test.rb index 88cd4bdaa0..dfd33b4edc 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/configuration_patch_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/configuration_patch_test.rb @@ -37,7 +37,7 @@ describe 'metric readers' do it 'defaults to a periodic reader with an otlp exporter' do skip 'OTLP exporter not compatible with JRuby' if RUBY_ENGINE == 'jruby' - + ENV['OTEL_METRICS_EXPORTER'] = nil configurator.configure assert_equal 1, OpenTelemetry.meter_provider.metric_readers.size diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb index 91ee91c887..c3f909a5c0 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_integration_test.rb @@ -11,6 +11,8 @@ before { reset_metrics_sdk } it 'emits metrics with list of exempler' do + ENV['OTEL_METRICS_EXPORTER'] = 'none' + OpenTelemetry::SDK.configure metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb index 5a9c1c3226..345b44aa96 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/exemplar/exemplar_reservoir_test.rb @@ -83,6 +83,7 @@ let(:exemplar_reservoir) { OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) } it 'integrate fixed size exemplar reservior with simple counter' do + reset_metrics_sdk meter = create_meter histogram = meter.create_histogram('histogram_always_on_exemplar', unit: 'smidgen', description: 'description', exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) @@ -104,6 +105,7 @@ let(:exemplar_reservoir) { OpenTelemetry::SDK::Metrics::Exemplar::FixedSizeExemplarReservoir.new(max_size: 2) } it 'integrate fixed size exemplar reservior with simple counter' do + reset_metrics_sdk meter = create_meter histogram = meter.create_histogram('histogram_always_off_exemplar', unit: 'smidgen', description: 'description', exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb index 53f2d0f44b..4d4073c7b2 100644 --- a/metrics_sdk/test/test_helper.rb +++ b/metrics_sdk/test/test_helper.rb @@ -36,6 +36,7 @@ def with_test_logger def create_meter ENV['OTEL_TRACES_EXPORTER'] = 'console' + ENV['OTEL_METRICS_EXPORTER'] = 'none' OpenTelemetry::SDK.configure OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) OpenTelemetry.meter_provider.exemplar_filter_on(exemplar_filter: exemplar_filter) From 4ba58fbeadda31caa2345f443f6bd137c8dac79b Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 15 Jan 2025 13:58:50 -0500 Subject: [PATCH 7/7] lint on metrics api --- .../lib/opentelemetry/internal/proxy_instrument.rb | 10 +++++----- metrics_api/lib/opentelemetry/internal/proxy_meter.rb | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb b/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb index 3d3a8f5a30..ca7a15c6f0 100644 --- a/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb +++ b/metrics_api/lib/opentelemetry/internal/proxy_instrument.rb @@ -21,11 +21,11 @@ def initialize(kind, name, unit, desc, callable, exemplar_filter, exemplar_reser def upgrade_with(meter) @delegate = case @kind - when :counter, :histogram, :up_down_counter - meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, exemplar_filter: @exemplar_filter, exemplar_reservoir: @exemplar_reservoir) - when :observable_counter, :observable_gauge, :observable_up_down_counter - meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, exemplar_filter: @exemplar_filter, exemplar_reservoir: @exemplar_reservoir, callback: @callback) - end + when :counter, :histogram, :up_down_counter + meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, exemplar_filter: @exemplar_filter, exemplar_reservoir: @exemplar_reservoir) + when :observable_counter, :observable_gauge, :observable_up_down_counter + meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, exemplar_filter: @exemplar_filter, exemplar_reservoir: @exemplar_reservoir, callback: @callback) + end end def add(amount, attributes: nil) diff --git a/metrics_api/lib/opentelemetry/internal/proxy_meter.rb b/metrics_api/lib/opentelemetry/internal/proxy_meter.rb index 982379a852..98572a8ae2 100644 --- a/metrics_api/lib/opentelemetry/internal/proxy_meter.rb +++ b/metrics_api/lib/opentelemetry/internal/proxy_meter.rb @@ -48,7 +48,7 @@ def create_instrument(kind, name, unit, description, callback, exemplar_filter, when :up_down_counter then @delegate.create_up_down_counter(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) when :gauge then @delegate.create_gauge(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir) when :observable_counter then @delegate.create_observable_counter(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir, callback: callback) - when :observable_gauge then @delegate.create_observable_gauge(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir, callback: callback) + when :observable_gauge then @delegate.create_observable_gauge(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir, callback: callback) when :observable_up_down_counter then @delegate.create_observable_up_down_counter(name, unit: unit, description: description, exemplar_filter: exemplar_filter, exemplar_reservoir: exemplar_reservoir, callback: callback) end end