opentelemetry-metrics-sdk 0.7.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f7970de692f93abe5d321c766ddfe2d29d85ba06a64a8e4daeac9e50a55bf42
4
- data.tar.gz: 5e147f09a61be36cde37d184a0e2692b949de74c5e0cdd9dae772ed9f40b727a
3
+ metadata.gz: d6d7be59eeec2cd9ac11018c7d83dae815350d80c428dcb1ce3078b2435c6fc6
4
+ data.tar.gz: 3f23f0765a8b193ab67beea38c5c52804ab966b5e3d7c0436b4a0960ee9ebbb9
5
5
  SHA512:
6
- metadata.gz: 80cb6c3b9c149bb5d2dfe4d4c3866af45121324490f9b534d63702340ed3ba4eb3bef209e6f559c73a338c11c7e4082344cf49281c3461b06dc55abc168951ee
7
- data.tar.gz: 887d608e30a72332f13a8181c3fbff0444b403d8badefdea8b51797b393d1e5952a37ffcef1d4038bbd17fe6b4d433b9c1e62ae3de6626c17bb13351c3342dc1
6
+ metadata.gz: 0dac2992b3eca5e5f42a1f503b600155995657e089be8d3bf49918a36b452a049dc8e7fe068e5f18ae9b9b989162d1128fbc28ca5ed96f4b7f183f93026543b4
7
+ data.tar.gz: 5707defc93ad7cbe2c0f56fabb9d992fe2620b0a2766df43da153ec5cf3b5f46b10089fc7805d02a341314fef134201489f93d52ed2bc223d8beb868c589a6a1
data/CHANGELOG.md CHANGED
@@ -1,24 +1,37 @@
1
1
  # Release History: opentelemetry-metrics-sdk
2
2
 
3
+ ### v0.9.0 / 2025-08-19
4
+
5
+ * ADDED: Add `LOWMEMORY` option to `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE`
6
+
7
+ ### v0.8.0 / 2025-08-14
8
+
9
+ - BREAKING CHANGE: Update default aggregation temporality for counter, histogram, and up down counter to cumulative
10
+
11
+ - ADDED: Support asynchronous instruments: ObservableGauge, ObservableCounter and ObservableUpDownCounter
12
+ - FIXED: Validate scale range on exponential histograms and raise exception if out of bounds
13
+ - FIXED: Update max instrument name length from 63 to 255 characters and allow `/` in instrument names
14
+ - FIXED: Validate scale range and raise exception if out of bounds for exponential histograms
15
+
3
16
  ### v0.7.3 / 2025-07-09
4
17
 
5
- * FIXED: Stop exporting metrics with empty data points
18
+ - FIXED: Stop exporting metrics with empty data points
6
19
 
7
20
  ### v0.7.2 / 2025-07-03
8
21
 
9
- * FIXED: Coerce aggregation temporality to be a symbol for exponential histograms
22
+ - FIXED: Coerce aggregation temporality to be a symbol for exponential histograms
10
23
 
11
24
  ### v0.7.1 / 2025-05-28
12
25
 
13
- * FIXED: Recover periodic metric readers after forking
26
+ - FIXED: Recover periodic metric readers after forking
14
27
 
15
28
  ### v0.7.0 / 2025-05-13
16
29
 
17
- * ADDED: Add basic exponential histogram
30
+ - ADDED: Add basic exponential histogram
18
31
 
19
32
  ### v0.6.1 / 2025-04-09
20
33
 
21
- * FIXED: Use condition signal to replace sleep and remove timeout.timeout…
34
+ - FIXED: Use condition signal to replace sleep and remove timeout.timeout…
22
35
 
23
36
  ### v0.6.0 / 2025-02-25
