fast-prometheus 0.2.0 → 0.3.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.
@@ -7,6 +7,10 @@ module Fast
7
7
  module Formats
8
8
  # Prometheus text exposition format 0.0.4.
9
9
  # Consumes Snapshot value objects; never reads live metrics.
10
+ #
11
+ # Every piece is appended straight to the output buffer: no line, label
12
+ # or name is built as its own String first, so a render allocates one
13
+ # String per sample (the value's to_s) and nothing per label.
10
14
  module Text
11
15
  CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8"
12
16
 
@@ -20,69 +24,75 @@ module Fast
20
24
  snapshot.metrics.each_with_object(String.new) do |metric, output|
21
25
  next if metric.type == :native_histogram
22
26
 
23
- output << "# HELP #{metric.name} #{escape_doc(metric.docstring)}\n"
24
- output << "# TYPE #{metric.name} #{metric.type}\n"
27
+ name = metric.name.name
28
+ output << "# HELP " << name << " " << escape_doc(metric.docstring) << "\n"
29
+ output << "# TYPE " << name << " " << metric.type.name << "\n"
25
30
 
26
- metric.series.each do |series|
27
- render_series(output, metric.type, metric.name, series)
31
+ names = metric.label_names
32
+ metric.series.each_pair do |values, value|
33
+ render_series(output, metric.type, name, names, values, value)
28
34
  end
29
35
  end
30
36
  end
31
37
 
32
- private_class_method def self.render_series(output, type, name, series)
38
+ private_class_method def self.render_series(output, type, name, names, values, value)
33
39
  case type
34
40
  when :counter, :gauge
35
- metric_line(output, name, series.labels, series.value)
41
+ sample(output, name, nil, names, values, value)
36
42
  when :histogram
37
- histogram_lines(output, name, series.labels, series.value)
43
+ histogram_lines(output, name, names, values, value)
38
44
  when :summary
39
- summary_lines(output, name, series.labels, series.value)
45
+ sample(output, name, "_sum", names, values, value.sum)
46
+ sample(output, name, "_count", names, values, value.count)
40
47
  end
41
48
  end
42
49
 
43
- private_class_method def self.escape_doc(string)
44
- return string unless DOC_ESCAPE.match?(string)
45
-
46
- string.gsub(DOC_ESCAPE, DOC_REPLACE)
50
+ # One sample line: name, optional suffix, labels (names and values
51
+ # side by side, plus an le label carrying +upper_bound+ for histogram
52
+ # buckets), value.
53
+ private_class_method def self.sample(output, name, suffix, names, values, value, upper_bound = nil)
54
+ output << name
55
+ output << suffix if suffix
56
+ append_labels(output, names, values, upper_bound)
57
+ output << " " << value.to_s << "\n"
47
58
  end
48
59
 
49
- private_class_method def self.escape_label(string)
50
- string = string.to_s
51
- return string unless LABEL_ESCAPE.match?(string)
60
+ private_class_method def self.append_labels(output, names, values, upper_bound)
61
+ return if names.empty? && upper_bound.nil?
52
62
 
53
- string.gsub(LABEL_ESCAPE, LABEL_REPLACE)
63
+ output << "{"
64
+ names.each_index { |i| output << label_name(names[i]) << "=\"" << escape_label(values[i]) << "\"," }
65
+ output << "le=\"" << upper_bound << "\"," if upper_bound
66
+ output.chop!
67
+ output << "}"
54
68
  end
55
69
 
56
- private_class_method def self.format_labels(labels)
57
- return "" if labels.empty?
58
-
59
- output = String.new("{")
60
- labels.each do |key, value|
61
- output << "#{key}=\"#{escape_label(value)}\""
62
- output << ","
70
+ private_class_method def self.histogram_lines(output, name, names, values, value)
71
+ value.cumulative_buckets.each do |boundary, count|
72
+ sample(output, name, "_bucket", names, values, count, boundary.infinite? ? "+Inf" : boundary.to_s)
63
73
  end
