@@ -29,13 +29,15 @@ pub fn key_to_parts(
2929/// Writes a help (description) line in the Prometheus [exposition format].
3030///
3131/// [exposition format]: https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-format-details
32- pub fn write_help_line ( buffer : & mut String , name : & str , unit : Option < Unit > , desc : & str ) {
32+ pub fn write_help_line (
33+ buffer : & mut String ,
34+ name : & str ,
35+ unit : Option < Unit > ,
36+ suffix : Option < & ' static str > ,
37+ desc : & str ,
38+ ) {
3339 buffer. push_str ( "# HELP " ) ;
34- buffer. push_str ( name) ;
35- if let Some ( unit) = unit {
36- buffer. push ( '_' ) ;
37- buffer. push_str ( unit. as_str ( ) ) ;
38- }
40+ add_metric_name ( buffer, name, unit, suffix) ;
3941 buffer. push ( ' ' ) ;
4042 let desc = sanitize_description ( desc) ;
4143 buffer. push_str ( & desc) ;
@@ -45,13 +47,15 @@ pub fn write_help_line(buffer: &mut String, name: &str, unit: Option<Unit>, desc
4547/// Writes a metric type line in the Prometheus [exposition format].
4648///
4749/// [exposition format]: https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-format-details
48- pub fn write_type_line ( buffer : & mut String , name : & str , unit : Option < Unit > , metric_type : & str ) {
50+ pub fn write_type_line (
51+ buffer : & mut String ,
52+ name : & str ,
53+ unit : Option < Unit > ,
54+ suffix : Option < & ' static str > ,
55+ metric_type : & str ,
56+ ) {
4957 buffer. push_str ( "# TYPE " ) ;
50- buffer. push_str ( name) ;
51- if let Some ( unit) = unit {
52- buffer. push ( '_' ) ;
53- buffer. push_str ( unit. as_str ( ) ) ;
54- }
58+ add_metric_name ( buffer, name, unit, suffix) ;
5559 buffer. push ( ' ' ) ;
5660 buffer. push_str ( metric_type) ;
5761 buffer. push ( '\n' ) ;
@@ -77,18 +81,7 @@ pub fn write_metric_line<T, T2>(
7781 T : std:: fmt:: Display ,
7882 T2 : std:: fmt:: Display ,
7983{
80- buffer. push_str ( name) ;
81-
82- match unit {
83- Some ( Unit :: Count ) | None => { }
84- Some ( Unit :: Percent ) => add_unit ( buffer, "ratio" ) ,
85- Some ( unit) => add_unit ( buffer, unit. as_str ( ) ) ,
86- }
87-
88- if let Some ( suffix) = suffix {
89- buffer. push ( '_' ) ;
90- buffer. push_str ( suffix) ;
91- }
84+ add_metric_name ( buffer, name, unit, suffix) ;
9285
9386 if !labels. is_empty ( ) || additional_label. is_some ( ) {
9487 buffer. push ( '{' ) ;
@@ -121,24 +114,58 @@ pub fn write_metric_line<T, T2>(
121114 buffer. push ( '\n' ) ;
122115}
123116
124- fn add_unit ( buffer : & mut String , unit : & str ) {
125- const SUM_SUFFIX : & str = "_sum" ;
126- const COUNT_SUFFIX : & str = "_count" ;
127- const BUCKET_SUFFIX : & str = "_bucket" ;
128-
129- if buffer. ends_with ( SUM_SUFFIX ) {
130- let suffix_pos = buffer. len ( ) - SUM_SUFFIX . len ( ) ;
131- buffer. insert ( suffix_pos, '_' ) ;
132- buffer. insert_str ( suffix_pos + 1 , unit) ;
133- } else if buffer. ends_with ( COUNT_SUFFIX ) {
134- let suffix_pos = buffer. len ( ) - COUNT_SUFFIX . len ( ) ;
135- buffer. insert ( suffix_pos, '_' ) ;
136- buffer. insert_str ( suffix_pos + 1 , unit) ;
137- } else if buffer. ends_with ( BUCKET_SUFFIX ) {
138- let suffix_pos = buffer. len ( ) - BUCKET_SUFFIX . len ( ) ;
139- buffer. insert ( suffix_pos, '_' ) ;
140- buffer. insert_str ( suffix_pos + 1 , unit) ;
141- } else {
117+ fn add_metric_name (
118+ buffer : & mut String ,
119+ name : & str ,
120+ unit : Option < Unit > ,
121+ suffix : Option < & ' static str > ,
122+ ) {
123+ buffer. push_str ( name) ;
124+ if let Some ( unit) = unit {
125+ add_unit_if_missing ( buffer, unit) ;
126+ }
127+ if let Some ( suffix) = suffix {
128+ add_suffix_if_missing ( buffer, suffix) ;
129+ }
130+ }
131+
132+ /// Adds a suffix to the metric name if it is not already in the name.
133+ fn add_suffix_if_missing ( buffer : & mut String , suffix : & str ) {
134+ if !buffer. ends_with ( suffix) {
135+ buffer. push ( '_' ) ;
136+ buffer. push_str ( suffix) ;
137+ }
138+ }
139+
140+ /// Adds a unit to the metric name if it is not already in the name.
141+ /// If the metric ends with a known suffix, we try to insert the unit before the suffix.
142+ /// Otherwise, we append the unit to the end of the metric name.
143+ fn add_unit_if_missing ( buffer : & mut String , unit : Unit ) {
144+ const KNOWN_SUFFIXES : [ & str ; 4 ] = [ "_sum" , "_count" , "_bucket" , "_total" ] ;
145+
146+ let unit = match unit {
147+ Unit :: Count => {
148+ // For count, we don't suffix the unit.
149+ return ;
150+ }
151+ Unit :: Percent => "ratio" ,
152+ unit => unit. as_str ( ) ,
153+ } ;
154+
155+ let mut handled = false ;
156+ for suffix in KNOWN_SUFFIXES {
157+ if buffer. ends_with ( suffix) {
158+ let suffix_pos = buffer. len ( ) - suffix. len ( ) ;
159+ // Check if name before suffix already has the unit
160+ if !& buffer[ ..suffix_pos] . ends_with ( unit) {
161+ buffer. insert ( suffix_pos, '_' ) ;
162+ buffer. insert_str ( suffix_pos + 1 , unit) ;
163+ }
164+ handled = true ;
165+ break ;
166+ }
167+ }
168+ if !handled && !buffer. ends_with ( unit) {
142169 buffer. push ( '_' ) ;
143170 buffer. push_str ( unit) ;
144171 }
0 commit comments