24
37
 
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Metrics
10
+ module Aggregation
11
+ # AggregationTemporality represents the temporality of
12
+ # data point ({NumberDataPoint} and {HistogramDataPoint}) in {Metrics}.
13
+ # It determine whether the data point will be cleared for each metrics pull/export.
14
+ class AggregationTemporality
15
+ class << self
16
+ private :new
17
+
18
+ # Returns a newly created {AggregationTemporality} with temporality == DELTA
19
+ #
20
+ # @return [AggregationTemporality]
21
+ def delta
22
+ new(DELTA)
23
+ end
24
+
25
+ # Returns a newly created {AggregationTemporality} with temporality == CUMULATIVE
26
+ #
27
+ # @return [AggregationTemporality]
28
+ def cumulative
29
+ new(CUMULATIVE)
30
+ end
31
+
32
+ # | Preference Value | Counter | Async Counter | Histogram | UpDownCounter | Async UpDownCounter |
33
+ # |------------------|------------|------------------|----------- |---------------|-------------------- |
34
+ # | **Cumulative** | Cumulative | Cumulative | Cumulative | Cumulative | Cumulative |
35
+ # | **Delta** | Delta | Delta | Delta | Cumulative | Cumulative |
36
+ # | **LowMemory** | Delta | Cumulative | Delta | Cumulative | Cumulative |
37
+ def determine_temporality(aggregation_temporality: nil, instrument_kind: nil, default: nil)
38
+ # aggregation_temporality can't be nil because it always has default value in symbol
39
+ if aggregation_temporality.is_a?(::Symbol)
40
+ aggregation_temporality == :delta ? delta : cumulative
41
+
42
+ elsif aggregation_temporality.is_a?(::String)
43
+ case aggregation_temporality
44
+ when 'LOWMEMORY', 'lowmemory', 'low_memory'
45
+ instrument_kind == :observable_counter ? cumulative : delta
46
+ when 'DELTA', 'delta'
47
+ delta
48
+ when 'CUMULATIVE', 'cumulative'
49
+ cumulative
50
+ else
51
+ default == :delta ? delta : cumulative
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+
58
+ attr_reader :temporality
59
+
60
+ # @api private
61
+ # The constructor is private and only for use internally by the class.
62
+ # Users should use the {delta} and {cumulative} factory methods to obtain
63
+ # a {AggregationTemporality} instance.
64
+ #
65
+ # @param [Integer] temporality One of the status codes below
66
+ def initialize(temporality)
67
+ @temporality = temporality
68
+ end
69
+
70
+ def delta?
71
+ @temporality == :delta
72
+ end
73
+
74
+ def cumulative?
75
+ @temporality == :cumulative
76
+ end
77
+
78
+ # delta: data point will be cleared after each metrics pull/export.
79
+ DELTA = :delta
80
+
81
+ # cumulative: data point will NOT be cleared after metrics pull/export.
82
+ CUMULATIVE = :cumulative
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -10,12 +10,6 @@ module OpenTelemetry
10
10
  module Aggregation
11
11
  # Contains the implementation of the Drop aggregation
12
12
  class Drop
13
- attr_reader :aggregation_temporality
14
-
15
- def initialize(aggregation_temporality: :delta)
16
- @aggregation_temporality = aggregation_temporality
17
- end
18
-
19
13
  def collect(start_time, end_time, data_points)
20
14
  data_points.values.map!(&:dup)
21
15
  end
@@ -14,24 +14,22 @@ module OpenTelemetry
14
14
  DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze
15
15
  private_constant :DEFAULT_BOUNDARIES
16
16
 
17
- attr_reader :aggregation_temporality
18
-
19
17
  # The default value for boundaries represents the following buckets:
20
18
  # (-inf, 0], (0, 5.0], (5.0, 10.0], (10.0, 25.0], (25.0, 50.0],
21
19
  # (50.0, 75.0], (75.0, 100.0], (100.0, 250.0], (250.0, 500.0],
22
20
  # (500.0, 1000.0], (1000.0, +inf)
23
21
  def initialize(
24
- aggregation_temporality: ENV.fetch('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', :delta), # TODO: the default should be :cumulative, see issue #1555
22
+ aggregation_temporality: ENV.fetch('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', :cumulative),
25
23
  boundaries: DEFAULT_BOUNDARIES,
26
24
  record_min_max: true
27
25
  )
28
- @aggregation_temporality = aggregation_temporality.to_sym
26
+ @aggregation_temporality = AggregationTemporality.determine_temporality(aggregation_temporality: aggregation_temporality, default: :cumulative)
29
27
  @boundaries = boundaries && !boundaries.empty? ? boundaries.sort : nil
30
28
  @record_min_max = record_min_max
31
29
  end
32
30
 
33
31
  def collect(start_time, end_time, data_points)
34
- if @aggregation_temporality == :delta
32
+ if @aggregation_temporality.delta?
35
33
  # Set timestamps and 'move' data point values to result.
36
34
  hdps = data_points.values.map! do |hdp|