64
- output.chop!
65
- output << "}"
66
- output
74
+
75
+ sample(output, name, "_sum", names, values, value.sum)
76
+ sample(output, name, "_count", names, values, value.count)
67
77
  end
68
78
 
69
- private_class_method def self.metric_line(output, name, labels, value)
70
- output << "#{name}#{format_labels(labels)} #{value}\n"
79
+ # Label names are Symbols (Symbol#name is frozen and shared); a
80
+ # String name is appended as is.
81
+ private_class_method def self.label_name(key)
82
+ key.is_a?(Symbol) ? key.name : key
71
83
  end
72
84
 
73
- private_class_method def self.histogram_lines(output, name, labels, value)
74
- value.cumulative_buckets.each do |boundary, count|
75
- le = boundary.infinite? ? "+Inf" : boundary.to_s
76
- metric_line(output, "#{name}_bucket", labels.merge("le" => le), count)
77
- end
85
+ private_class_method def self.escape_doc(string)
86
+ return string unless DOC_ESCAPE.match?(string)
78
87
 
79
- metric_line(output, "#{name}_sum", labels, value.sum)
80
- metric_line(output, "#{name}_count", labels, value.count)
88
+ string.gsub(DOC_ESCAPE, DOC_REPLACE)
81
89
  end
82
90
 
83
- private_class_method def self.summary_lines(output, name, labels, value)
84
- metric_line(output, "#{name}_sum", labels, value.sum)
85
- metric_line(output, "#{name}_count", labels, value.count)
91
+ private_class_method def self.escape_label(string)
92
+ string = string.to_s
93
+ return string unless LABEL_ESCAPE.match?(string)
94
+
95
+ string.gsub(LABEL_ESCAPE, LABEL_REPLACE)
86
96
  end
87
97
  end
88
98
  end
@@ -10,18 +10,18 @@ module Fast
10
10
  :gauge
11
11
  end
12
12
 
13
- def set(value, labels: {})
13
+ def set(value, labels: NO_LABELS)
14
14
  raise ArgumentError, "value must be a numeric" unless value.is_a?(Numeric)
15
15
 
16
16
  key = resolve(labels)
17
17
  store.synchronize { store[key] = value.to_f }
18
18
  end
19
19
 
20
- def set_to_current_time(labels: {})
20
+ def set_to_current_time(labels: NO_LABELS)
21
21
  set(Time.now.to_f, labels: labels)
22
22
  end
23
23
 
24
- def increment(by: 1, labels: {})
24
+ def increment(by: 1, labels: NO_LABELS)
25
25
  raise ArgumentError, "by must be a numeric" unless by.is_a?(Numeric)
26
26
  return if by.zero?
27
27
 
@@ -29,7 +29,7 @@ module Fast
29
29
  store.synchronize { store[key] = (store[key] || 0.0) + by.to_f }
30
30
  end
31
31
 
32
- def decrement(by: 1, labels: {})
32
+ def decrement(by: 1, labels: NO_LABELS)
33
33
  raise ArgumentError, "by must be a numeric" unless by.is_a?(Numeric)
34
34
  return if by.zero?
35
35
 
@@ -25,15 +25,19 @@ module Fast
25
25
  @cells = [0] * (buckets.size + 1)
26
26
  end
27
27
 
28
- # Cumulative bucket counts, ending with [Float::INFINITY, count].
28
+ # Cumulative bucket counts as frozen [boundary, count] pairs, ending
29
+ # with [Float::INFINITY, count]. Frozen on the way out so a snapshot
30
+ # can hold it as is.
29
31
  def cumulative_buckets
30
32
  cumulative = 0
31
- result = @buckets.each_with_index.map do |boundary, i|
33
+ result = Array.new(@buckets.size + 1)
34
+ @buckets.each_index do |i|
32
35
  cumulative += @cells[i]
33
- [boundary, cumulative]
36
+ result[i] = [@buckets[i], cumulative].freeze
34
37
  end
35
38
  cumulative += @cells.last # overflow cell
36
- result << [Float::INFINITY, cumulative]
39
+ result[@buckets.size] = [Float::INFINITY, cumulative].freeze
40
+ result.freeze
37
41
  end
