11//! Protobuf serialization support for Prometheus metrics.
22
3- use indexmap:: IndexMap ;
43use metrics:: Unit ;
54use prost:: Message ;
65use std:: collections:: HashMap ;
76
8- use crate :: common:: Snapshot ;
7+ use crate :: common:: { LabelSet , Snapshot } ;
98use crate :: distribution:: Distribution ;
109use crate :: formatting:: sanitize_metric_name;
1110
@@ -26,28 +25,27 @@ pub(crate) const PROTOBUF_CONTENT_TYPE: &str =
2625/// length header.
2726#[ allow( clippy:: too_many_lines) ]
2827pub ( crate ) fn render_protobuf (
29- snapshot : & Snapshot ,
28+ snapshot : Snapshot ,
3029 descriptions : & HashMap < String , ( metrics:: SharedString , Option < Unit > ) > ,
31- global_labels : & IndexMap < String , String > ,
3230 counter_suffix : Option < & ' static str > ,
3331) -> Vec < u8 > {
3432 let mut output = Vec :: new ( ) ;
3533
3634 // 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) ;
3937 let help =
4038 descriptions. get ( name. as_str ( ) ) . map ( |( desc, _) | desc. to_string ( ) ) . unwrap_or_default ( ) ;
4139
4240 let mut metrics = Vec :: new ( ) ;
4341 for ( labels, value) in by_labels {
44- let label_pairs = parse_labels ( labels, global_labels ) ;
42+ let label_pairs = label_set_to_protobuf ( labels) ;
4543
4644 metrics. push ( pb:: Metric {
4745 label : label_pairs,
4846 counter : Some ( pb:: Counter {
4947 #[ allow( clippy:: cast_precision_loss) ]
50- value : Some ( * value as f64 ) ,
48+ value : Some ( value as f64 ) ,
5149
5250 ..Default :: default ( )
5351 } ) ,
@@ -68,18 +66,18 @@ pub(crate) fn render_protobuf(
6866 }
6967
7068 // 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) ;
7371 let help =
7472 descriptions. get ( name. as_str ( ) ) . map ( |( desc, _) | desc. to_string ( ) ) . unwrap_or_default ( ) ;
7573
7674 let mut metrics = Vec :: new ( ) ;
7775 for ( labels, value) in by_labels {
78- let label_pairs = parse_labels ( labels, global_labels ) ;
76+ let label_pairs = label_set_to_protobuf ( labels) ;
7977
8078 metrics. push ( pb:: Metric {
8179 label : label_pairs,
82- gauge : Some ( pb:: Gauge { value : Some ( * value) } ) ,
80+ gauge : Some ( pb:: Gauge { value : Some ( value) } ) ,
8381
8482 ..Default :: default ( )
8583 } ) ;
@@ -97,18 +95,20 @@ pub(crate) fn render_protobuf(
9795 }
9896
9997 // 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) ;
102100 let help =
103101 descriptions. get ( name. as_str ( ) ) . map ( |( desc, _) | desc. to_string ( ) ) . unwrap_or_default ( ) ;
104102
105103 let mut metrics = Vec :: new ( ) ;
104+ let mut metric_type = None ;
106105 for ( labels, distribution) in by_labels {
107- let label_pairs = parse_labels ( labels, global_labels ) ;
106+ let label_pairs = label_set_to_protobuf ( labels) ;
108107
109108 let metric = match distribution {
110109 Distribution :: Summary ( summary, quantiles, sum) => {
111110 use quanta:: Instant ;
111+ metric_type = Some ( pb:: MetricType :: Summary ) ;
112112 let snapshot = summary. snapshot ( Instant :: now ( ) ) ;
113113 let quantile_values: Vec < pb:: Quantile > = quantiles
114114 . iter ( )
@@ -122,7 +122,7 @@ pub(crate) fn render_protobuf(
122122 label : label_pairs,
123123 summary : Some ( pb:: Summary {
124124 sample_count : Some ( summary. count ( ) as u64 ) ,
125- sample_sum : Some ( * sum) ,
125+ sample_sum : Some ( sum) ,
126126 quantile : quantile_values,
127127
128128 created_timestamp : None ,
@@ -132,6 +132,7 @@ pub(crate) fn render_protobuf(
132132 }
133133 }
134134 Distribution :: Histogram ( histogram) => {
135+ metric_type = Some ( pb:: MetricType :: Histogram ) ;
135136 let mut buckets = Vec :: new ( ) ;
136137 for ( le, count) in histogram. buckets ( ) {
137138 buckets. push ( pb:: Bucket {
@@ -167,10 +168,9 @@ pub(crate) fn render_protobuf(
167168 metrics. push ( metric) ;
168169 }
169170
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 ;
174174 } ;
175175
176176 let metric_family = pb:: MetricFamily {
@@ -187,29 +187,11 @@ pub(crate) fn render_protobuf(
187187 output
188188}
189189
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 > {
191191 let mut label_pairs = Vec :: new ( ) ;
192192
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) } ) ;
213195 }
214196
215197 label_pairs
@@ -235,16 +217,18 @@ mod tests {
235217 fn test_render_protobuf_counters ( ) {
236218 let mut counters = HashMap :: new ( ) ;
237219 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 ) ;
239225 counters. insert ( "http_requests" . to_string ( ) , counter_labels) ;
240226
241227 let snapshot = Snapshot { counters, gauges : HashMap :: new ( ) , distributions : HashMap :: new ( ) } ;
242228
243229 let descriptions = HashMap :: new ( ) ;
244- let global_labels = IndexMap :: new ( ) ;
245230
246- let protobuf_data =
247- render_protobuf ( & snapshot, & descriptions, & global_labels, Some ( "total" ) ) ;
231+ let protobuf_data = render_protobuf ( snapshot, & descriptions, Some ( "total" ) ) ;
248232
249233 assert ! ( !protobuf_data. is_empty( ) , "Protobuf data should not be empty" ) ;
250234
@@ -264,7 +248,11 @@ mod tests {
264248 fn test_render_protobuf_gauges ( ) {
265249 let mut gauges = HashMap :: new ( ) ;
266250 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 ) ;
268256 gauges. insert ( "cpu_usage" . to_string ( ) , gauge_labels) ;
269257
270258 let snapshot = Snapshot { counters : HashMap :: new ( ) , gauges, distributions : HashMap :: new ( ) } ;
@@ -274,9 +262,8 @@ mod tests {
274262 "cpu_usage" . to_string ( ) ,
275263 ( SharedString :: const_str ( "CPU usage percentage" ) , None ) ,
276264 ) ;
277- let global_labels = IndexMap :: new ( ) ;
278265
279- let protobuf_data = render_protobuf ( & snapshot, & descriptions, & global_labels , None ) ;
266+ let protobuf_data = render_protobuf ( snapshot, & descriptions, None ) ;
280267
281268 assert ! ( !protobuf_data. is_empty( ) , "Protobuf data should not be empty" ) ;
282269
0 commit comments