37
35
  hdp.start_time_unix_nano = start_time
@@ -87,6 +85,10 @@ module OpenTelemetry
87
85
  nil
88
86
  end
89
87
 
88
+ def aggregation_temporality
89
+ @aggregation_temporality.temporality
90
+ end
91
+
90
92
  private
91
93
 
92
94
  def empty_bucket_counts
@@ -16,22 +16,23 @@ module OpenTelemetry
16
16
  module Aggregation
17
17
  # Contains the implementation of the {https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram ExponentialBucketHistogram} aggregation
18
18
  class ExponentialBucketHistogram # rubocop:disable Metrics/ClassLength
19
- attr_reader :aggregation_temporality
20
-
21
19
  # relate to min max scale: https://opentelemetry.io/docs/specs/otel/metrics/sdk/#support-a-minimum-and-maximum-scale
20
+ DEFAULT_SIZE = 160
21
+ DEFAULT_SCALE = 20
22
22
  MAX_SCALE = 20
23
23
  MIN_SCALE = -10
24
- MAX_SIZE = 160
24
+ MIN_MAX_SIZE = 2
25
+ MAX_MAX_SIZE = 16_384
25
26
 
26
27
  # The default boundaries are calculated based on default max_size and max_scale values
27
28
  def initialize(
28
29
  aggregation_temporality: ENV.fetch('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', :delta),
29
- max_size: MAX_SIZE,
30
- max_scale: MAX_SCALE,
30
+ max_size: DEFAULT_SIZE,
31
+ max_scale: DEFAULT_SCALE,
31
32
  record_min_max: true,
32
33
  zero_threshold: 0
33
34
  )
34
- @aggregation_temporality = aggregation_temporality.to_sym
35
+ @aggregation_temporality = AggregationTemporality.determine_temporality(aggregation_temporality: aggregation_temporality, default: :delta)
35
36
  @record_min_max = record_min_max
36
37
  @min = Float::INFINITY
37
38
  @max = -Float::INFINITY
@@ -46,7 +47,7 @@ module OpenTelemetry
46
47
  end
47
48
 
48
49
  def collect(start_time, end_time, data_points)
49
- if @aggregation_temporality == :delta
50
+ if @aggregation_temporality.delta?
50
51
  # Set timestamps and 'move' data point values to result.
51
52
  hdps = data_points.values.map! do |hdp|
52
53
  hdp.start_time_unix_nano = start_time
@@ -165,6 +166,10 @@ module OpenTelemetry
165
166
  end
166
167
  # rubocop:enable Metrics/MethodLength
167
168
 
169
+ def aggregation_temporality
170
+ @aggregation_temporality.temporality
171
+ end
172
+
168
173
  private
169
174
 
170
175
  def grow_buckets(span, buckets)
@@ -175,6 +180,7 @@ module OpenTelemetry
175
180
  end
176
181
 
177
182
  def new_mapping(scale)
183
+ scale = validate_scale(scale)
178
184
  scale <= 0 ? ExponentialHistogram::ExponentMapping.new(scale) : ExponentialHistogram::LogarithmMapping.new(scale)
179
185
  end
180
186
 
@@ -203,17 +209,17 @@ module OpenTelemetry
203
209
  end
204
210
 
205
211
  def validate_scale(scale)
206
- return scale unless scale > MAX_SCALE || scale < MIN_SCALE
212
+ raise ArgumentError, "Scale #{scale} is larger than maximum scale #{MAX_SCALE}" if scale > MAX_SCALE
213
+ raise ArgumentError, "Scale #{scale} is smaller than minimum scale #{MIN_SCALE}" if scale < MIN_SCALE
207
214
 
208
- OpenTelemetry.logger.warn "Scale #{scale} is invalid, using default max scale #{MAX_SCALE}"
209
- MAX_SCALE
215
+ scale
210
216
  end
211
217
 
212
218
  def validate_size(size)
213
- return size unless size > MAX_SIZE || size < 0
219
+ raise ArgumentError, "Max size #{size} is smaller than minimum size #{MIN_MAX_SIZE}" if size < MIN_MAX_SIZE
220
+ raise ArgumentError, "Max size #{size} is larger than maximum size #{MAX_MAX_SIZE}" if size > MAX_MAX_SIZE
214
221
 
215
- OpenTelemetry.logger.warn "Size #{size} is invalid, using default max size #{MAX_SIZE}"
216
- MAX_SIZE
222
+ size
217
223
  end