38
42
  end
39
43
 
@@ -56,7 +60,7 @@ module Fast
56
60
 
57
61
  # Record an observation. Finds the first boundary >= value (inclusive le)
58
62
  # using bsearch_index. Values above the last boundary go to the overflow cell.
59
- def observe(value, labels: {})
63
+ def observe(value, labels: NO_LABELS)
60
64
  key = resolve(labels)
61
65
  index = @buckets.bsearch_index { |boundary| boundary >= value } || @buckets.size
62
66
 
@@ -71,7 +75,7 @@ module Fast
71
75
 
72
76
  # Cumulative bucket counts for a specific series, ending with
73
77
  # [Float::INFINITY, count]. Zero-valued pairs for an unobserved series.
74
- def cumulative_buckets(labels: {})
78
+ def cumulative_buckets(labels: NO_LABELS)
75
79
  key = resolve(labels)
76
80
  store.synchronize { slot_or_zero(key).cumulative_buckets }
77
81
  end
@@ -79,7 +83,7 @@ module Fast
79
83
  # Prometheus-client-shaped Hash: each bucket boundary's to_s => cumulative
80
84
  # count, then "+Inf" => cumulative count, then "sum" => Float sum. A
81
85
  # fresh Hash per call; zero-valued for an unobserved series.
82
- def get(labels: {})
86
+ def get(labels: NO_LABELS)
83
87
  key = resolve(labels)
84
88
  store.synchronize { hash_shape(slot_or_zero(key)) }
85
89
  end
@@ -89,13 +93,13 @@ module Fast
89
93
  end
90
94
 
91
95
  # Reader for sum of a specific series; 0.0 when unobserved.
92
- def sum(labels: {})
96
+ def sum(labels: NO_LABELS)
93
97
  key = resolve(labels)
94
98
  store.synchronize { slot_or_zero(key).sum }
95
99
  end
96
100
 
97
101
  # Reader for count of a specific series; 0 when unobserved.
98
- def count(labels: {})
102
+ def count(labels: NO_LABELS)
99
103
  key = resolve(labels)
100
104
  store.synchronize { slot_or_zero(key).count }
101
105
  end
@@ -129,11 +133,7 @@ module Fast
129
133
  end
130
134
 
131
135
  def snapshot_value(slot)
132
- HistogramValue.new(
133
- sum: slot.sum,
134
- count: slot.count,
135
- cumulative_buckets: slot.cumulative_buckets.map { |pair| pair.dup.freeze }.freeze
136
- )
136
+ HistogramValue.new(slot.sum, slot.count, slot.cumulative_buckets).freeze
137
137
  end
138
138
 
139
139
  def hash_shape(slot)
@@ -15,6 +15,9 @@ module Fast
15
15
  METRIC_NAME = /\A[a-zA-Z_:][a-zA-Z0-9_:]*\z/
16
16
  LABEL_NAME = /\A[a-zA-Z_][a-zA-Z0-9_]*\z/
17
17
 
18
+ # The default label set, shared so an unlabeled call allocates nothing.
19
+ NO_LABELS = {}.freeze
20
+
18
21
  def initialize(name, docstring:, labels: [], preset_labels: {}, store: nil)
19
22
  validate_metric_name(name)
20
23
  validate_docstring(docstring)
@@ -38,7 +41,7 @@ module Fast
38
41
  # Returns the current value for the given label set. Scalar types
39
42
  # (Counter, Gauge) return 0.0 for unobserved series. Histogram, Summary,
40
43
  # and NativeHistogram override this with their own zero-valued shapes.
41
- def get(labels: {})
44
+ def get(labels: NO_LABELS)
42
45
  key = resolve(labels)
43
46
  store.synchronize { store[key] || 0.0 }
44
47
  end
@@ -46,7 +49,7 @@ module Fast
46
49
  # Creates the series for +labels+ at its zero value if absent. Never
47
50
  # resets a series that already exists. Raises InvalidLabelSet on an
