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