218
224
  end
219
225
  end
@@ -10,30 +10,14 @@ module OpenTelemetry
10
10
  module Aggregation
11
11
  # Contains the implementation of the LastValue aggregation
12
12
  class LastValue
13
- attr_reader :aggregation_temporality
14
-
15
- def initialize(aggregation_temporality: :delta)
16
- @aggregation_temporality = aggregation_temporality
17
- end
18
-
19
13
  def collect(start_time, end_time, data_points)
20
- if @aggregation_temporality == :delta
21
- # Set timestamps and 'move' data point values to result.
22
- ndps = data_points.values.map! do |ndp|
23
- ndp.start_time_unix_nano = start_time
24
- ndp.time_unix_nano = end_time
25
- ndp
26
- end
27
- data_points.clear
28
- ndps
29
- else
30
- # Update timestamps and take a snapshot.
31
- data_points.values.map! do |ndp|
32
- ndp.start_time_unix_nano ||= start_time # Start time of a data point is from the first observation.
33
- ndp.time_unix_nano = end_time
34
- ndp.dup
35
- end
14
+ ndps = data_points.values.map! do |ndp|
15
+ ndp.start_time_unix_nano = start_time
16
+ ndp.time_unix_nano = end_time
17
+ ndp
36
18
  end
19
+ data_points.clear
20
+ ndps
37
21
  end
38
22
 
39
23
  def update(increment, attributes, data_points)
@@ -11,16 +11,13 @@ module OpenTelemetry
11
11
  # Contains the implementation of the Sum aggregation
12
12
  # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#sum-aggregation
13
13
  class Sum
14
- attr_reader :aggregation_temporality
15
-
16
- def initialize(aggregation_temporality: ENV.fetch('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', :delta), monotonic: false)
17
- # TODO: the default should be :cumulative, see issue #1555
18
- @aggregation_temporality = aggregation_temporality.to_sym
14
+ def initialize(aggregation_temporality: ENV.fetch('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', :cumulative), monotonic: false, instrument_kind: nil)
15
+ @aggregation_temporality = AggregationTemporality.determine_temporality(aggregation_temporality: aggregation_temporality, instrument_kind: instrument_kind, default: :cumulative)
19
16
  @monotonic = monotonic
20
17
  end
21
18
 
22
19
  def collect(start_time, end_time, data_points)
23
- if @aggregation_temporality == :delta
20
+ if @aggregation_temporality.delta?
24
21
  # Set timestamps and 'move' data point values to result.
25
22
  ndps = data_points.values.map! do |ndp|
26
23
  ndp.start_time_unix_nano = start_time
@@ -57,6 +54,10 @@ module OpenTelemetry
57
54
  ndp.value += increment
58
55
  nil
59
56
  end
57
+
58
+ def aggregation_temporality
59
+ @aggregation_temporality.temporality
60
+ end
60
61
  end
61
62
  end
62
63
  end
@@ -15,6 +15,7 @@ module OpenTelemetry
15
15
  end
16
16
  end
17
17
 
18
+ require 'opentelemetry/sdk/metrics/aggregation/aggregation_temporality'
18
19
  require 'opentelemetry/sdk/metrics/aggregation/number_data_point'
19
20
  require 'opentelemetry/sdk/metrics/aggregation/histogram_data_point'