48
51
  # unknown or incomplete label set, same as any mutator.
49
- def init_label_set(labels = {})
52
+ def init_label_set(labels = NO_LABELS)
50
53
  seed(resolve(labels))
51
54
  end
52
55
 
@@ -68,23 +71,25 @@ module Fast
68
71
  end
69
72
 
70
73
  def values
71
- snapshot_values
74
+ series_map { |slot| snapshot_value(slot) }
72
75
  end
73
76
 
74
77
  # Every series' value in its frozen snapshot shape (see HistogramValue,
75
78
  # SummaryValue, NativeHistogramValue; Counter/Gauge use the already-
76
- # frozen Float), keyed by label hash, built under the store lock. The
77
- # seam MetricSnapshot uses to build a consistent view of every series.
79
+ # frozen Float), keyed by the series' label values in #labels order,
80
+ # the store's own key. The store is copied and its slots frozen under
81
+ # the lock, so this is a consistent view of every series at one
82
+ # instant that costs one Hash. The seam MetricSnapshot uses.
78
83
  def snapshot_values
79
- series_map { |slot| snapshot_value(slot) }
84
+ store.synchronize { store.to_h.transform_values! { |slot| snapshot_value(slot) } }
80
85
  end
81
86
 
82
- # Runs +block+ exclusively with respect to every other mutation or read
87
+ # Runs the block exclusively with respect to every other mutation or read
83
88
  # of this metric's store (including on metrics sharing it via
84
89
  # #with_labels). Reentrant on the same thread. This is the seam
85
90
  # MetricSnapshot uses to build a consistent snapshot of every series.
86
- def synchronize(&block)
87
- store.synchronize(&block)
91
+ def synchronize
92
+ store.synchronize { yield } # rubocop:disable Style/ExplicitBlockArgument
88
93
  end
89
94
 
90
95
  protected
@@ -131,9 +136,21 @@ module Fast
131
136
  end
132
137
 
133
138
  # Shared shape behind #values and #snapshot_values: every series'
134
- # storage transformed by +block+, keyed by label hash, under the lock.
135
- def series_map(&block)
136
- store.synchronize { store.to_h.transform_keys { |key| @labels.zip(key).to_h }.transform_values(&block) }
139
+ # storage transformed by the block, keyed by a fresh frozen label hash,
140
+ # under the lock. yield, not &block: a captured block is a Proc
141
+ # allocation on every call.
142
+ def series_map
143
+ store.synchronize do
144
+ store.to_h.transform_keys { |key| label_hash(key) }.transform_values { |slot| yield slot } # rubocop:disable Style/ExplicitBlockArgument
145
+ end
146
+ end
147
+
148
+ # {name => value} for a resolved key, built without the per-pair Arrays
149
+ # Array#zip would make (or the iterator object each_with_index would).
150
+ def label_hash(key)
151
+ hash = {}
152
+ @labels.each_index { |i| hash[@labels[i]] = key[i] }
153
+ hash.freeze
137
154
  end
138
155
 
139
156
  # Creates the series at +key+ at its zero value if absent. Never resets
@@ -25,14 +25,14 @@ module Fast
25
25
  @negative = {}
26
26
  end
27
27
 
28
- # Sorted array of [index, count] pairs for positive side.
28
+ # Sorted frozen [index, count] pairs for the positive side.
29
29
  def positive_buckets
30
- @positive.sort
30
+ @positive.sort.each(&:freeze).freeze
31
31
  end
32
32
 
33
- # Sorted array of [index, count] pairs for negative side.
33
+ # Sorted frozen [index, count] pairs for the negative side.
34
34
  def negative_buckets
35
- @negative.sort
35
+ @negative.sort.each(&:freeze).freeze
36
36
  end
37
37
  end
38
38
 
@@ -64,7 +64,7 @@ module Fast
64
64
  end
65
65
 
66
66
  # Record an observation. Never raises — NaN/±Inf are handled gracefully.
67
- def observe(value, labels: {})
67
+ def observe(value, labels: NO_LABELS)
68
68
  v = value.to_f
