1
1
//! Open Metrics text format implementation.
2
2
//!
3
3
//! ```
4
- //! # use prometheus_client::encoding::text::encode;
4
+ //! # use prometheus_client::encoding::text::{ encode, encode_registry, encode_eof} ;
5
5
//! # use prometheus_client::metrics::counter::Counter;
6
6
//! # use prometheus_client::registry::Registry;
7
7
//! #
15
15
//! # );
16
16
//! # counter.inc();
17
17
//! let mut buffer = String::new();
18
+ //!
19
+ //! // Encode the complete OpenMetrics exposition into the message buffer
18
20
//! encode(&mut buffer, ®istry).unwrap();
21
+ //! let expected_msg = "# HELP my_counter This is my counter.\n".to_owned() +
22
+ //! "# TYPE my_counter counter\n" +
23
+ //! "my_counter_total 1\n" +
24
+ //! "# EOF\n";
25
+ //! assert_eq!(expected_msg, buffer);
26
+ //! buffer.clear();
27
+ //!
28
+ //! // Encode just the registry into the message buffer
29
+ //! encode_registry(&mut buffer, ®istry).unwrap();
30
+ //! let expected_reg = "# HELP my_counter This is my counter.\n".to_owned() +
31
+ //! "# TYPE my_counter counter\n" +
32
+ //! "my_counter_total 1\n";
33
+ //! assert_eq!(expected_reg, buffer);
19
34
//!
20
- //! let expected = "# HELP my_counter This is my counter.\n".to_owned() +
21
- //! "# TYPE my_counter counter\n" +
22
- //! "my_counter_total 1\n" +
23
- //! "# EOF\n";
24
- //! assert_eq!(expected, buffer);
35
+ //! // Encode EOF marker into message buffer to complete the OpenMetrics exposition
36
+ //! encode_eof(&mut buffer).unwrap();
37
+ //! assert_eq!(expected_msg, buffer);
25
38
//! ```
26
39
27
40
use crate :: encoding:: { EncodeExemplarValue , EncodeLabelSet } ;
@@ -33,15 +46,140 @@ use std::borrow::Cow;
33
46
use std:: collections:: HashMap ;
34
47
use std:: fmt:: Write ;
35
48
49
+ /// Encode both the metrics registered with the provided [`Registry`] and the
50
+ /// EOF marker into the provided [`Write`]r using the OpenMetrics text format.
51
+ ///
52
+ /// Note: This function encodes the **complete** OpenMetrics exposition.
53
+ ///
54
+ /// Use [`encode_registry`] or [`encode_eof`] if partial encoding is needed.
55
+ ///
56
+ /// See [OpenMetrics exposition format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#text-format)
57
+ /// for additional details.
58
+ ///
59
+ /// # Examples
60
+ ///
61
+ /// ```no_run
62
+ /// # use prometheus_client::encoding::text::encode;
63
+ /// # use prometheus_client::metrics::counter::Counter;
64
+ /// # use prometheus_client::metrics::gauge::Gauge;
65
+ /// # use prometheus_client::registry::Registry;
66
+ /// #
67
+ /// // Initialize registry with metric families
68
+ /// let mut registry = Registry::default();
69
+ /// let counter: Counter = Counter::default();
70
+ /// registry.register(
71
+ /// "my_counter",
72
+ /// "This is my counter",
73
+ /// counter.clone(),
74
+ /// );
75
+ /// let gauge: Gauge = Gauge::default();
76
+ /// registry.register(
77
+ /// "my_gauge",
78
+ /// "This is my gauge",
79
+ /// gauge.clone(),
80
+ /// );
81
+ ///
82
+ /// // Encode the complete OpenMetrics exposition into the buffer
83
+ /// let mut buffer = String::new();
84
+ /// encode(&mut buffer, ®istry)?;
85
+ /// # Ok::<(), std::fmt::Error>(())
86
+ /// ```
87
+ pub fn encode < W > ( writer : & mut W , registry : & Registry ) -> Result < ( ) , std:: fmt:: Error >
88
+ where
89
+ W : Write ,
90
+ {
91
+ encode_registry ( writer, registry) ?;
92
+ encode_eof ( writer)
93
+ }
94
+
36
95
/// Encode the metrics registered with the provided [`Registry`] into the
37
96
/// provided [`Write`]r using the OpenMetrics text format.
38
- pub fn encode < W > ( writer : & mut W , registry : & Registry ) -> Result < ( ) , std:: fmt:: Error >
97
+ ///
98
+ /// Note: The OpenMetrics exposition requires that a complete message must end
99
+ /// with an EOF marker.
100
+ ///
101
+ /// This function may be called repeatedly for the HTTP scrape response until
102
+ /// [`encode_eof`] signals the end of the response.
103
+ ///
104
+ /// This may also be used to compose a partial message with metrics assembled
105
+ /// from multiple registries.
106
+ ///
107
+ /// # Examples
108
+ ///
109
+ /// ```no_run
110
+ /// # use prometheus_client::encoding::text::encode_registry;
111
+ /// # use prometheus_client::metrics::counter::Counter;
112
+ /// # use prometheus_client::metrics::gauge::Gauge;
113
+ /// # use prometheus_client::registry::Registry;
114
+ /// #
115
+ /// // Initialize registry with a counter
116
+ /// let mut reg_counter = Registry::default();
117
+ /// let counter: Counter = Counter::default();
118
+ /// reg_counter.register(
119
+ /// "my_counter",
120
+ /// "This is my counter",
121
+ /// counter.clone(),
122
+ /// );
123
+ ///
124
+ /// // Encode the counter registry into the buffer
125
+ /// let mut buffer = String::new();
126
+ /// encode_registry(&mut buffer, ®_counter)?;
127
+ ///
128
+ /// // Initialize another registry but with a gauge
129
+ /// let mut reg_gauge = Registry::default();
130
+ /// let gauge: Gauge = Gauge::default();
131
+ /// reg_gauge.register(
132
+ /// "my_gauge",
133
+ /// "This is my gauge",
134
+ /// gauge.clone(),
135
+ /// );
136
+ ///
137
+ /// // Encode the gauge registry into the buffer
138
+ /// encode_registry(&mut buffer, ®_gauge)?;
139
+ /// # Ok::<(), std::fmt::Error>(())
140
+ /// ```
141
+ pub fn encode_registry < W > ( writer : & mut W , registry : & Registry ) -> Result < ( ) , std:: fmt:: Error >
39
142
where
40
143
W : Write ,
41
144
{
42
- registry. encode ( & mut DescriptorEncoder :: new ( writer) . into ( ) ) ?;
43
- writer. write_str ( "# EOF\n " ) ?;
44
- Ok ( ( ) )
145
+ registry. encode ( & mut DescriptorEncoder :: new ( writer) . into ( ) )
146
+ }
147
+
148
+ /// Encode the EOF marker into the provided [`Write`]r using the OpenMetrics
149
+ /// text format.
150
+ ///
151
+ /// Note: This function is used to mark/signal the end of the exposition.
152
+ ///
153
+ /// # Examples
154
+ ///
155
+ /// ```no_run
156
+ /// # use prometheus_client::encoding::text::{encode_registry, encode_eof};
157
+ /// # use prometheus_client::metrics::counter::Counter;
158
+ /// # use prometheus_client::metrics::gauge::Gauge;
159
+ /// # use prometheus_client::registry::Registry;
160
+ /// #
161
+ /// // Initialize registry with a counter
162
+ /// let mut registry = Registry::default();
163
+ /// let counter: Counter = Counter::default();
164
+ /// registry.register(
165
+ /// "my_counter",
166
+ /// "This is my counter",
167
+ /// counter.clone(),
168
+ /// );
169
+ ///
170
+ /// // Encode registry into the buffer
171
+ /// let mut buffer = String::new();
172
+ /// encode_registry(&mut buffer, ®istry)?;
173
+ ///
174
+ /// // Encode EOF marker to complete the message
175
+ /// encode_eof(&mut buffer)?;
176
+ /// # Ok::<(), std::fmt::Error>(())
177
+ /// ```
178
+ pub fn encode_eof < W > ( writer : & mut W ) -> Result < ( ) , std:: fmt:: Error >
179
+ where
180
+ W : Write ,
181
+ {
182
+ writer. write_str ( "# EOF\n " )
45
183
}
46
184
47
185
pub ( crate ) struct DescriptorEncoder < ' a > {
@@ -915,6 +1053,52 @@ mod tests {
915
1053
parse_with_python_client ( encoded) ;
916
1054
}
917
1055
1056
+ #[ test]
1057
+ fn encode_registry_eof ( ) {
1058
+ let mut orders_registry = Registry :: default ( ) ;
1059
+
1060
+ let total_orders: Counter < u64 > = Default :: default ( ) ;
1061
+ orders_registry. register ( "orders" , "Total orders received" , total_orders. clone ( ) ) ;
1062
+ total_orders. inc ( ) ;
1063
+
1064
+ let processing_times = Histogram :: new ( exponential_buckets ( 1.0 , 2.0 , 10 ) ) ;
1065
+ orders_registry. register_with_unit (
1066
+ "processing_times" ,
1067
+ "Order times" ,
1068
+ Unit :: Seconds ,
1069
+ processing_times. clone ( ) ,
1070
+ ) ;
1071
+ processing_times. observe ( 2.4 ) ;
1072
+
1073
+ let mut user_auth_registry = Registry :: default ( ) ;
1074
+
1075
+ let successful_logins: Counter < u64 > = Default :: default ( ) ;
1076
+ user_auth_registry. register (
1077
+ "successful_logins" ,
1078
+ "Total successful logins" ,
1079
+ successful_logins. clone ( ) ,
1080
+ ) ;
1081
+ successful_logins. inc ( ) ;
1082
+
1083
+ let failed_logins: Counter < u64 > = Default :: default ( ) ;
1084
+ user_auth_registry. register (
1085
+ "failed_logins" ,
1086
+ "Total failed logins" ,
1087
+ failed_logins. clone ( ) ,
1088
+ ) ;
1089
+
1090
+ let mut response = String :: new ( ) ;
1091
+
1092
+ encode_registry ( & mut response, & orders_registry) . unwrap ( ) ;
1093
+ assert_eq ! ( & response[ response. len( ) - 20 ..] , "bucket{le=\" +Inf\" } 1\n " ) ;
1094
+
1095
+ encode_registry ( & mut response, & user_auth_registry) . unwrap ( ) ;
1096
+ assert_eq ! ( & response[ response. len( ) - 20 ..] , "iled_logins_total 0\n " ) ;
1097
+
1098
+ encode_eof ( & mut response) . unwrap ( ) ;
1099
+ assert_eq ! ( & response[ response. len( ) - 20 ..] , "ogins_total 0\n # EOF\n " ) ;
1100
+ }
1101
+
918
1102
fn parse_with_python_client ( input : String ) {
919
1103
pyo3:: prepare_freethreaded_python ( ) ;
920
1104
0 commit comments