20
21
  require 'opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram'
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Metrics
10
+ module Instrument
11
+ # {AsynchronousInstrument} contains the common functionality shared across
12
+ # the asynchronous instruments SDK instruments.
13
+ class AsynchronousInstrument
14
+ def initialize(name, unit, description, callback, instrumentation_scope, meter_provider)
15
+ @name = name
16
+ @unit = unit
17
+ @description = description
18
+ @instrumentation_scope = instrumentation_scope
19
+ @meter_provider = meter_provider
20
+ @metric_streams = []
21
+ @callbacks = []
22
+ @timeout = nil
23
+ @attributes = {}
24
+
25
+ init_callback(callback)
26
+ meter_provider.register_asynchronous_instrument(self)
27
+ end
28
+
29
+ # @api private
30
+ def register_with_new_metric_store(metric_store, aggregation: default_aggregation)
31
+ ms = OpenTelemetry::SDK::Metrics::State::AsynchronousMetricStream.new(
32
+ @name,
33
+ @description,
34
+ @unit,
35
+ instrument_kind,
36
+ @meter_provider,
37
+ @instrumentation_scope,
38
+ aggregation,
39
+ @callbacks,
40
+ @timeout,
41
+ @attributes
42
+ )
43
+ @metric_streams << ms
44
+ metric_store.add_metric_stream(ms)
45
+ end
46
+
47
+ # The API MUST support creation of asynchronous instruments by passing zero or more callback functions
48
+ # to be permanently registered to the newly created instrument.
49
+ def init_callback(callback)
50
+ if callback.instance_of?(Proc)
51
+ @callbacks << callback
52
+ elsif callback.instance_of?(Array)
53
+ callback.each { |cb| @callbacks << cb if cb.instance_of?(Proc) }
54
+ else
55
+ OpenTelemetry.logger.warn "Only accept single Proc or Array of Proc for initialization with callback (given callback #{callback.class}"
56
+ end
57
+ end
58
+
59
+ # Where the API supports registration of callback functions after asynchronous instrumentation creation,
60
+ # the user MUST be able to undo registration of the specific callback after its registration by some means.
61
+ def register_callback(callback)
62
+ if callback.instance_of?(Proc)
63
+ @callbacks << callback
64
+ callback
65
+ else
66
+ OpenTelemetry.logger.warn "Only accept single Proc for registering callback (given callback #{callback.class}"
67
+ end
68
+ end
69
+
70
+ def unregister(callback)
71
+ @callbacks.delete(callback)
72
+ end
73
+
74
+ def timeout(timeout)
75
+ @timeout = timeout
76
+ end
77
+
78
+ def add_attributes(attributes)
79
+ @attributes.merge!(attributes) if attributes.instance_of?(Hash)
80
+ end
81
+
82
+ private
83
+
84
+ # update the observed value (after calling observe)
85
+ # invoke callback will execute callback and export metric_data that is observed
86
+ def update(timeout, attributes)
87
+ @metric_streams.each { |ms| ms.invoke_callback(timeout, attributes) }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -8,16 +8,31 @@ module OpenTelemetry
8
8
  module SDK
9
9
  module Metrics
10
10
  module Instrument
11
- # {ObservableCounter} is the SDK implementation of {OpenTelemetry::Metrics::ObservableCounter}.
12
- class ObservableCounter < OpenTelemetry::Metrics::Instrument::ObservableCounter
13
- attr_reader :name, :unit, :description
11
+ # {ObservableCounter} is the SDK implementation of {OpenTelemetry::SDK::Metrics::Instrument::AsynchronousInstrument}.
12
+ # Asynchronous Counter is an asynchronous Instrument which reports monotonically increasing value(s) when the instrument is being observed.
13
+ class ObservableCounter < OpenTelemetry::SDK::Metrics::Instrument::AsynchronousInstrument
14
+ # Returns the instrument kind as a Symbol
15
+ #
16
+ # @return [Symbol]
17
+ def instrument_kind
18
+ :observable_counter
19
+ end
20
+
21
+ # Observe the ObservableCounter with fixed timeout duration.
22
+ #
23
+ # @param [int] timeout The timeout duration for callback to run, which MUST be a non-negative numeric value.
24
+ # @param [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] attributes
25
+ # Values must be non-nil and (array of) string, boolean or numeric type.
26
+ # Array values must not contain nil elements and all elements must be of
27
+ # the same basic type (string, numeric, boolean).
28
+ def observe(timeout: nil, attributes: {})
29
+ update(timeout, attributes)
30
+ end
31
+
32
+ private
14
33
 
15
- def initialize(name, unit, description, callback, meter)
16
- @name = name
17
- @unit = unit
18
- @description = description
19
- @callback = callback
20
- @meter = meter
34
+ def default_aggregation
35
+ OpenTelemetry::SDK::Metrics::Aggregation::Sum.new(monotonic: true, instrument_kind: instrument_kind)
21
36
  end
22
37
  end
23
38
  end
@@ -8,16 +8,31 @@ module OpenTelemetry
8
8
  module SDK
9
9
  module Metrics
10
10
  module Instrument
