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.
- checksums.yaml +4 -4
- data/AGENTS.md +8 -1
- data/CHANGELOG.md +56 -0
- data/README.md +15 -8
- data/lib/fast/prometheus/counter.rb +1 -1
- data/lib/fast/prometheus/formats/metrics_proto.rb +95 -0
- data/lib/fast/prometheus/formats/protobuf.rb +209 -89
- data/lib/fast/prometheus/formats/text.rb +48 -38
- data/lib/fast/prometheus/gauge.rb +4 -4
- data/lib/fast/prometheus/histogram.rb +14 -14
- data/lib/fast/prometheus/metric.rb +29 -12
- data/lib/fast/prometheus/native_histogram.rb +8 -15
- data/lib/fast/prometheus/otlp/mapper.rb +59 -61
- data/lib/fast/prometheus/otlp/proto.rb +169 -0
- data/lib/fast/prometheus/otlp/service_interface.rb +3 -3
- data/lib/fast/prometheus/snapshot.rb +29 -27
- data/lib/fast/prometheus/store.rb +4 -2
- data/lib/fast/prometheus/summary.rb +3 -3
- data/lib/fast/prometheus/version.rb +1 -1
- metadata +7 -10
- data/lib/fast/prometheus/formats/metrics_pb.rb +0 -33
- data/lib/fast/prometheus/otlp/pb/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.rb +0 -27
- data/lib/fast/prometheus/otlp/pb/opentelemetry/proto/common/v1/common_pb.rb +0 -26
- data/lib/fast/prometheus/otlp/pb/opentelemetry/proto/metrics/v1/metrics_pb.rb +0 -41
- data/lib/fast/prometheus/otlp/pb/opentelemetry/proto/resource/v1/resource_pb.rb +0 -23
|
@@ -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
|
-
|
|
24
|
-
output << "#
|
|
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.
|
|
27
|
-
|
|
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,
|
|
38
|
+
private_class_method def self.render_series(output, type, name, names, values, value)
|
|
33
39
|
case type
|
|
34
40
|
when :counter, :gauge
|
|
35
|
-
|
|
41
|
+
sample(output, name, nil, names, values, value)
|
|
36
42
|
when :histogram
|
|
37
|
-
histogram_lines(output, name,
|
|
43
|
+
histogram_lines(output, name, names, values, value)
|
|
38
44
|
when :summary
|
|
39
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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.
|
|
50
|
-
|
|
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
|
-
|
|
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.
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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.
|
|
74
|
-
|
|
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
|
-
|
|
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.
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
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.
|
|
33
|
+
result = Array.new(@buckets.size + 1)
|
|
34
|
+
@buckets.each_index do |i|
|
|
32
35
|
cumulative += @cells[i]
|
|
33
|
-
[
|
|
36
|
+
result[i] = [@buckets[i], cumulative].freeze
|
|
34
37
|
end
|
|
35
38
|
cumulative += @cells.last # overflow cell
|
|
36
|
-
result
|
|
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
|
-
|
|
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
|
|
77
|
-
#
|
|
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
|
-
|
|
84
|
+
store.synchronize { store.to_h.transform_values! { |slot| snapshot_value(slot) } }
|
|
80
85
|
end
|
|
81
86
|
|
|
82
|
-
# Runs
|
|
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
|
|
87
|
-
store.synchronize
|
|
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
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
9
|
+
# Maps a Snapshot to a Proto::ExportMetricsServiceRequest.
|
|
13
10
|
class Mapper
|
|
14
|
-
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
|
-
|
|
20
|
+
Proto::ExportMetricsServiceRequest.new(
|
|
24
21
|
resource_metrics: [
|
|
25
|
-
|
|
26
|
-
resource:
|
|
22
|
+
Proto::ResourceMetrics.new(
|
|
23
|
+
resource: Proto::Resource.new(
|
|
27
24
|
attributes: @resource_attributes
|
|
28
25
|
),
|
|
29
26
|
scope_metrics: [
|
|
30
|
-
|
|
31
|
-
scope:
|
|
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
|
-
|
|
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:
|
|
60
|
-
data_points: metric_snapshot
|
|
61
|
-
number_data_point(
|
|
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:
|
|
70
|
-
data_points: metric_snapshot
|
|
71
|
-
number_data_point(
|
|
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:
|
|
78
|
-
data_points: metric_snapshot
|
|
79
|
-
histogram_data_point(
|
|
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:
|
|
87
|
-
data_points: metric_snapshot
|
|
88
|
-
summary_data_point(
|
|
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:
|
|
95
|
-
data_points: metric_snapshot
|
|
96
|
-
exponential_histogram_data_point(
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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:
|
|
112
|
+
as_double: value.to_f
|
|
110
113
|
)
|
|
111
114
|
end
|
|
112
115
|
|
|
113
|
-
def histogram_data_point(
|
|
114
|
-
|
|
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
|
-
|
|
118
|
-
attributes:
|
|
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:
|
|
122
|
-
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(
|
|
146
|
-
|
|
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:
|
|
153
|
-
sum:
|
|
152
|
+
count: value.count,
|
|
153
|
+
sum: value.sum
|
|
154
154
|
)
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
-
def exponential_histogram_data_point(
|
|
158
|
-
|
|
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:
|
|
165
|
-
sum:
|
|
166
|
-
scale:
|
|
167
|
-
zero_count:
|
|
168
|
-
zero_threshold:
|
|
169
|
-
positive: dense_buckets(
|
|
170
|
-
negative: dense_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
|
|
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
|
-
|
|
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
|
-
|
|
197
|
+
Proto::KeyValue.new(
|
|
200
198
|
key: key,
|
|
201
|
-
value:
|
|
199
|
+
value: Proto::AnyValue.new(string_value: value)
|
|
202
200
|
)
|
|
203
201
|
end
|
|
204
202
|
end
|