69
69
  key = resolve(labels)
70
70
 
@@ -109,7 +109,7 @@ module Fast
109
109
 
110
110
  # A frozen NativeHistogramValue for a label set; zero-valued at this
111
111
  # metric's schema/zero_threshold for an unobserved series.
112
- def get(labels: {})
112
+ def get(labels: NO_LABELS)
113
113
  key = resolve(labels)
114
114
  store.synchronize { snapshot_value(store[key] || zero_value) }
115
115
  end
@@ -131,15 +131,8 @@ module Fast
131
131
  end
132
132
 
133
133
  def snapshot_value(slot)
134
- NativeHistogramValue.new(
135
- schema: slot.schema,
136
- zero_threshold: slot.zero_threshold,
137
- zero_count: slot.zero_count,
138
- sum: slot.sum,
139
- count: slot.count,
140
- positive_buckets: slot.positive_buckets.map { |pair| pair.dup.freeze }.freeze,
141
- negative_buckets: slot.negative_buckets.map { |pair| pair.dup.freeze }.freeze
142
- ).freeze
134
+ NativeHistogramValue.new(slot.schema, slot.zero_threshold, slot.zero_count, slot.sum, slot.count,
135
+ slot.positive_buckets, slot.negative_buckets).freeze
143
136
  end
144
137
 
145
138
  def index_for(value, schema)
@@ -1,17 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fast/prometheus"
4
-
5
- $LOAD_PATH.unshift(File.expand_path("pb", __dir__)) unless $LOAD_PATH.include?(File.expand_path("pb", __dir__))
6
-
7
- require "opentelemetry/proto/collector/metrics/v1/metrics_service_pb"
4
+ require_relative "proto"
8
5
 
9
6
  module Fast
10
7
  module Prometheus
11
8
  module OTLP
12
- # Maps a Snapshot to an ExportMetricsServiceRequest.
9
+ # Maps a Snapshot to a Proto::ExportMetricsServiceRequest.
13
10
  class Mapper
14
- CUMULATIVE = Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_CUMULATIVE
11
+ CUMULATIVE = :AGGREGATION_TEMPORALITY_CUMULATIVE
15
12
  private_constant :CUMULATIVE
16
13
 
17
14
  def initialize(resource_attributes: {}, start_time: Time.now)
@@ -20,15 +17,15 @@ module Fast
20
17
  end
21
18
 
22
19
  def request(snapshot)