11
- # {ObservableGauge} is the SDK implementation of {OpenTelemetry::Metrics::ObservableGauge}.
12
- class ObservableGauge < OpenTelemetry::Metrics::Instrument::ObservableGauge
13
- attr_reader :name, :unit, :description
11
+ # {ObservableGauge} is the SDK implementation of {OpenTelemetry::SDK::Metrics::Instrument::AsynchronousInstrument}.
12
+ # Asynchronous Gauge is an asynchronous Instrument which reports non-additive value(s) (e.g. the room temperature)
13
+ class ObservableGauge < OpenTelemetry::SDK::Metrics::Instrument::AsynchronousInstrument
14
+ # Returns the instrument kind as a Symbol
15
+ #
16
+ # @return [Symbol]
17
+ def instrument_kind
18
+ :observable_gauge
19
+ end
20
+
21
+ # Observe the Gauge with fixed timeout duration.
22
+ #
23
+ # @param [int] timeout The timeout duration for callback to run, which MUST be a non-negative numeric value.
24
+ # @param [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] attributes
25
+ # Values must be non-nil and (array of) string, boolean or numeric type.
26
+ # Array values must not contain nil elements and all elements must be of
27
+ # the same basic type (string, numeric, boolean).
28
+ def observe(timeout: nil, attributes: {})
29
+ update(timeout, attributes)
30
+ end
31
+
32
+ private
14
33
 
15
- def initialize(name, unit, description, callback, meter)
16
- @name = name
17
- @unit = unit
18
- @description = description
19
- @callback = callback
20
- @meter = meter
34
+ def default_aggregation
35
+ OpenTelemetry::SDK::Metrics::Aggregation::LastValue.new
21
36
  end
22
37
  end
23
38
  end
@@ -8,16 +8,31 @@ module OpenTelemetry
8
8
  module SDK
9
9
  module Metrics
10
10
  module Instrument
11
- # {ObservableUpDownCounter} is the SDK implementation of {OpenTelemetry::Metrics::ObservableUpDownCounter}.
12
- class ObservableUpDownCounter < OpenTelemetry::Metrics::Instrument::ObservableUpDownCounter
13
- attr_reader :name, :unit, :description
11
+ # {ObservableUpDownCounter} is the SDK implementation of {OpenTelemetry::SDK::Metrics::Instrument::AsynchronousInstrument}.
12
+ # Asynchronous UpDownCounter is an asynchronous Instrument which reports additive value(s) (e.g. the process heap size)
13
+ class ObservableUpDownCounter < OpenTelemetry::SDK::Metrics::Instrument::AsynchronousInstrument
14
+ # Returns the instrument kind as a Symbol
15
+ #
16
+ # @return [Symbol]
17
+ def instrument_kind
18
+ :observable_up_down_counter
19
+ end
20
+
21
+ # Observe the UpDownCounter with fixed timeout duration.
22
+ #
23
+ # @param [int] timeout The timeout duration for callback to run, which MUST be a non-negative numeric value.
24
+ # @param [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] attributes
25
+ # Values must be non-nil and (array of) string, boolean or numeric type.
26
+ # Array values must not contain nil elements and all elements must be of
27
+ # the same basic type (string, numeric, boolean).
28
+ def observe(timeout: nil, attributes: {})
29
+ update(timeout, attributes)
30
+ end
31
+
32
+ private
14
33
 
15
- def initialize(name, unit, description, callback, meter)
16
- @name = name
17
- @unit = unit
18
- @description = description
19
- @callback = callback
20
- @meter = meter
34
+ def default_aggregation
35
+ OpenTelemetry::SDK::Metrics::Aggregation::Sum.new(aggregation_temporality: :cumulative, monotonic: false)
21
36
  end
22
37
  end
23
38
  end
@@ -35,7 +35,7 @@ module OpenTelemetry
35
35
  private
36
36
 
37
37
  def default_aggregation
38
- OpenTelemetry::SDK::Metrics::Aggregation::Sum.new(monotonic: false)
38
+ OpenTelemetry::SDK::Metrics::Aggregation::Sum.new(aggregation_temporality: :cumulative, monotonic: false)
39
39
  end
40
40
  end
41
41
  end
@@ -14,6 +14,7 @@ module OpenTelemetry
14
14
  end
15
15
 
16
16
  require 'opentelemetry/sdk/metrics/instrument/synchronous_instrument'
17
+ require 'opentelemetry/sdk/metrics/instrument/asynchronous_instrument'
17
18
  require 'opentelemetry/sdk/metrics/instrument/counter'
18
19
  require 'opentelemetry/sdk/metrics/instrument/histogram'
