1
1
//! Protobuf serialization support for Prometheus metrics.
2
2
3
- use indexmap:: IndexMap ;
4
3
use metrics:: Unit ;
5
4
use prost:: Message ;
6
5
use std:: collections:: HashMap ;
7
6
8
- use crate :: common:: Snapshot ;
7
+ use crate :: common:: { LabelSet , Snapshot } ;
9
8
use crate :: distribution:: Distribution ;
10
9
use crate :: formatting:: sanitize_metric_name;
11
10
@@ -26,28 +25,27 @@ pub(crate) const PROTOBUF_CONTENT_TYPE: &str =
26
25
/// length header.
27
26
#[ allow( clippy:: too_many_lines) ]
28
27
pub ( crate ) fn render_protobuf (
29
- snapshot : & Snapshot ,
28
+ snapshot : Snapshot ,
30
29
descriptions : & HashMap < String , ( metrics:: SharedString , Option < Unit > ) > ,
31
- global_labels : & IndexMap < String , String > ,
32
30
counter_suffix : Option < & ' static str > ,
33
31
) -> Vec < u8 > {
34
32
let mut output = Vec :: new ( ) ;
35
33
36
34
// Process counters
37
- for ( name, by_labels) in & snapshot. counters {
38
- let sanitized_name = sanitize_metric_name ( name) ;
35
+ for ( name, by_labels) in snapshot. counters {
36
+ let sanitized_name = sanitize_metric_name ( & name) ;
39
37
let help =
40
38
descriptions. get ( name. as_str ( ) ) . map ( |( desc, _) | desc. to_string ( ) ) . unwrap_or_default ( ) ;
41
39
42
40
let mut metrics = Vec :: new ( ) ;
43
41
for ( labels, value) in by_labels {
44
- let label_pairs = parse_labels ( labels, global_labels ) ;
42
+ let label_pairs = label_set_to_protobuf ( labels) ;
45
43
46
44
metrics. push ( pb:: Metric {
47
45
label : label_pairs,
48
46
counter : Some ( pb:: Counter {
49
47
#[ allow( clippy:: cast_precision_loss) ]
50
- value : Some ( * value as f64 ) ,
48
+ value : Some ( value as f64 ) ,
51
49
52
50
..Default :: default ( )
53
51
} ) ,
@@ -68,18 +66,18 @@ pub(crate) fn render_protobuf(
68
66
}
69
67
70
68
// Process gauges
71
- for ( name, by_labels) in & snapshot. gauges {
72
- let sanitized_name = sanitize_metric_name ( name) ;
69
+ for ( name, by_labels) in snapshot. gauges {
70
+ let sanitized_name = sanitize_metric_name ( & name) ;
73
71
let help =
74
72
descriptions. get ( name. as_str ( ) ) . map ( |( desc, _) | desc. to_string ( ) ) . unwrap_or_default ( ) ;
75
73
76
74
let mut metrics = Vec :: new ( ) ;
77
75
for ( labels, value) in by_labels {
78
- let label_pairs = parse_labels ( labels, global_labels ) ;
76
+ let label_pairs = label_set_to_protobuf ( labels) ;
79
77
80
78
metrics. push ( pb:: Metric {
81
79
label : label_pairs,
82
- gauge : Some ( pb:: Gauge { value : Some ( * value) } ) ,
80
+ gauge : Some ( pb:: Gauge { value : Some ( value) } ) ,
83
81
84
82
..Default :: default ( )
85
83
} ) ;
@@ -97,18 +95,20 @@ pub(crate) fn render_protobuf(
97
95
}
98
96
99
97
// Process distributions (histograms and summaries)
100
- for ( name, by_labels) in & snapshot. distributions {
101
- let sanitized_name = sanitize_metric_name ( name) ;
98
+ for ( name, by_labels) in snapshot. distributions {
99
+ let sanitized_name = sanitize_metric_name ( & name) ;
102
100
let help =
103
101
descriptions. get ( name. as_str ( ) ) . map ( |( desc, _) | desc. to_string ( ) ) . unwrap_or_default ( ) ;
104
102
105
103
let mut metrics = Vec :: new ( ) ;
104
+ let mut metric_type = None ;
106
105
for ( labels, distribution) in by_labels {
107
- let label_pairs = parse_labels ( labels, global_labels ) ;
106
+ let label_pairs = label_set_to_protobuf ( labels) ;
108
107
109
108
let metric = match distribution {
110
109
Distribution :: Summary ( summary, quantiles, sum) => {
111
110
use quanta:: Instant ;
111
+ metric_type = Some ( pb:: MetricType :: Summary ) ;
112
112
let snapshot = summary. snapshot ( Instant :: now ( ) ) ;
113
113
let quantile_values: Vec < pb:: Quantile > = quantiles
114
114
. iter ( )
@@ -122,7 +122,7 @@ pub(crate) fn render_protobuf(
122
122
label : label_pairs,
123
123
summary : Some ( pb:: Summary {
124
124
sample_count : Some ( summary. count ( ) as u64 ) ,
125
- sample_sum : Some ( * sum) ,
125
+ sample_sum : Some ( sum) ,
126
126
quantile : quantile_values,
127
127
128
128
created_timestamp : None ,
@@ -132,6 +132,7 @@ pub(crate) fn render_protobuf(
132
132
}
133
133
}
134
134
Distribution :: Histogram ( histogram) => {
135
+ metric_type = Some ( pb:: MetricType :: Histogram ) ;
135
136
let mut buckets = Vec :: new ( ) ;
136
137
for ( le, count) in histogram. buckets ( ) {
137
138
buckets. push ( pb:: Bucket {
@@ -167,10 +168,9 @@ pub(crate) fn render_protobuf(
167
168
metrics. push ( metric) ;
168
169
}
169
170
170
- let metric_type = match by_labels. values ( ) . next ( ) {
171
- Some ( Distribution :: Summary ( _, _, _) ) => pb:: MetricType :: Summary ,
172
- Some ( Distribution :: Histogram ( _) ) => pb:: MetricType :: Histogram ,
173
- None => continue , // Skip empty metric families
171
+ let Some ( metric_type) = metric_type else {
172
+ // Skip empty metric families
173
+ continue ;
174
174
} ;
175
175
176
176
let metric_family = pb:: MetricFamily {
@@ -187,29 +187,11 @@ pub(crate) fn render_protobuf(
187
187
output
188
188
}
189
189
190
- fn parse_labels ( labels : & [ String ] , global_labels : & IndexMap < String , String > ) -> Vec < pb:: LabelPair > {
190
+ fn label_set_to_protobuf ( labels : LabelSet ) -> Vec < pb:: LabelPair > {
191
191
let mut label_pairs = Vec :: new ( ) ;
192
192
193
- // Add global labels first
194
- for ( key, value) in global_labels {
195
- label_pairs. push ( pb:: LabelPair { name : Some ( key. clone ( ) ) , value : Some ( value. clone ( ) ) } ) ;
196
- }
197
-
198
- // Add metric-specific labels
199
- for label_str in labels {
200
- if let Some ( eq_pos) = label_str. find ( '=' ) {
201
- let key = & label_str[ ..eq_pos] ;
202
- let value = & label_str[ eq_pos + 1 ..] ;
203
- let value = value. trim_matches ( '"' ) ;
204
-
205
- // Skip if this label key already exists from global labels
206
- if !global_labels. contains_key ( key) {
207
- label_pairs. push ( pb:: LabelPair {
208
- name : Some ( key. to_string ( ) ) ,
209
- value : Some ( value. to_string ( ) ) ,
210
- } ) ;
211
- }
212
- }
193
+ for ( key, value) in labels. labels {
194
+ label_pairs. push ( pb:: LabelPair { name : Some ( key) , value : Some ( value) } ) ;
213
195
}
214
196
215
197
label_pairs
@@ -235,16 +217,18 @@ mod tests {
235
217
fn test_render_protobuf_counters ( ) {
236
218
let mut counters = HashMap :: new ( ) ;
237
219
let mut counter_labels = HashMap :: new ( ) ;
238
- counter_labels. insert ( vec ! [ "method=\" GET\" " . to_string( ) ] , 42u64 ) ;
220
+ let labels = LabelSet :: from_key_and_global (
221
+ & metrics:: Key :: from_parts ( "" , vec ! [ metrics:: Label :: new( "method" , "GET" ) ] ) ,
222
+ & IndexMap :: new ( ) ,
223
+ ) ;
224
+ counter_labels. insert ( labels, 42u64 ) ;
239
225
counters. insert ( "http_requests" . to_string ( ) , counter_labels) ;
240
226
241
227
let snapshot = Snapshot { counters, gauges : HashMap :: new ( ) , distributions : HashMap :: new ( ) } ;
242
228
243
229
let descriptions = HashMap :: new ( ) ;
244
- let global_labels = IndexMap :: new ( ) ;
245
230
246
- let protobuf_data =
247
- render_protobuf ( & snapshot, & descriptions, & global_labels, Some ( "total" ) ) ;
231
+ let protobuf_data = render_protobuf ( snapshot, & descriptions, Some ( "total" ) ) ;
248
232
249
233
assert ! ( !protobuf_data. is_empty( ) , "Protobuf data should not be empty" ) ;
250
234
@@ -264,7 +248,11 @@ mod tests {
264
248
fn test_render_protobuf_gauges ( ) {
265
249
let mut gauges = HashMap :: new ( ) ;
266
250
let mut gauge_labels = HashMap :: new ( ) ;
267
- gauge_labels. insert ( vec ! [ "instance=\" localhost\" " . to_string( ) ] , 0.75f64 ) ;
251
+ let labels = LabelSet :: from_key_and_global (
252
+ & metrics:: Key :: from_parts ( "" , vec ! [ metrics:: Label :: new( "instance" , "localhost" ) ] ) ,
253
+ & IndexMap :: new ( ) ,
254
+ ) ;
255
+ gauge_labels. insert ( labels, 0.75f64 ) ;
268
256
gauges. insert ( "cpu_usage" . to_string ( ) , gauge_labels) ;
269
257
270
258
let snapshot = Snapshot { counters : HashMap :: new ( ) , gauges, distributions : HashMap :: new ( ) } ;
@@ -274,9 +262,8 @@ mod tests {
274
262
"cpu_usage" . to_string ( ) ,
275
263
( SharedString :: const_str ( "CPU usage percentage" ) , None ) ,
276
264
) ;
277
- let global_labels = IndexMap :: new ( ) ;
278
265
279
- let protobuf_data = render_protobuf ( & snapshot, & descriptions, & global_labels , None ) ;
266
+ let protobuf_data = render_protobuf ( snapshot, & descriptions, None ) ;
280
267
281
268
assert ! ( !protobuf_data. is_empty( ) , "Protobuf data should not be empty" ) ;
282
269
0 commit comments