opentelemetry-metrics-sdk 0.14.0 → 0.15.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/opentelemetry/sdk/metrics/aggregation/drop.rb +1 -1
- data/lib/opentelemetry/sdk/metrics/aggregation/explicit_bucket_histogram.rb +56 -39
- data/lib/opentelemetry/sdk/metrics/aggregation/exponential_bucket_histogram.rb +61 -48
- data/lib/opentelemetry/sdk/metrics/aggregation/last_value.rb +38 -15
- data/lib/opentelemetry/sdk/metrics/aggregation/sum.rb +36 -16
- data/lib/opentelemetry/sdk/metrics/export/console_metric_pull_exporter.rb +2 -2
- data/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb +2 -2
- data/lib/opentelemetry/sdk/metrics/export/metric_reader.rb +2 -2
- data/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb +3 -2
- data/lib/opentelemetry/sdk/metrics/state/asynchronous_metric_stream.rb +8 -2
- data/lib/opentelemetry/sdk/metrics/state/metric_store.rb +3 -1
- data/lib/opentelemetry/sdk/metrics/state/metric_stream.rb +26 -4
- data/lib/opentelemetry/sdk/metrics/version.rb +1 -1
- data/lib/opentelemetry/sdk/metrics/view/registered_view.rb +2 -1
- metadata +5 -103
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fee8234836c749267c8ef70c3a6e1edd1e7f07f2f684be31be5bbb692783bbe5
|
|
4
|
+
data.tar.gz: d95498e59164701c62e415db1d99d20f201f77a7a9d621667603bdb7485c668c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13fc13a8866034bbd502032c7b6aef1fe485591d065cd01d533eec77ed9aa3fc8c3e11def8317b891bda9347317558ea0322bc211c00d46c3ee0cbc3a57c2bce
|
|
7
|
+
data.tar.gz: 123a2e82d19c865243d45d0e2e29dce12e2c3166813ccdc8ad5e10ea45fb6e31d6d9c54b9558f36c380e8ece670146c9ba1b2450286b03a6ab6c70ec28db0839
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Release History: opentelemetry-metrics-sdk
|
|
2
2
|
|
|
3
|
+
### v0.15.0 / 2026-06-16
|
|
4
|
+
|
|
5
|
+
* BREAKING CHANGE: Metrics cardinality limit (#1909)
|
|
6
|
+
* ADDED: Metrics cardinality limit (#1909)
|
|
7
|
+
|
|
3
8
|
### v0.14.0 / 2026-05-12
|
|
4
9
|
|
|
5
10
|
* BREAKING CHANGE: Use trace_based exemplar filter by default (#2112)
|
|
@@ -22,7 +22,7 @@ module OpenTelemetry
|
|
|
22
22
|
data_points.values.map!(&:dup)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def update(increment, attributes, data_points, exemplar_offer: false)
|
|
25
|
+
def update(increment, attributes, data_points, cardinality_limit, exemplar_offer: false)
|
|
26
26
|
data_points[attributes] = NumberDataPoint.new(
|
|
27
27
|
{},
|
|
28
28
|
0,
|
|
@@ -11,6 +11,7 @@ module OpenTelemetry
|
|
|
11
11
|
# Contains the implementation of the ExplicitBucketHistogram aggregation
|
|
12
12
|
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
|
|
13
13
|
class ExplicitBucketHistogram
|
|
14
|
+
OVERFLOW_ATTRIBUTE_SET = { 'otel.metric.overflow' => true }.freeze
|
|
14
15
|
attr_reader :exemplar_reservoir
|
|
15
16
|
|
|
16
17
|
DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze
|
|
@@ -59,41 +60,48 @@ module OpenTelemetry
|
|
|
59
60
|
end
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
def update(amount, attributes, data_points, exemplar_offer: false)
|
|
63
|
-
hdp = data_points.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
def update(amount, attributes, data_points, cardinality_limit, exemplar_offer: false)
|
|
64
|
+
hdp = if data_points.key?(attributes)
|
|
65
|
+
data_points[attributes]
|
|
66
|
+
elsif data_points.size >= cardinality_limit - 1
|
|
67
|
+
data_points[OVERFLOW_ATTRIBUTE_SET] || create_new_data_point(OVERFLOW_ATTRIBUTE_SET, data_points)
|
|
68
|
+
else
|
|
69
|
+
create_new_data_point(attributes, data_points)
|
|
70
|
+
end
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
nil, # :time_unix_nano
|
|
73
|
-
0, # :count
|
|
74
|
-
0, # :sum
|
|
75
|
-
empty_bucket_counts, # :bucket_counts
|
|
76
|
-
@boundaries, # :explicit_bounds
|
|
77
|
-
nil, # :exemplars
|
|
78
|
-
min, # :min
|
|
79
|
-
max # :max
|
|
80
|
-
)
|
|
81
|
-
end
|
|
72
|
+
update_histogram_data_point(hdp, amount, exemplar_offer: exemplar_offer)
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
82
75
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
reservoir.reset
|
|
87
|
-
@exemplar_reservoir_storage[attributes] = reservoir
|
|
88
|
-
end
|
|
76
|
+
def aggregation_temporality
|
|
77
|
+
@aggregation_temporality.temporality
|
|
78
|
+
end
|
|
89
79
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def create_new_data_point(attributes, data_points)
|
|
83
|
+
if @record_min_max
|
|
84
|
+
min = Float::INFINITY
|
|
85
|
+
max = -Float::INFINITY
|
|
95
86
|
end
|
|
96
87
|
|
|
88
|
+
data_points[attributes] = HistogramDataPoint.new(
|
|
89
|
+
attributes,
|
|
90
|
+
nil, # :start_time_unix_nano
|
|
91
|
+
nil, # :time_unix_nano
|
|
92
|
+
0, # :count
|
|
93
|
+
0, # :sum
|
|
94
|
+
empty_bucket_counts, # :bucket_counts
|
|
95
|
+
@boundaries, # :explicit_bounds
|
|
96
|
+
nil, # :exemplars
|
|
97
|
+
min, # :min
|
|
98
|
+
max # :max
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def update_histogram_data_point(hdp, amount, exemplar_offer: false)
|
|
103
|
+
reservior_update(hdp.attributes, amount, exemplar_offer)
|
|
104
|
+
|
|
97
105
|
if @record_min_max
|
|
98
106
|
hdp.max = amount if amount > hdp.max
|
|
99
107
|
hdp.min = amount if amount < hdp.min
|
|
@@ -101,18 +109,27 @@ module OpenTelemetry
|
|
|
101
109
|
|
|
102
110
|
hdp.sum += amount
|
|
103
111
|
hdp.count += 1
|
|
104
|
-
|
|
105
|
-
bucket_index = @boundaries.bsearch_index { |i| i >= amount } || @boundaries.size
|
|
106
|
-
hdp.bucket_counts[bucket_index] += 1
|
|
107
|
-
end
|
|
108
|
-
nil
|
|
109
|
-
end
|
|
112
|
+
return unless @boundaries
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
bucket_index = @boundaries.bsearch_index { |i| i >= amount } || @boundaries.size
|
|
115
|
+
hdp.bucket_counts[bucket_index] += 1
|
|
113
116
|
end
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
def reservior_update(attributes, amount, exemplar_offer)
|
|
119
|
+
reservoir = @exemplar_reservoir_storage[attributes]
|
|
120
|
+
unless reservoir
|
|
121
|
+
reservoir = @exemplar_reservoir.dup
|
|
122
|
+
reservoir.reset
|
|
123
|
+
@exemplar_reservoir_storage[attributes] = reservoir
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
return unless exemplar_offer
|
|
127
|
+
|
|
128
|
+
reservoir.offer(value: amount,
|
|
129
|
+
timestamp: OpenTelemetry::Common::Utilities.time_in_nanoseconds,
|
|
130
|
+
attributes: attributes,
|
|
131
|
+
context: OpenTelemetry::Context.current)
|
|
132
|
+
end
|
|
116
133
|
|
|
117
134
|
def empty_bucket_counts
|
|
118
135
|
@boundaries ? Array.new(@boundaries.size + 1, 0) : nil
|
|
@@ -17,6 +17,8 @@ module OpenTelemetry
|
|
|
17
17
|
module Aggregation
|
|
18
18
|
# Contains the implementation of the {https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram ExponentialBucketHistogram} aggregation
|
|
19
19
|
class ExponentialBucketHistogram # rubocop:disable Metrics/ClassLength
|
|
20
|
+
OVERFLOW_ATTRIBUTE_SET = { 'otel.metric.overflow' => true }.freeze
|
|
21
|
+
|
|
20
22
|
# relate to min max scale: https://opentelemetry.io/docs/specs/otel/metrics/sdk/#support-a-minimum-and-maximum-scale
|
|
21
23
|
DEFAULT_SIZE = 160
|
|
22
24
|
DEFAULT_SCALE = 20
|
|
@@ -94,7 +96,6 @@ module OpenTelemetry
|
|
|
94
96
|
|
|
95
97
|
# this will slow down the operation especially if large amount of data_points present
|
|
96
98
|
# but it should be fine since with cumulative, the data_points are merged into previous_* and not kept in data_points
|
|
97
|
-
# rubocop:disable Metrics/BlockLength
|
|
98
99
|
data_points.each do |attributes, hdp|
|
|
99
100
|
# Store current values
|
|
100
101
|
current_positive = hdp.positive
|
|
@@ -181,7 +182,6 @@ module OpenTelemetry
|
|
|
181
182
|
merged_data_points[attributes] = merged_hdp
|
|
182
183
|
@previous_mappings[attributes] = @mappings[attributes] if @mappings[attributes] # Preserve mapping for next collection
|
|
183
184
|
end
|
|
184
|
-
# rubocop:enable Metrics/BlockLength
|
|
185
185
|
|
|
186
186
|
# when you have no local_data_points, the loop from cumulative aggregation will not run
|
|
187
187
|
# so return last merged data points if exists
|
|
@@ -221,49 +221,53 @@ module OpenTelemetry
|
|
|
221
221
|
# rubocop:enable Metrics/MethodLength
|
|
222
222
|
|
|
223
223
|
# this is aggregate in python; there is no merge in aggregate; but rescale happened
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
224
|
+
def update(amount, attributes, data_points, cardinality_limit, exemplar_offer: false)
|
|
225
|
+
hdp = if data_points.key?(attributes)
|
|
226
|
+
data_points[attributes]
|
|
227
|
+
elsif data_points.size >= cardinality_limit - 1
|
|
228
|
+
data_points[OVERFLOW_ATTRIBUTE_SET] || create_new_data_point(OVERFLOW_ATTRIBUTE_SET, data_points)
|
|
229
|
+
else
|
|
230
|
+
create_new_data_point(attributes, data_points)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
update_histogram_data_point(hdp, attributes, amount, exemplar_offer: exemplar_offer)
|
|
234
|
+
nil
|
|
235
|
+
end
|
|
232
236
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
nil, # :start_time_unix_nano
|
|
237
|
-
0, # :time_unix_nano
|
|
238
|
-
0, # :count
|
|
239
|
-
0, # :sum
|
|
240
|
-
@scale, # :scale
|
|
241
|
-
@zero_count, # :zero_count
|
|
242
|
-
ExponentialHistogram::Buckets.new, # :positive
|
|
243
|
-
ExponentialHistogram::Buckets.new, # :negative
|
|
244
|
-
0, # :flags
|
|
245
|
-
nil, # :exemplars
|
|
246
|
-
min, # :min
|
|
247
|
-
max, # :max
|
|
248
|
-
@zero_threshold # :zero_threshold
|
|
249
|
-
)
|
|
250
|
-
end
|
|
237
|
+
def aggregation_temporality
|
|
238
|
+
@aggregation_temporality.temporality
|
|
239
|
+
end
|
|
251
240
|
|
|
252
|
-
|
|
253
|
-
unless reservoir
|
|
254
|
-
reservoir = @exemplar_reservoir.dup
|
|
255
|
-
reservoir.reset
|
|
256
|
-
@exemplar_reservoir_storage[attributes] = reservoir
|
|
257
|
-
end
|
|
241
|
+
private
|
|
258
242
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
context: OpenTelemetry::Context.current)
|
|
243
|
+
def create_new_data_point(attributes, data_points)
|
|
244
|
+
if @record_min_max
|
|
245
|
+
min = Float::INFINITY
|
|
246
|
+
max = -Float::INFINITY
|
|
264
247
|
end
|
|
265
248
|
|
|
266
|
-
|
|
249
|
+
data_points[attributes] = ExponentialHistogramDataPoint.new(
|
|
250
|
+
attributes,
|
|
251
|
+
nil, # :start_time_unix_nano
|
|
252
|
+
0, # :time_unix_nano
|
|
253
|
+
0, # :count
|
|
254
|
+
0, # :sum
|
|
255
|
+
@scale, # :scale
|
|
256
|
+
@zero_count, # :zero_count
|
|
257
|
+
ExponentialHistogram::Buckets.new, # :positive
|
|
258
|
+
ExponentialHistogram::Buckets.new, # :negative
|
|
259
|
+
0, # :flags
|
|
260
|
+
nil, # :exemplars
|
|
261
|
+
min, # :min
|
|
262
|
+
max, # :max
|
|
263
|
+
@zero_threshold # :zero_threshold
|
|
264
|
+
)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
268
|
+
def update_histogram_data_point(hdp, attributes, amount, exemplar_offer: false)
|
|
269
|
+
reservior_update(attributes, amount, exemplar_offer)
|
|
270
|
+
|
|
267
271
|
if @record_min_max
|
|
268
272
|
hdp.max = amount if amount > hdp.max
|
|
269
273
|
hdp.min = amount if amount < hdp.min
|
|
@@ -339,15 +343,8 @@ module OpenTelemetry
|
|
|
339
343
|
bucket_index += buckets.counts.size if bucket_index.negative?
|
|
340
344
|
|
|
341
345
|
buckets.increment_bucket(bucket_index)
|
|
342
|
-
nil
|
|
343
346
|
end
|
|
344
|
-
# rubocop:enable Metrics/
|
|
345
|
-
|
|
346
|
-
def aggregation_temporality
|
|
347
|
-
@aggregation_temporality.temporality
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
private
|
|
347
|
+
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
351
348
|
|
|
352
349
|
def grow_buckets(span, buckets)
|
|
353
350
|
return if span < buckets.counts.size
|
|
@@ -356,6 +353,22 @@ module OpenTelemetry
|
|
|
356
353
|
buckets.grow(span + 1, @size)
|
|
357
354
|
end
|
|
358
355
|
|
|
356
|
+
def reservior_update(attributes, amount, exemplar_offer)
|
|
357
|
+
reservoir = @exemplar_reservoir_storage[attributes]
|
|
358
|
+
unless reservoir
|
|
359
|
+
reservoir = @exemplar_reservoir.dup
|
|
360
|
+
reservoir.reset
|
|
361
|
+
@exemplar_reservoir_storage[attributes] = reservoir
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
return unless exemplar_offer
|
|
365
|
+
|
|
366
|
+
reservoir.offer(value: amount,
|
|
367
|
+
timestamp: OpenTelemetry::Common::Utilities.time_in_nanoseconds,
|
|
368
|
+
attributes: attributes,
|
|
369
|
+
context: OpenTelemetry::Context.current)
|
|
370
|
+
end
|
|
371
|
+
|
|
359
372
|
def new_mapping(scale)
|
|
360
373
|
scale = validate_scale(scale)
|
|
361
374
|
scale <= 0 ? ExponentialHistogram::ExponentMapping.new(scale) : ExponentialHistogram::LogarithmMapping.new(scale)
|
|
@@ -10,6 +10,7 @@ module OpenTelemetry
|
|
|
10
10
|
module Aggregation
|
|
11
11
|
# Contains the implementation of the LastValue aggregation
|
|
12
12
|
class LastValue
|
|
13
|
+
OVERFLOW_ATTRIBUTE_SET = { 'otel.metric.overflow' => true }.freeze
|
|
13
14
|
attr_reader :exemplar_reservoir
|
|
14
15
|
|
|
15
16
|
# if no reservoir pass from instrument, then use this empty reservoir to avoid no method found error
|
|
@@ -33,29 +34,51 @@ module OpenTelemetry
|
|
|
33
34
|
ndps
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
def update(increment, attributes, data_points, exemplar_offer: false)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
def update(increment, attributes, data_points, cardinality_limit, exemplar_offer: false)
|
|
38
|
+
# Check if we already have this attribute set
|
|
39
|
+
ndp = if data_points.key?(attributes)
|
|
40
|
+
data_points[attributes]
|
|
41
|
+
elsif data_points.size >= cardinality_limit - 1
|
|
42
|
+
data_points[OVERFLOW_ATTRIBUTE_SET] || create_new_data_point(OVERFLOW_ATTRIBUTE_SET, data_points)
|
|
43
|
+
else
|
|
44
|
+
create_new_data_point(attributes, data_points)
|
|
45
|
+
end
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
attributes: attributes,
|
|
48
|
-
context: OpenTelemetry::Context.current)
|
|
49
|
-
end
|
|
47
|
+
update_number_data_point(ndp, increment, exemplar_offer: exemplar_offer)
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
50
|
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def create_new_data_point(attributes, data_points)
|
|
51
54
|
data_points[attributes] = NumberDataPoint.new(
|
|
52
55
|
attributes,
|
|
53
56
|
nil,
|
|
54
57
|
nil,
|
|
55
|
-
|
|
58
|
+
0,
|
|
56
59
|
nil
|
|
57
60
|
)
|
|
58
|
-
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def update_number_data_point(ndp, increment, exemplar_offer: false)
|
|
64
|
+
ndp.value = increment
|
|
65
|
+
reservior_update(ndp.attributes, increment, exemplar_offer)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def reservior_update(attributes, increment, exemplar_offer)
|
|
69
|
+
reservoir = @exemplar_reservoir_storage[attributes]
|
|
70
|
+
unless reservoir
|
|
71
|
+
reservoir = @exemplar_reservoir.dup
|
|
72
|
+
reservoir.reset
|
|
73
|
+
@exemplar_reservoir_storage[attributes] = reservoir
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return unless exemplar_offer
|
|
77
|
+
|
|
78
|
+
reservoir.offer(value: increment,
|
|
79
|
+
timestamp: OpenTelemetry::Common::Utilities.time_in_nanoseconds,
|
|
80
|
+
attributes: attributes,
|
|
81
|
+
context: OpenTelemetry::Context.current)
|
|
59
82
|
end
|
|
60
83
|
end
|
|
61
84
|
end
|
|
@@ -9,8 +9,8 @@ module OpenTelemetry
|
|
|
9
9
|
module Metrics
|
|
10
10
|
module Aggregation
|
|
11
11
|
# Contains the implementation of the Sum aggregation
|
|
12
|
-
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#sum-aggregation
|
|
13
12
|
class Sum
|
|
13
|
+
OVERFLOW_ATTRIBUTE_SET = { 'otel.metric.overflow' => true }.freeze
|
|
14
14
|
attr_reader :exemplar_reservoir
|
|
15
15
|
|
|
16
16
|
# if no reservior pass from instrument, then use this empty reservior to avoid no method found error
|
|
@@ -51,21 +51,48 @@ module OpenTelemetry
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
def update(increment, attributes, data_points, cardinality_limit, exemplar_offer: false)
|
|
55
|
+
return if @monotonic && increment < 0
|
|
56
|
+
|
|
57
|
+
# Check if we already have this attribute set
|
|
58
|
+
ndp = if data_points.key?(attributes)
|
|
59
|
+
data_points[attributes]
|
|
60
|
+
elsif data_points.size >= cardinality_limit - 1
|
|
61
|
+
data_points[OVERFLOW_ATTRIBUTE_SET] || create_new_data_point(OVERFLOW_ATTRIBUTE_SET, data_points)
|
|
62
|
+
else
|
|
63
|
+
create_new_data_point(attributes, data_points)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
update_number_data_point(ndp, increment, exemplar_offer: exemplar_offer)
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
54
70
|
def monotonic?
|
|
55
71
|
@monotonic
|
|
56
72
|
end
|
|
57
73
|
|
|
58
|
-
def
|
|
59
|
-
|
|
74
|
+
def aggregation_temporality
|
|
75
|
+
@aggregation_temporality.temporality
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
60
79
|
|
|
61
|
-
|
|
80
|
+
def create_new_data_point(attributes, data_points)
|
|
81
|
+
data_points[attributes] = NumberDataPoint.new(
|
|
62
82
|
attributes,
|
|
63
83
|
nil,
|
|
64
84
|
nil,
|
|
65
85
|
0,
|
|
66
86
|
nil
|
|
67
87
|
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def update_number_data_point(ndp, increment, exemplar_offer: false)
|
|
91
|
+
reservior_update(ndp.attributes, increment, exemplar_offer)
|
|
92
|
+
ndp.value += increment
|
|
93
|
+
end
|
|
68
94
|
|
|
95
|
+
def reservior_update(attributes, increment, exemplar_offer)
|
|
69
96
|
reservoir = @exemplar_reservoir_storage[attributes]
|
|
70
97
|
unless reservoir
|
|
71
98
|
reservoir = @exemplar_reservoir.dup
|
|
@@ -73,19 +100,12 @@ module OpenTelemetry
|
|
|
73
100
|
@exemplar_reservoir_storage[attributes] = reservoir
|
|
74
101
|
end
|
|
75
102
|
|
|
76
|
-
|
|
77
|
-
reservoir.offer(value: increment,
|
|
78
|
-
timestamp: OpenTelemetry::Common::Utilities.time_in_nanoseconds,
|
|
79
|
-
attributes: attributes,
|
|
80
|
-
context: OpenTelemetry::Context.current)
|
|
81
|
-
end
|
|
103
|
+
return unless exemplar_offer
|
|
82
104
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def aggregation_temporality
|
|
88
|
-
@aggregation_temporality.temporality
|
|
105
|
+
reservoir.offer(value: increment,
|
|
106
|
+
timestamp: OpenTelemetry::Common::Utilities.time_in_nanoseconds,
|
|
107
|
+
attributes: attributes,
|
|
108
|
+
context: OpenTelemetry::Context.current)
|
|
89
109
|
end
|
|
90
110
|
end
|
|
91
111
|
end
|
|
@@ -12,8 +12,8 @@ module OpenTelemetry
|
|
|
12
12
|
#
|
|
13
13
|
# Potentially useful for exploratory purposes.
|
|
14
14
|
class ConsoleMetricPullExporter < MetricReader
|
|
15
|
-
def initialize
|
|
16
|
-
super
|
|
15
|
+
def initialize(aggregation_cardinality_limit: nil)
|
|
16
|
+
super(aggregation_cardinality_limit: aggregation_cardinality_limit)
|
|
17
17
|
@stopped = false
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -13,8 +13,8 @@ module OpenTelemetry
|
|
|
13
13
|
class InMemoryMetricPullExporter < MetricReader
|
|
14
14
|
attr_reader :metric_snapshots
|
|
15
15
|
|
|
16
|
-
def initialize
|
|
17
|
-
super
|
|
16
|
+
def initialize(aggregation_cardinality_limit: nil)
|
|
17
|
+
super(aggregation_cardinality_limit: aggregation_cardinality_limit)
|
|
18
18
|
@metric_snapshots = []
|
|
19
19
|
@mutex = Mutex.new
|
|
20
20
|
end
|
|
@@ -14,8 +14,8 @@ module OpenTelemetry
|
|
|
14
14
|
class MetricReader
|
|
15
15
|
attr_reader :metric_store
|
|
16
16
|
|
|
17
|
-
def initialize
|
|
18
|
-
@metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new
|
|
17
|
+
def initialize(aggregation_cardinality_limit: nil)
|
|
18
|
+
@metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new(cardinality_limit: aggregation_cardinality_limit)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def collect
|
|
@@ -24,8 +24,9 @@ module OpenTelemetry
|
|
|
24
24
|
# @return a new instance of the {PeriodicMetricReader}.
|
|
25
25
|
def initialize(export_interval_millis: Float(ENV.fetch('OTEL_METRIC_EXPORT_INTERVAL', 60_000)),
|
|
26
26
|
export_timeout_millis: Float(ENV.fetch('OTEL_METRIC_EXPORT_TIMEOUT', 30_000)),
|
|
27
|
-
exporter: nil
|
|
28
|
-
|
|
27
|
+
exporter: nil,
|
|
28
|
+
aggregation_cardinality_limit: nil)
|
|
29
|
+
super(aggregation_cardinality_limit: aggregation_cardinality_limit)
|
|
29
30
|
|
|
30
31
|
@export_interval = export_interval_millis / 1000.0
|
|
31
32
|
@export_timeout = export_timeout_millis / 1000.0
|
|
@@ -51,17 +51,22 @@ module OpenTelemetry
|
|
|
51
51
|
|
|
52
52
|
def invoke_callback(timeout, attributes)
|
|
53
53
|
if @registered_views.empty?
|
|
54
|
+
|
|
55
|
+
resolved_cardinality_limit = @cardinality_limit || DEFAULT_CARDINALITY_LIMIT
|
|
54
56
|
@mutex.synchronize do
|
|
55
57
|
@callback.each do |cb|
|
|
56
58
|
value = safe_guard_callback(cb, timeout: timeout)
|
|
59
|
+
|
|
57
60
|
if value.is_a?(Numeric)
|
|
58
61
|
exemplar_offer = should_offer_exemplar?(value, attributes)
|
|
59
|
-
@default_aggregation.update(value, attributes, @data_points, exemplar_offer: exemplar_offer)
|
|
62
|
+
@default_aggregation.update(value, attributes, @data_points, resolved_cardinality_limit, exemplar_offer: exemplar_offer)
|
|
60
63
|
end
|
|
61
64
|
end
|
|
62
65
|
end
|
|
63
66
|
else
|
|
64
67
|
@registered_views.each do |view, data_points|
|
|
68
|
+
resolved_cardinality_limit = resolve_cardinality_limit(view)
|
|
69
|
+
|
|
65
70
|
@mutex.synchronize do
|
|
66
71
|
@callback.each do |cb|
|
|
67
72
|
value = safe_guard_callback(cb, timeout: timeout)
|
|
@@ -69,9 +74,10 @@ module OpenTelemetry
|
|
|
69
74
|
|
|
70
75
|
merged_attributes = attributes || {}
|
|
71
76
|
merged_attributes.merge!(view.attribute_keys)
|
|
77
|
+
|
|
72
78
|
if view.valid_aggregation?
|
|
73
79
|
exemplar_offer = should_offer_exemplar?(value, merged_attributes)
|
|
74
|
-
view.aggregation.update(value, attributes, data_points, exemplar_offer: exemplar_offer)
|
|
80
|
+
view.aggregation.update(value, attributes, data_points, resolved_cardinality_limit, exemplar_offer: exemplar_offer)
|
|
75
81
|
end
|
|
76
82
|
end
|
|
77
83
|
end
|
|
@@ -13,11 +13,12 @@ module OpenTelemetry
|
|
|
13
13
|
# The MetricStore module provides SDK internal functionality that is not a part of the
|
|
14
14
|
# public API.
|
|
15
15
|
class MetricStore
|
|
16
|
-
def initialize
|
|
16
|
+
def initialize(cardinality_limit: nil)
|
|
17
17
|
@mutex = Mutex.new
|
|
18
18
|
@epoch_start_time = OpenTelemetry::Common::Utilities.time_in_nanoseconds
|
|
19
19
|
@epoch_end_time = nil
|
|
20
20
|
@metric_streams = []
|
|
21
|
+
@cardinality_limit = cardinality_limit
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def collect
|
|
@@ -32,6 +33,7 @@ module OpenTelemetry
|
|
|
32
33
|
|
|
33
34
|
def add_metric_stream(metric_stream)
|
|
34
35
|
@mutex.synchronize do
|
|
36
|
+
metric_stream.cardinality_limit = @cardinality_limit
|
|
35
37
|
@metric_streams = @metric_streams.dup.push(metric_stream)
|
|
36
38
|
nil
|
|
37
39
|
end
|
|
@@ -12,9 +12,13 @@ module OpenTelemetry
|
|
|
12
12
|
#
|
|
13
13
|
# The MetricStream class provides SDK internal functionality that is not a part of the
|
|
14
14
|
# public API.
|
|
15
|
+
#
|
|
15
16
|
# rubocop:disable Metrics/ClassLength
|
|
16
17
|
class MetricStream
|
|
17
18
|
attr_reader :name, :description, :unit, :instrument_kind, :instrumentation_scope, :data_points
|
|
19
|
+
attr_writer :cardinality_limit
|
|
20
|
+
|
|
21
|
+
DEFAULT_CARDINALITY_LIMIT = 2000
|
|
18
22
|
|
|
19
23
|
def initialize(
|
|
20
24
|
name,
|
|
@@ -51,10 +55,14 @@ module OpenTelemetry
|
|
|
51
55
|
return metric_data if empty_data_point?
|
|
52
56
|
|
|
53
57
|
if @registered_views.empty?
|
|
54
|
-
metric_data << aggregate_metric_data(start_time,
|
|
58
|
+
metric_data << aggregate_metric_data(start_time,
|
|
59
|
+
end_time)
|
|
55
60
|
else
|
|
56
61
|
@registered_views.each do |view, data_points|
|
|
57
|
-
metric_data << aggregate_metric_data(start_time,
|
|
62
|
+
metric_data << aggregate_metric_data(start_time,
|
|
63
|
+
end_time,
|
|
64
|
+
aggregation: view.aggregation,
|
|
65
|
+
data_points: data_points)
|
|
58
66
|
end
|
|
59
67
|
end
|
|
60
68
|
|
|
@@ -62,20 +70,29 @@ module OpenTelemetry
|
|
|
62
70
|
end
|
|
63
71
|
end
|
|
64
72
|
|
|
73
|
+
# view has the cardinality, pass to aggregation update
|
|
74
|
+
# to determine if aggregation have the cardinality
|
|
75
|
+
# if the aggregation does not have the cardinality, then it will be default 2000
|
|
76
|
+
# it better to move overflowed data_points during update because if do it in collect,
|
|
77
|
+
# then we need to sort the entire data_points (~ 2000) based on time, which is time-consuming
|
|
78
|
+
# view will modify the data_point that is not suitable when there are multiple views
|
|
65
79
|
def update(value, attributes)
|
|
66
80
|
if @registered_views.empty?
|
|
81
|
+
resolved_cardinality_limit = resolve_cardinality_limit(nil)
|
|
67
82
|
@mutex.synchronize do
|
|
68
83
|
exemplar_offer = should_offer_exemplar?(value, attributes)
|
|
69
|
-
@default_aggregation.update(value, attributes, @data_points, exemplar_offer: exemplar_offer)
|
|
84
|
+
@default_aggregation.update(value, attributes, @data_points, resolved_cardinality_limit, exemplar_offer: exemplar_offer)
|
|
70
85
|
end
|
|
71
86
|
else
|
|
72
87
|
@registered_views.each do |view, data_points|
|
|
88
|
+
resolved_cardinality_limit = resolve_cardinality_limit(view)
|
|
73
89
|
@mutex.synchronize do
|
|
74
90
|
attributes ||= {}
|
|
75
91
|
attributes.merge!(view.attribute_keys)
|
|
92
|
+
|
|
76
93
|
if view.valid_aggregation?
|
|
77
94
|
exemplar_offer = should_offer_exemplar?(value, attributes)
|
|
78
|
-
view.aggregation.update(value, attributes, data_points, exemplar_offer: exemplar_offer)
|
|
95
|
+
view.aggregation.update(value, attributes, data_points, resolved_cardinality_limit, exemplar_offer: exemplar_offer)
|
|
79
96
|
end
|
|
80
97
|
end
|
|
81
98
|
end
|
|
@@ -119,6 +136,11 @@ module OpenTelemetry
|
|
|
119
136
|
end
|
|
120
137
|
end
|
|
121
138
|
|
|
139
|
+
def resolve_cardinality_limit(view)
|
|
140
|
+
cardinality_limit = view&.aggregation_cardinality_limit || @cardinality_limit || DEFAULT_CARDINALITY_LIMIT
|
|
141
|
+
[cardinality_limit, 0].max # if cardinality_limit is negative, then give it 0
|
|
142
|
+
end
|
|
143
|
+
|
|
122
144
|
def should_offer_exemplar?(value, attributes)
|
|
123
145
|
return false if @exemplar_reservoir&.noop?
|
|
124
146
|
|
|
@@ -10,13 +10,14 @@ module OpenTelemetry
|
|
|
10
10
|
module View
|
|
11
11
|
# RegisteredView is an internal class used to match Views with a given {MetricStream}
|
|
12
12
|
class RegisteredView
|
|
13
|
-
attr_reader :name, :aggregation, :attribute_keys, :regex
|
|
13
|
+
attr_reader :name, :aggregation, :attribute_keys, :regex, :aggregation_cardinality_limit
|
|
14
14
|
|
|
15
15
|
def initialize(name, **options)
|
|
16
16
|
@name = name
|
|
17
17
|
@options = options
|
|
18
18
|
@aggregation = options[:aggregation]
|
|
19
19
|
@attribute_keys = options[:attribute_keys] || {}
|
|
20
|
+
@aggregation_cardinality_limit = options[:aggregation_cardinality_limit]
|
|
20
21
|
|
|
21
22
|
generate_regex_pattern(name)
|
|
22
23
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: opentelemetry-metrics-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenTelemetry Authors
|
|
@@ -51,104 +51,6 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '1.2'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: minitest
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - "~>"
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '5.0'
|
|
61
|
-
type: :development
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - "~>"
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '5.0'
|
|
68
|
-
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: opentelemetry-test-helpers
|
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - ">="
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0'
|
|
75
|
-
type: :development
|
|
76
|
-
prerelease: false
|
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - ">="
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: '0'
|
|
82
|
-
- !ruby/object:Gem::Dependency
|
|
83
|
-
name: rake
|
|
84
|
-
requirement: !ruby/object:Gem::Requirement
|
|
85
|
-
requirements:
|
|
86
|
-
- - "~>"
|
|
87
|
-
- !ruby/object:Gem::Version
|
|
88
|
-
version: '13.3'
|
|
89
|
-
type: :development
|
|
90
|
-
prerelease: false
|
|
91
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
-
requirements:
|
|
93
|
-
- - "~>"
|
|
94
|
-
- !ruby/object:Gem::Version
|
|
95
|
-
version: '13.3'
|
|
96
|
-
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: rubocop
|
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - "~>"
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: '1.65'
|
|
103
|
-
type: :development
|
|
104
|
-
prerelease: false
|
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - "~>"
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: '1.65'
|
|
110
|
-
- !ruby/object:Gem::Dependency
|
|
111
|
-
name: simplecov
|
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
|
113
|
-
requirements:
|
|
114
|
-
- - "~>"
|
|
115
|
-
- !ruby/object:Gem::Version
|
|
116
|
-
version: '0.17'
|
|
117
|
-
type: :development
|
|
118
|
-
prerelease: false
|
|
119
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
-
requirements:
|
|
121
|
-
- - "~>"
|
|
122
|
-
- !ruby/object:Gem::Version
|
|
123
|
-
version: '0.17'
|
|
124
|
-
- !ruby/object:Gem::Dependency
|
|
125
|
-
name: yard
|
|
126
|
-
requirement: !ruby/object:Gem::Requirement
|
|
127
|
-
requirements:
|
|
128
|
-
- - "~>"
|
|
129
|
-
- !ruby/object:Gem::Version
|
|
130
|
-
version: '0.9'
|
|
131
|
-
type: :development
|
|
132
|
-
prerelease: false
|
|
133
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
-
requirements:
|
|
135
|
-
- - "~>"
|
|
136
|
-
- !ruby/object:Gem::Version
|
|
137
|
-
version: '0.9'
|
|
138
|
-
- !ruby/object:Gem::Dependency
|
|
139
|
-
name: yard-doctest
|
|
140
|
-
requirement: !ruby/object:Gem::Requirement
|
|
141
|
-
requirements:
|
|
142
|
-
- - "~>"
|
|
143
|
-
- !ruby/object:Gem::Version
|
|
144
|
-
version: 0.1.6
|
|
145
|
-
type: :development
|
|
146
|
-
prerelease: false
|
|
147
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
-
requirements:
|
|
149
|
-
- - "~>"
|
|
150
|
-
- !ruby/object:Gem::Version
|
|
151
|
-
version: 0.1.6
|
|
152
54
|
description: A stats collection and distributed tracing framework
|
|
153
55
|
email:
|
|
154
56
|
- cncf-opentelemetry-contributors@lists.cncf.io
|
|
@@ -219,10 +121,10 @@ homepage: https://github.com/open-telemetry/opentelemetry-ruby
|
|
|
219
121
|
licenses:
|
|
220
122
|
- Apache-2.0
|
|
221
123
|
metadata:
|
|
222
|
-
changelog_uri: https://
|
|
223
|
-
source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby/tree/opentelemetry-metrics-sdk/v0.
|
|
124
|
+
changelog_uri: https://rubydoc.info/gems/opentelemetry-metrics-sdk/0.15.0/file/CHANGELOG.md
|
|
125
|
+
source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby/tree/opentelemetry-metrics-sdk/v0.15.0/metrics_sdk
|
|
224
126
|
bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby/issues
|
|
225
|
-
documentation_uri: https://
|
|
127
|
+
documentation_uri: https://rubydoc.info/gems/opentelemetry-metrics-sdk/0.15.0
|
|
226
128
|
rdoc_options: []
|
|
227
129
|
require_paths:
|
|
228
130
|
- lib
|
|
@@ -237,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
237
139
|
- !ruby/object:Gem::Version
|
|
238
140
|
version: '0'
|
|
239
141
|
requirements: []
|
|
240
|
-
rubygems_version: 4.0.
|
|
142
|
+
rubygems_version: 4.0.10
|
|
241
143
|
specification_version: 4
|
|
242
144
|
summary: A stats collection and distributed tracing framework
|
|
243
145
|
test_files: []
|