19
20
  require 'opentelemetry/sdk/metrics/instrument/observable_counter'
@@ -11,7 +11,7 @@ module OpenTelemetry
11
11
  module Metrics
12
12
  # {Meter} is the SDK implementation of {OpenTelemetry::Metrics::Meter}.
13
13
  class Meter < OpenTelemetry::Metrics::Meter
14
- NAME_REGEX = /\A[a-zA-Z][-.\w]{0,62}\z/
14
+ NAME_REGEX = %r{\A[a-zA-Z][-./\w]{0,254}\z}
15
15
 
16
16
  # @api private
17
17
  #
@@ -28,6 +28,30 @@ module OpenTelemetry
28
28
  @meter_provider = meter_provider
29
29
  end
30
30
 
31
+ # Multiple-instrument callbacks
32
+ # Callbacks registered after the time of instrument creation MAY be associated with multiple instruments.
33
+ # Related spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#multiple-instrument-callbacks
34
+ # Related spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#synchronous-instrument-api
35
+ #
36
+ # @param [Array] instruments A list (or tuple, etc.) of Instruments used in the callback function.
37
+ # @param [Proc] callback A callback function
38
+ #
39
+ # It is RECOMMENDED that the API authors use one of the following forms for the callback function:
40
+ # The list (or tuple, etc.) returned by the callback function contains (Instrument, Measurement) pairs.
41
+ # the Observable Result parameter receives an additional (Instrument, Measurement) pairs
42
+ # Here it chose the second form
43
+ def register_callback(instruments, callback)
44
+ instruments.each do |instrument|
45
+ instrument.register_callback(callback)
46
+ end
47
+ end
48
+
49
+ def unregister(instruments, callback)
50
+ instruments.each do |instrument|
51
+ instrument.unregister(callback)
52
+ end
53
+ end
54
+
31
55
  # @api private
32
56
  def add_metric_reader(metric_reader)
33
57
  @instrument_registry.each_value do |instrument|
@@ -126,6 +126,7 @@ module OpenTelemetry
126
126
  end
127
127
  end
128
128
  end
129
+ alias register_asynchronous_instrument register_synchronous_instrument
129
130
 
130
131
  # A View provides SDK users with the flexibility to customize the metrics that are output by the SDK.
131
132
  #
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Metrics
10
+ module State
11
+ # @api private
12
+ #
13
+ # The AsynchronousMetricStream class provides SDK internal functionality that is not a part of the
14
+ # public API. It extends MetricStream to support asynchronous instruments.
15
+ class AsynchronousMetricStream < MetricStream
16
+ def initialize(
17
+ name,
18
+ description,
19
+ unit,
20
+ instrument_kind,
21
+ meter_provider,
22
+ instrumentation_scope,
23
+ aggregation,
24
+ callback,
25
+ timeout,
26
+ attributes
27
+ )
28
+ # Call parent constructor with common parameters
29
+ super(name, description, unit, instrument_kind, meter_provider, instrumentation_scope, aggregation)
30
+
31
+ # Initialize asynchronous-specific attributes
32
+ @callback = callback
33
+ @start_time = now_in_nano
34
+ @timeout = timeout
35
+ @attributes = attributes
36
+ end
37
+
38
+ # When collect, if there are asynchronous SDK Instruments involved, their callback functions will be triggered.
39
+ # Related spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#collect
40
+ # invoke_callback will update the data_points in aggregation
41
+ def collect(start_time, end_time)
42
+ invoke_callback(@timeout, @attributes)
43
+
44
+ # Call parent collect method for the core collection logic
45
+ super(start_time, end_time)
46
+ end
47
+
48
+ def invoke_callback(timeout, attributes)
49
+ if @registered_views.empty?
50
+ @mutex.synchronize do
51
+ Timeout.timeout(timeout || 30) do
52
+ @callback.each do |cb|
53
+ value = cb.call
54
+ @default_aggregation.update(value, attributes, @data_points)
55
+ end
56
+ end
57
+ end
58
+ else
59
+ @registered_views.each do |view|
60
+ @mutex.synchronize do
61
+ Timeout.timeout(timeout || 30) do
62
+ @callback.each do |cb|
63
+ value = cb.call
64
+ merged_attributes = attributes || {}
65
+ merged_attributes.merge!(view.attribute_keys)
66
+ view.aggregation.update(value, merged_attributes, @data_points) if view.valid_aggregation?
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def now_in_nano
75
+ (Time.now.to_r * 1_000_000_000).to_i
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -72,6 +72,7 @@ module OpenTelemetry
72
72
  def aggregate_metric_data(start_time, end_time, aggregation: nil)