23
- Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.new(
20
+ Proto::ExportMetricsServiceRequest.new(
24
21
  resource_metrics: [
25
- Opentelemetry::Proto::Metrics::V1::ResourceMetrics.new(
26
- resource: Opentelemetry::Proto::Resource::V1::Resource.new(
22
+ Proto::ResourceMetrics.new(
23
+ resource: Proto::Resource.new(
27
24
  attributes: @resource_attributes
28
25
  ),
29
26
  scope_metrics: [
30
- Opentelemetry::Proto::Metrics::V1::ScopeMetrics.new(
31
- scope: Opentelemetry::Proto::Common::V1::InstrumentationScope.new(
27
+ Proto::ScopeMetrics.new(
28
+ scope: Proto::InstrumentationScope.new(
32
29
  name: "fast-prometheus",
33
30
  version: Fast::Prometheus::VERSION
34
31
  ),
@@ -45,7 +42,7 @@ module Fast
45
42
  def build_metric(metric_snapshot, taken_at)
46
43
  time_unix_nano = (taken_at.to_f * 1_000_000_000).to_i
47
44
 
48
- Opentelemetry::Proto::Metrics::V1::Metric.new(
45
+ Proto::Metric.new(
49
46
  name: metric_snapshot.name.to_s,
50
47
  description: metric_snapshot.docstring,
51
48
  **build_data(metric_snapshot, time_unix_nano)
@@ -56,9 +53,9 @@ module Fast
56
53
  case metric_snapshot.type
57
54
  when :counter
58
55
  {
59
- sum: Opentelemetry::Proto::Metrics::V1::Sum.new(
60
- data_points: metric_snapshot.series.map do |series|
61
- number_data_point(series, time_unix_nano)
56
+ sum: Proto::Sum.new(
57
+ data_points: data_points(metric_snapshot) do |attributes, value|
58
+ number_data_point(attributes, value, time_unix_nano)
62
59
  end,
63
60
  aggregation_temporality: CUMULATIVE,
64
61
  is_monotonic: true
@@ -66,34 +63,34 @@ module Fast
66
63
  }
67
64
  when :gauge
68
65
  {
69
- gauge: Opentelemetry::Proto::Metrics::V1::Gauge.new(
70
- data_points: metric_snapshot.series.map do |series|
71
- number_data_point(series, time_unix_nano)
66
+ gauge: Proto::Gauge.new(
67
+ data_points: data_points(metric_snapshot) do |attributes, value|
68
+ number_data_point(attributes, value, time_unix_nano)
72
69
  end
73
70
  )
74
71
  }
75
72
  when :histogram
76
73
  {
77
- histogram: Opentelemetry::Proto::Metrics::V1::Histogram.new(
78
- data_points: metric_snapshot.series.map do |series|
79
- histogram_data_point(series, time_unix_nano)
74
+ histogram: Proto::Histogram.new(
75
+ data_points: data_points(metric_snapshot) do |attributes, value|
76
+ histogram_data_point(attributes, value, time_unix_nano)
80
77
  end,
81
78
  aggregation_temporality: CUMULATIVE
82
79
  )
83
80
  }
84
81
  when :summary
85
82
  {
86
- summary: Opentelemetry::Proto::Metrics::V1::Summary.new(
87
- data_points: metric_snapshot.series.map do |series|
88
- summary_data_point(series, time_unix_nano)
83
+ summary: Proto::Summary.new(
84
+ data_points: data_points(metric_snapshot) do |attributes, value|
85
+ summary_data_point(attributes, value, time_unix_nano)
89
86
  end
90
87
  )
91
88
  }
92
89
  when :native_histogram
93
90
  {
94
- exponential_histogram: Opentelemetry::Proto::Metrics::V1::ExponentialHistogram.new(
95
- data_points: metric_snapshot.series.map do |series|
96
- exponential_histogram_data_point(series, time_unix_nano)
91
+ exponential_histogram: Proto::ExponentialHistogram.new(
92
+ data_points: data_points(metric_snapshot) do |attributes, value|
93
+ exponential_histogram_data_point(attributes, value, time_unix_nano)
97
94
  end,
98
95
  aggregation_temporality: CUMULATIVE
99
96
  )
@@ -101,25 +98,30 @@ module Fast
101
98
  end
102
99
  end
103
100
 
104
- def number_data_point(series, time_unix_nano)
105
- Opentelemetry::Proto::Metrics::V1::NumberDataPoint.new(
106
- attributes: build_attributes(series.labels),
101
+ # One data point per series: the block gets the series' OTLP
102
+ # attributes and its snapshot value.
103
+ def data_points(metric_snapshot)
104
+ metric_snapshot.series.map { |values, value| yield build_attributes(metric_snapshot.labels(values)), value }
105
+ end
106
+
107
+ def number_data_point(attributes, value, time_unix_nano)
108
+ Proto::NumberDataPoint.new(
109
+ attributes: attributes,
107
110
  start_time_unix_nano: @start_time_unix_nano,
108
111
  time_unix_nano: time_unix_nano,
109
- as_double: series.value.to_f
112
+ as_double: value.to_f
110
113
  )
111
114
  end
112
115
 
113
- def histogram_data_point(series, time_unix_nano)
114
- nv = series.value
115
- bounds, counts = non_cumulative_buckets(nv.cumulative_buckets)
116
+ def histogram_data_point(attributes, value, time_unix_nano)
117
+ bounds, counts = non_cumulative_buckets(value.cumulative_buckets)
116
118
 
117
- Opentelemetry::Proto::Metrics::V1::HistogramDataPoint.new(
118
- attributes: build_attributes(series.labels),
119
+ Proto::HistogramDataPoint.new(
120
+ attributes: attributes,
119
121
  start_time_unix_nano: @start_time_unix_nano,
120
122
  time_unix_nano: time_unix_nano,
121
- count: nv.count,
122
- sum: nv.sum,
123
+ count: value.count,
124
+ sum: value.sum,
123
125
  bucket_counts: counts,
124
126
  explicit_bounds: bounds
125
127
  )
@@ -142,32 +144,28 @@ module Fast
142
144
  [bounds, counts]
143
145
  end
144
146
 
145
- def summary_data_point(series, time_unix_nano)
146
- sv = series.value
147
-
148
- Opentelemetry::Proto::Metrics::V1::SummaryDataPoint.new(
149
- attributes: build_attributes(series.labels),
147
+ def summary_data_point(attributes, value, time_unix_nano)
148
+ Proto::SummaryDataPoint.new(
149
+ attributes: attributes,
150
150
  start_time_unix_nano: @start_time_unix_nano,
151
151
  time_unix_nano: time_unix_nano,
152
- count: sv.count,
153
- sum: sv.sum
152
+ count: value.count,
153
+ sum: value.sum
154
154
  )
155
155
  end
156
156
 
157
- def exponential_histogram_data_point(series, time_unix_nano)
158
- nv = series.value
159
-
160
- Opentelemetry::Proto::Metrics::V1::ExponentialHistogramDataPoint.new(
161
- attributes: build_attributes(series.labels),
157
+ def exponential_histogram_data_point(attributes, value, time_unix_nano)
158
+ Proto::ExponentialHistogramDataPoint.new(
159
+ attributes: attributes,
162
160
  start_time_unix_nano: @start_time_unix_nano,
163
161
  time_unix_nano: time_unix_nano,
164
- count: nv.count,
165
- sum: nv.sum,
166
- scale: nv.schema,
167
- zero_count: nv.zero_count,
168
- zero_threshold: nv.zero_threshold,
169
- positive: dense_buckets(nv.positive_buckets),
170
- negative: dense_buckets(nv.negative_buckets)
162
+ count: value.count,
163
+ sum: value.sum,
164
+ scale: value.schema,
165
+ zero_count: value.zero_count,
166
+ zero_threshold: value.zero_threshold,
167
+ positive: dense_buckets(value.positive_buckets),
168
+ negative: dense_buckets(value.negative_buckets)
171
169
  )
172
170
  end
173
171
 
@@ -175,7 +173,7 @@ module Fast
175
173
  # OTLP index = prom index - 1. Offset = min OTLP index.
176
174
  # Buckets are always sorted (from NativeHistogram::Slot#positive_buckets / #negative_buckets).
177
175
  def dense_buckets(buckets)
178
- return Opentelemetry::Proto::Metrics::V1::ExponentialHistogramDataPoint::Buckets.new if buckets.empty?
176
+ return Proto::ExponentialHistogramDataPoint::Buckets.new if buckets.empty?
179
177
 
180
178
  first_idx, = buckets.first
181
179
  last_idx, = buckets.last
@@ -185,7 +183,7 @@ module Fast
185
183
  counts = Array.new(length, 0)
186
184
  buckets.each { |prom_idx, count| counts[prom_idx - offset - 1] = count }
187
185
 
188
- Opentelemetry::Proto::Metrics::V1::ExponentialHistogramDataPoint::Buckets.new(
186
+ Proto::ExponentialHistogramDataPoint::Buckets.new(
189
187
  offset: offset,
190
188
  bucket_counts: counts
191
189
  )
@@ -196,9 +194,9 @@ module Fast
196
194
  end
197
195
 
198
196
  def kv(key, value)
199
- Opentelemetry::Proto::Common::V1::KeyValue.new(
197
+ Proto::KeyValue.new(
200
198
  key: key,
201
- value: Opentelemetry::Proto::Common::V1::AnyValue.new(string_value: value)
199
+ value: Proto::AnyValue.new(string_value: value)
202
200
  )
203
201
  end
204
202
  end