fast-prometheus 0.1.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 +108 -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 +7 -3
- data/lib/fast/prometheus/histogram.rb +53 -17
- data/lib/fast/prometheus/metric.rb +76 -16
- data/lib/fast/prometheus/native_histogram.rb +22 -8
- 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/registry.rb +21 -9
- data/lib/fast/prometheus/snapshot.rb +29 -55
- data/lib/fast/prometheus/store.rb +4 -2
- data/lib/fast/prometheus/summary.rb +22 -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
|
@@ -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
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fast/protowire"
|
|
4
|
+
|
|
5
|
+
module Fast
|
|
6
|
+
module Prometheus
|
|
7
|
+
module OTLP
|
|
8
|
+
# The OTLP metrics data model (proto/opentelemetry, proto3), declared
|
|
9
|
+
# for the wire: what the exporters send and the one response they read.
|
|
10
|
+
module Proto
|
|
11
|
+
class AnyValue < Fast::Protowire::Message
|
|
12
|
+
oneof :value do
|
|
13
|
+
field :string_value, :string, 1
|
|
14
|
+
field :bool_value, :bool, 2
|
|
15
|
+
field :int_value, :int64, 3
|
|
16
|
+
field :double_value, :double, 4
|
|
17
|
+
field :bytes_value, :bytes, 7
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class KeyValue < Fast::Protowire::Message
|
|
22
|
+
field :key, :string, 1
|
|
23
|
+
field :value, AnyValue, 2
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class InstrumentationScope < Fast::Protowire::Message
|
|
27
|
+
field :name, :string, 1
|
|
28
|
+
field :version, :string, 2
|
|
29
|
+
repeated :attributes, KeyValue, 3
|
|
30
|
+
field :dropped_attributes_count, :uint32, 4
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Resource < Fast::Protowire::Message
|
|
34
|
+
repeated :attributes, KeyValue, 1
|
|
35
|
+
field :dropped_attributes_count, :uint32, 2
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
AggregationTemporality = Fast::Protowire::Enum.define(
|
|
39
|
+
AGGREGATION_TEMPORALITY_UNSPECIFIED: 0,
|
|
40
|
+
AGGREGATION_TEMPORALITY_DELTA: 1,
|
|
41
|
+
AGGREGATION_TEMPORALITY_CUMULATIVE: 2
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
class NumberDataPoint < Fast::Protowire::Message
|
|
45
|
+
field :start_time_unix_nano, :fixed64, 2
|
|
46
|
+
field :time_unix_nano, :fixed64, 3
|
|
47
|
+
oneof :value do
|
|
48
|
+
field :as_double, :double, 4
|
|
49
|
+
field :as_int, :sfixed64, 6
|
|
50
|
+
end
|
|
51
|
+
repeated :attributes, KeyValue, 7
|
|
52
|
+
field :flags, :uint32, 8
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class HistogramDataPoint < Fast::Protowire::Message
|
|
56
|
+
field :start_time_unix_nano, :fixed64, 2
|
|
57
|
+
field :time_unix_nano, :fixed64, 3
|
|
58
|
+
field :count, :fixed64, 4
|
|
59
|
+
optional :sum, :double, 5
|
|
60
|
+
repeated :bucket_counts, :fixed64, 6
|
|
61
|
+
repeated :explicit_bounds, :double, 7
|
|
62
|
+
repeated :attributes, KeyValue, 9
|
|
63
|
+
field :flags, :uint32, 10
|
|
64
|
+
optional :min, :double, 11
|
|
65
|
+
optional :max, :double, 12
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class SummaryDataPoint < Fast::Protowire::Message
|
|
69
|
+
class ValueAtQuantile < Fast::Protowire::Message
|
|
70
|
+
field :quantile, :double, 1
|
|
71
|
+
field :value, :double, 2
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
field :start_time_unix_nano, :fixed64, 2
|
|
75
|
+
field :time_unix_nano, :fixed64, 3
|
|
76
|
+
field :count, :fixed64, 4
|
|
77
|
+
field :sum, :double, 5
|
|
78
|
+
repeated :quantile_values, ValueAtQuantile, 6
|
|
79
|
+
repeated :attributes, KeyValue, 7
|
|
80
|
+
field :flags, :uint32, 8
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class ExponentialHistogramDataPoint < Fast::Protowire::Message
|
|
84
|
+
class Buckets < Fast::Protowire::Message
|
|
85
|
+
field :offset, :sint32, 1
|
|
86
|
+
repeated :bucket_counts, :uint64, 2
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
repeated :attributes, KeyValue, 1
|
|
90
|
+
field :start_time_unix_nano, :fixed64, 2
|
|
91
|
+
field :time_unix_nano, :fixed64, 3
|
|
92
|
+
field :count, :fixed64, 4
|
|
93
|
+
optional :sum, :double, 5
|
|
94
|
+
field :scale, :sint32, 6
|
|
95
|
+
field :zero_count, :fixed64, 7
|
|
96
|
+
field :positive, Buckets, 8
|
|
97
|
+
field :negative, Buckets, 9
|
|
98
|
+
field :flags, :uint32, 10
|
|
99
|
+
optional :min, :double, 12
|
|
100
|
+
optional :max, :double, 13
|
|
101
|
+
field :zero_threshold, :double, 14
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class Gauge < Fast::Protowire::Message
|
|
105
|
+
repeated :data_points, NumberDataPoint, 1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class Sum < Fast::Protowire::Message
|
|
109
|
+
repeated :data_points, NumberDataPoint, 1
|
|
110
|
+
field :aggregation_temporality, AggregationTemporality, 2
|
|
111
|
+
field :is_monotonic, :bool, 3
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Histogram < Fast::Protowire::Message
|
|
115
|
+
repeated :data_points, HistogramDataPoint, 1
|
|
116
|
+
field :aggregation_temporality, AggregationTemporality, 2
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class ExponentialHistogram < Fast::Protowire::Message
|
|
120
|
+
repeated :data_points, ExponentialHistogramDataPoint, 1
|
|
121
|
+
field :aggregation_temporality, AggregationTemporality, 2
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class Summary < Fast::Protowire::Message
|
|
125
|
+
repeated :data_points, SummaryDataPoint, 1
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class Metric < Fast::Protowire::Message
|
|
129
|
+
field :name, :string, 1
|
|
130
|
+
field :description, :string, 2
|
|
131
|
+
field :unit, :string, 3
|
|
132
|
+
oneof :data do
|
|
133
|
+
field :gauge, Gauge, 5
|
|
134
|
+
field :sum, Sum, 7
|
|
135
|
+
field :histogram, Histogram, 9
|
|
136
|
+
field :exponential_histogram, ExponentialHistogram, 10
|
|
137
|
+
field :summary, Summary, 11
|
|
138
|
+
end
|
|
139
|
+
repeated :metadata, KeyValue, 12
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class ScopeMetrics < Fast::Protowire::Message
|
|
143
|
+
field :scope, InstrumentationScope, 1
|
|
144
|
+
repeated :metrics, Metric, 2
|
|
145
|
+
field :schema_url, :string, 3
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class ResourceMetrics < Fast::Protowire::Message
|
|
149
|
+
field :resource, Resource, 1
|
|
150
|
+
repeated :scope_metrics, ScopeMetrics, 2
|
|
151
|
+
field :schema_url, :string, 3
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
class ExportMetricsServiceRequest < Fast::Protowire::Message
|
|
155
|
+
repeated :resource_metrics, ResourceMetrics, 1
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class ExportMetricsPartialSuccess < Fast::Protowire::Message
|
|
159
|
+
field :rejected_data_points, :int64, 1
|
|
160
|
+
field :error_message, :string, 2
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class ExportMetricsServiceResponse < Fast::Protowire::Message
|
|
164
|
+
field :partial_success, ExportMetricsPartialSuccess, 1
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "fast/prometheus"
|
|
4
4
|
require "protocol/grpc/interface"
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "proto"
|
|
6
6
|
|
|
7
7
|
module Fast
|
|
8
8
|
module Prometheus
|
|
@@ -10,8 +10,8 @@ module Fast
|
|
|
10
10
|
# Protocol::GRPC::Interface for opentelemetry.proto.collector.metrics.v1.MetricsService.
|
|
11
11
|
class MetricsServiceInterface < Protocol::GRPC::Interface
|
|
12
12
|
rpc :Export,
|
|
13
|
-
request_class:
|
|
14
|
-
response_class:
|
|
13
|
+
request_class: Proto::ExportMetricsServiceRequest,
|
|
14
|
+
response_class: Proto::ExportMetricsServiceResponse
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -28,14 +28,19 @@ module Fast
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
# Remove a metric by name.
|
|
31
|
+
# Remove a metric by name (String or Symbol).
|
|
32
32
|
def unregister(name)
|
|
33
|
-
@lock.synchronize { @by_name.delete(name) }
|
|
33
|
+
@lock.synchronize { @by_name.delete(normalize(name)) }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
# Look up a metric by name, or nil.
|
|
36
|
+
# Look up a metric by name (String or Symbol), or nil.
|
|
37
37
|
def get(name)
|
|
38
|
-
@lock.synchronize { @by_name[name] }
|
|
38
|
+
@lock.synchronize { @by_name[normalize(name)] }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# True iff a metric is registered under +name+ (String or Symbol).
|
|
42
|
+
def exist?(name)
|
|
43
|
+
@lock.synchronize { @by_name.key?(normalize(name)) }
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
# Atomic fetch-or-register: returns the metric already registered under
|
|
@@ -45,15 +50,16 @@ module Fast
|
|
|
45
50
|
# DuplicateMetric. Raises ArgumentError if the block's metric is not
|
|
46
51
|
# named +name+.
|
|
47
52
|
def fetch_or_register(name)
|
|
53
|
+
key = normalize(name)
|
|
48
54
|
@lock.synchronize do
|
|
49
|
-
next @by_name[
|
|
55
|
+
next @by_name[key] if @by_name.key?(key)
|
|
50
56
|
|
|
51
57
|
metric = yield
|
|
52
|
-
unless metric.name ==
|
|
53
|
-
raise ArgumentError, "fetch_or_register(#{
|
|
58
|
+
unless metric.name == key
|
|
59
|
+
raise ArgumentError, "fetch_or_register(#{key.inspect}) block built #{metric.name.inspect}"
|
|
54
60
|
end
|
|
55
61
|
|
|
56
|
-
@by_name[
|
|
62
|
+
@by_name[key] = metric
|
|
57
63
|
end
|
|
58
64
|
end
|
|
59
65
|
|
|
@@ -93,6 +99,12 @@ module Fast
|
|
|
93
99
|
def collect
|
|
94
100
|
Snapshot.of(metrics)
|
|
95
101
|
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def normalize(name)
|
|
106
|
+
name.to_s.to_sym
|
|
107
|
+
end
|
|
96
108
|
end
|
|
97
109
|
|
|
98
110
|
@registry_lock = Mutex.new
|
|
@@ -105,7 +117,7 @@ module Fast
|
|
|
105
117
|
end
|
|
106
118
|
|
|
107
119
|
def self.registry=(registry)
|
|
108
|
-
@registry = registry
|
|
120
|
+
@registry_lock.synchronize { @registry = registry }
|
|
109
121
|
end
|
|
110
122
|
end
|
|
111
123
|
end
|
|
@@ -2,66 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module Fast
|
|
4
4
|
module Prometheus
|
|
5
|
-
# Immutable value types for snapshots.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
# Immutable value types for snapshots.
|
|
6
|
+
#
|
|
7
|
+
# A MetricSnapshot's +series+ is the metric's store as it stood at one
|
|
8
|
+
# instant: a frozen Hash from each series' label values (an Array in
|
|
9
|
+
# +label_names+ order, the store's own key) to its value. Counter and
|
|
10
|
+
# gauge values are Floats; the other types' values are the frozen
|
|
11
|
+
# Structs below, built from the live slot while the store was locked.
|
|
12
|
+
# Nothing per series is copied beyond that, so a snapshot of any size
|
|
13
|
+
# costs one Hash per metric.
|
|
14
|
+
#
|
|
15
|
+
# The value shapes are Structs frozen where they are built, not Data:
|
|
16
|
+
# Data.new allocates two objects besides the instance, and a scrape
|
|
17
|
+
# builds one of these per histogram, summary or native histogram series.
|
|
18
|
+
# (sum and count shadow Enumerable's on the Structs, as they would on Data.)
|
|
19
|
+
|
|
20
|
+
HistogramValue = Struct.new(:sum, :count, :cumulative_buckets) # rubocop:disable Lint/StructNewOverride
|
|
21
|
+
|
|
22
|
+
NativeHistogramValue = Struct.new(:schema, :zero_threshold, :zero_count, :sum, :count, # rubocop:disable Lint/StructNewOverride
|
|
23
|
+
:positive_buckets, :negative_buckets)
|
|
24
|
+
|
|
25
|
+
SummaryValue = Struct.new(:sum, :count) # rubocop:disable Lint/StructNewOverride
|
|
15
26
|
|
|
16
27
|
MetricSnapshot = Data.define(:name, :docstring, :type, :label_names, :series) do
|
|
17
|
-
# Build a MetricSnapshot from a live metric.
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# interleaves between series.
|
|
28
|
+
# Build a MetricSnapshot from a live metric. +series+ is the metric's
|
|
29
|
+
# store copied and its slots frozen under the metric's lock (see
|
|
30
|
+
# Metric#snapshot_values), so no series is observed mid-mutation and
|
|
31
|
+
# no writer interleaves between series.
|
|
21
32
|
def self.of(metric)
|
|
22
|
-
metric.
|
|
23
|
-
new(
|
|
24
|
-
metric.name,
|
|
25
|
-
metric.docstring,
|
|
26
|
-
metric.type,
|
|
27
|
-
metric.label_names.dup.freeze,
|
|
28
|
-
build_series(metric).freeze
|
|
29
|
-
)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def self.build_series(metric)
|
|
34
|
-
metric.values.map do |labels, value|
|
|
35
|
-
Series.new(
|
|
36
|
-
labels: labels.dup.freeze,
|
|
37
|
-
value: build_value(metric.type, value)
|
|
38
|
-
)
|
|
39
|
-
end
|
|
33
|
+
new(metric.name, metric.docstring, metric.type, metric.labels.dup.freeze, metric.snapshot_values.freeze)
|
|
40
34
|
end
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
value
|
|
46
|
-
when :histogram
|
|
47
|
-
HistogramValue.new(
|
|
48
|
-
sum: value.sum,
|
|
49
|
-
count: value.count,
|
|
50
|
-
cumulative_buckets: value.cumulative_buckets.map { |pair| pair.dup.freeze }.freeze
|
|
51
|
-
)
|
|
52
|
-
when :summary
|
|
53
|
-
SummaryValue.new(sum: value.sum, count: value.count)
|
|
54
|
-
when :native_histogram
|
|
55
|
-
NativeHistogramValue.new(
|
|
56
|
-
schema: value.schema,
|
|
57
|
-
zero_threshold: value.zero_threshold,
|
|
58
|
-
zero_count: value.zero_count,
|
|
59
|
-
sum: value.sum,
|
|
60
|
-
count: value.count,
|
|
61
|
-
positive_buckets: value.positive_buckets.map { |pair| pair.dup.freeze }.freeze,
|
|
62
|
-
negative_buckets: value.negative_buckets.map { |pair| pair.dup.freeze }.freeze
|
|
63
|
-
)
|
|
64
|
-
end
|
|
36
|
+
# The {name => value} Hash for one series' label values.
|
|
37
|
+
def labels(values)
|
|
38
|
+
label_names.zip(values).to_h
|
|
65
39
|
end
|
|
66
40
|
end
|
|
67
41
|
|
|
@@ -17,8 +17,10 @@ module Fast
|
|
|
17
17
|
@monitor = Monitor.new
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
# Written with yield rather than a captured &block, so taking the lock
|
|
21
|
+
# allocates nothing.
|
|
22
|
+
def synchronize
|
|
23
|
+
@monitor.synchronize { yield } # rubocop:disable Style/ExplicitBlockArgument
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def [](key)
|
|
@@ -21,7 +21,7 @@ module Fast
|
|
|
21
21
|
:summary
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def observe(value, labels:
|
|
24
|
+
def observe(value, labels: NO_LABELS)
|
|
25
25
|
key = resolve(labels)
|
|
26
26
|
store.synchronize do
|
|
27
27
|
slot = store[key] ||= Value.new
|
|
@@ -30,9 +30,14 @@ module Fast
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
# {"count" => Integer, "sum" => Float}. Zero-valued for an unobserved series.
|
|
34
|
+
def get(labels: NO_LABELS)
|
|
34
35
|
key = resolve(labels)
|
|
35
|
-
store.synchronize { store[key] }
|
|
36
|
+
store.synchronize { hash_shape(store[key] || zero_value) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def values
|
|
40
|
+
series_map { |slot| hash_shape(slot) }
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
protected
|
|
@@ -45,6 +50,20 @@ module Fast
|
|
|
45
50
|
|
|
46
51
|
super
|
|
47
52
|
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def zero_value
|
|
57
|
+
Value.new
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def snapshot_value(slot)
|
|
61
|
+
SummaryValue.new(slot.sum, slot.count).freeze
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def hash_shape(slot)
|
|
65
|
+
{ "count" => slot.count, "sum" => slot.sum }
|
|
66
|
+
end
|
|
48
67
|
end
|
|
49
68
|
end
|
|
50
69
|
end
|