73
73
  aggregator = aggregation || @default_aggregation
74
74
  is_monotonic = aggregator.respond_to?(:monotonic?) ? aggregator.monotonic? : nil
75
+ aggregation_temporality = aggregator.respond_to?(:aggregation_temporality) ? aggregator.aggregation_temporality : nil
75
76
 
76
77
  MetricData.new(
77
78
  @name,
@@ -81,7 +82,7 @@ module OpenTelemetry
81
82
  @meter_provider.resource,
82
83
  @instrumentation_scope,
83
84
  aggregator.collect(start_time, end_time, @data_points),
84
- aggregator.aggregation_temporality,
85
+ aggregation_temporality,
85
86
  start_time,
86
87
  end_time,
87
88
  is_monotonic
@@ -20,3 +20,4 @@ end
20
20
  require 'opentelemetry/sdk/metrics/state/metric_data'
21
21
  require 'opentelemetry/sdk/metrics/state/metric_store'
22
22
  require 'opentelemetry/sdk/metrics/state/metric_stream'
23
+ require 'opentelemetry/sdk/metrics/state/asynchronous_metric_stream'
@@ -8,7 +8,7 @@ module OpenTelemetry
8
8
  module SDK
9
9
  module Metrics
10
10
  # Current OpenTelemetry metrics sdk version
11
- VERSION = '0.7.3'
11
+ VERSION = '0.9.0'
12
12
  end
13
13
  end
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentelemetry-metrics-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenTelemetry Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-09 00:00:00.000000000 Z
11
+ date: 2025-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opentelemetry-api
@@ -206,6 +206,7 @@ files:
206
206
  - lib/opentelemetry-metrics-sdk.rb
207
207
  - lib/opentelemetry/sdk/metrics.rb
208
208
  - lib/opentelemetry/sdk/metrics/aggregation.rb
209
+ - lib/opentelemetry/sdk/metrics/aggregation/aggregation_temporality.rb
209
210
  - lib/opentelemetry/sdk/metrics/aggregation/drop.rb
210
211
  - lib/opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram.rb
211
212
  - lib/opentelemetry/sdk/metrics/aggregation/exponential_bucket_histogram.rb
@@ -227,6 +228,7 @@ files:
227
228
  - lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb
228
229
  - lib/opentelemetry/sdk/metrics/fork_hooks.rb
229
230
  - lib/opentelemetry/sdk/metrics/instrument.rb
231
+ - lib/opentelemetry/sdk/metrics/instrument/asynchronous_instrument.rb
230
232
  - lib/opentelemetry/sdk/metrics/instrument/counter.rb
231
233
  - lib/opentelemetry/sdk/metrics/instrument/gauge.rb
232
234
  - lib/opentelemetry/sdk/metrics/instrument/histogram.rb
@@ -238,6 +240,7 @@ files:
238
240
  - lib/opentelemetry/sdk/metrics/meter.rb
239
241
  - lib/opentelemetry/sdk/metrics/meter_provider.rb
240
242
  - lib/opentelemetry/sdk/metrics/state.rb
243
+ - lib/opentelemetry/sdk/metrics/state/asynchronous_metric_stream.rb
241
244
  - lib/opentelemetry/sdk/metrics/state/metric_data.rb
242
245
  - lib/opentelemetry/sdk/metrics/state/metric_store.rb
243
246
  - lib/opentelemetry/sdk/metrics/state/metric_stream.rb
@@ -248,10 +251,10 @@ homepage: https://github.com/open-telemetry/opentelemetry-ruby
248
251
  licenses:
249
252
  - Apache-2.0
250
253
  metadata:
251
- changelog_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-metrics-sdk/v0.7.3/file.CHANGELOG.html
254
+ changelog_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-metrics-sdk/v0.9.0/file.CHANGELOG.html
252
255
  source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/metrics_sdk
253
256
  bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby/issues
254
- documentation_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-metrics-sdk/v0.7.3
257
+ documentation_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-metrics-sdk/v0.9.0
255
258
  post_install_message:
256
259
  rdoc_options: []
257
260
  require_paths: