1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
//! Metrics-related functionality.
//!
//! Rustfoundry provides simple and ergonomic interface to [Prometheus] metrics:
//! - Use [`metrics`] macro to define regular metrics.
//! - Use [`report_info`] function to register service information metrics (metrics, whose value is
//! persistent during the service lifetime, e.g. software version).
//! - Use [`collect`] method to obtain metrics report programmatically.
//! - Use [telemetry server] to expose a metrics endpoint.
//!
//! [Prometheus]: https://prometheus.io/
use MetricsSettings;
use crateResult;
use ;
use Serialize;
use TypeId;
pub
use ;
pub use ;
pub use MetricConstructor;
pub use Gauge;
pub use Histogram;
pub use ;
pub use NonstandardUnsuffixedCounter as Counter;
pub use Family;
/// Collects all metrics in [Prometheus text format].
///
/// [Prometheus text format]: https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format
/// A macro that allows to define Prometheus metrics.
///
/// The macro is a proc macro attribute that should be put on a module containing
/// bodyless functions. Each bodyless function corresponds to a single metric, whose
/// name becomes `<global prefix>_<module name>_<bodyless function name>`and function's
/// Rust doc comment is reported as metric description to Prometheus.
///
/// # Labels
/// Arguments of the bodyless functions become labels for that metric.
///
/// The metric types must implement [`prometheus_client::metrics::MetricType`], they
/// are reexported from this module for convenience:
///
/// * [`Counter`]
/// * [`Gauge`]
/// * [`Histogram`]
/// * [`TimeHistogram`]
///
/// The metrics associated with the functions are automatically registered in a global
/// registry, and they can be collected with the [`collect`] function.
///
/// # Metric attributes
///
/// Example below shows how to use all the attributes listed here.
///
/// ## `#[ctor]`
///
/// `#[ctor]` attribute allows specifying how the metric should be built (e.g. [`HistogramBuilder`]).
/// Constructor should implement the [`MetricConstructor<MetricType>`] trait.
///
/// ## `#[optional]`
///
/// Metrics marked with `#[optional]` are collected in a separate registry and reported only if
/// `collect_optional` argument of [`collect`] is set to `true`, or, in case the [telemetry server]
/// is used, if [`MetricsSettings::report_optional`] is set to `true`.
///
///
/// Can be used for heavy-weight metrics (e.g. with high cardinality) that don't need to be reported
/// on a regular basis.
///
/// # Example
///
/// ```
/// # // As rustdoc puts doc tests in `fn main()`, the implicit `use super::*;` inserted
/// # // in the metric mod doesn't see `SomeLabel`, so we wrap the entire test in a module.
/// # mod rustdoc_workaround {
/// use rustfoundry::telemetry::metrics::{metrics, Counter, Gauge, HistogramBuilder, TimeHistogram};
/// use serde_with::DisplayFromStr;
/// use std::net::IpAddr;
/// use std::io;
/// use std::sync::Arc;
///
/// mod labels {
/// use serde::Serialize;
///
/// #[derive(Clone, Eq, Hash, PartialEq, Serialize)]
/// #[serde(rename_all = "lowercase")]
/// pub enum IpVersion {
/// V4,
/// V6,
/// }
///
/// #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize)]
/// #[serde(rename_all = "lowercase")]
/// pub enum L4Protocol {
/// Tcp,
/// Udp,
/// Quic,
/// Unknown,
/// }
///
/// #[derive(Clone, Eq, Hash, PartialEq, Serialize)]
/// #[serde(rename_all = "lowercase")]
/// pub enum ProxiedProtocol {
/// Ip,
/// Tcp,
/// Udp,
/// Quic,
/// Unknown,
/// }
///
/// impl From<L4Protocol> for ProxiedProtocol {
/// fn from(l4: L4Protocol) -> Self {
/// match l4 {
/// L4Protocol::Tcp => Self::Tcp,
/// L4Protocol::Udp => Self::Udp,
/// L4Protocol::Quic => Self::Quic,
/// L4Protocol::Unknown => Self::Unknown,
/// }
/// }
/// }
/// }
///
/// // The generated module contains an implicit `use super::*;` statement.
/// #[metrics]
/// pub mod my_app_metrics {
/// /// Number of active client connections
/// pub fn client_connections_active(
/// // Labels with an anonymous reference type will get cloned.
/// endpoint: &Arc<String>,
/// protocol: labels::L4Protocol,
/// ip_version: labels::IpVersion,
/// ingress_ip: IpAddr,
/// ) -> Gauge;
///
/// /// Histogram of task schedule delays
/// #[ctor = HistogramBuilder {
/// // 100 us to 1 second
/// buckets: &[1E-4, 2E-4, 3E-4, 4E-4, 5E-4, 6E-4, 7E-4, 8E-4, 9E-4, 1E-3, 1E-2, 2E-2, 4E-2, 8E-2, 1E-1, 1.0],
/// }]
/// pub fn tokio_runtime_task_schedule_delay_histogram(
/// task: &Arc<str>,
/// ) -> TimeHistogram;
///
/// /// Number of client connections
/// pub fn client_connections_total(
/// endpoint: &Arc<String>,
/// // Labels with type `impl Into<T>` will invoke `std::convert::Into<T>`.
/// protocol: impl Into<labels::ProxiedProtocol>,
/// ingress_ip: IpAddr,
/// ) -> Counter;
///
/// /// Tunnel transmit error count
/// pub fn tunnel_transmit_errors_total(
/// endpoint: &Arc<String>,
/// protocol: labels::L4Protocol,
/// ingress_ip: IpAddr,
/// // `serde_as` attribute is allowed without decorating the metric with `serde_with::serde_as`.
/// #[serde_as(as = "DisplayFromStr")]
/// kind: io::ErrorKind,
/// raw_os_error: i32,
/// ) -> Counter;
///
/// /// Number of stalled futures
/// #[optional]
/// pub fn debug_stalled_future_count(
/// // Labels with a `'static` lifetime are used as is, without cloning.
/// name: &'static str,
/// ) -> Counter;
///
/// /// Number of Proxy-Status serialization errors
/// // Metrics with no labels are also obviously supported.
/// pub fn proxy_status_serialization_error_count() -> Counter;
/// }
///
/// fn usage() {
/// let endpoint = Arc::new("http-over-tcp".to_owned());
/// let l4_protocol = labels::L4Protocol::Tcp;
/// let ingress_ip = "127.0.0.1".parse::<IpAddr>().unwrap();
///
/// my_app_metrics::client_connections_total(
/// &endpoint,
/// l4_protocol,
/// ingress_ip,
/// ).inc();
///
/// let client_connections_active = my_app_metrics::client_connections_active(
/// &endpoint,
/// l4_protocol,
/// labels::IpVersion::V4,
/// ingress_ip,
/// );
///
/// client_connections_active.inc();
///
/// my_app_metrics::proxy_status_serialization_error_count().inc();
///
/// client_connections_active.dec();
/// }
/// # }
/// ```
///
/// # Renamed or reexported crate
///
/// The macro will fail to compile if `rustfoundry` crate is reexported. However, the crate path
/// can be explicitly specified for the macro to workaround that:
///
/// ```
/// # mod rustdoc_workaround {
/// mod reexport {
/// pub use rustfoundry::*;
/// }
///
/// use self::reexport::telemetry::metrics::Counter;
///
/// #[reexport::telemetry::metrics::metrics(crate_path = "reexport")]
/// mod my_app_metrics {
/// /// Total number of tasks workers stole from each other.
/// fn tokio_runtime_total_task_steal_count() -> Counter;
/// }
/// # }
/// ```
///
/// [telemetry server]: crate::telemetry::init_with_server
/// [`MetricsSettings::report_optional`]: crate::telemetry::settings::MetricsSettings::report_optional
pub use metrics;
/// A macro that allows to define a Prometheus info metric.
///
/// The metrics defined by this function should be used with [`report_info`] and they can be
/// collected with the telemetry server.
///
/// The struct name becomes the metric name in `snake_case`, and each field of the struct becomes
/// a label.
///
/// # Simple example
///
/// See [`report_info`] for a simple example.
///
/// # Renaming the metric.
///
/// ```
/// use rustfoundry::telemetry::metrics::{info_metric, report_info};
///
/// /// Build information
/// #[info_metric(name = "build_info")]
/// struct BuildInformation {
/// version: &'static str,
/// }
///
/// report_info(BuildInformation {
/// version: "1.2.3",
/// });
/// ```
/// # Renamed or reexported crate
///
/// The macro will fail to compile if `rustfoundry` crate is reexported. However, the crate path
/// can be explicitly specified for the macro to workaround that:
///
/// ```
/// # mod rustdoc_workaround {
/// mod reexport {
/// pub use rustfoundry::*;
/// }
///
/// /// Build information
/// #[reexport::telemetry::metrics::info_metric(crate_path = "reexport")]
/// struct BuildInfo {
/// version: &'static str,
/// }
/// # }
/// ```
pub use info_metric;
/// Describes an info metric.
///
/// Info metrics are used to expose textual information, through the label set, which should not
/// change often during process lifetime. Common examples are an application's version, revision
/// control commit, and the version of a compiler.
/// Registers an info metric, i.e. a gauge metric whose value is always `1`, set at init time.
///
/// # Examples
///
/// ```
/// use rustfoundry::telemetry::metrics::{info_metric, report_info};
///
/// /// Build information
/// #[info_metric]
/// struct BuildInfo {
/// version: &'static str,
/// }
///
/// report_info(BuildInfo {
/// version: "1.2.3",
/// });
/// ```
/// A builder suitable for [`Histogram`] and [`TimeHistogram`].
///
/// # Example
///
/// ```
/// # // As rustdoc puts doc tests in `fn main()`, the implicit `use super::*;` inserted
/// # // in the metric mod doesn't see `SomeLabel`, so we wrap the entire test in a module.
/// # mod rustdoc_workaround {
/// use rustfoundry::telemetry::metrics::{metrics, HistogramBuilder, TimeHistogram};
///
/// #[metrics]
/// pub mod my_app_metrics {
/// #[ctor = HistogramBuilder {
/// // 0 us to 10 ms
/// buckets: &[0.0, 1E-4, 2E-4, 3E-4, 4E-4, 5E-4, 6E-4, 7E-4, 8E-4, 9E-4, 1E-3, 1E-2, 2E-2, 4E-2, 8E-2, 1E-1, 1.0],
/// }]
/// pub fn tokio_runtime_task_schedule_delay_histogram(
/// task: String,
/// ) -> TimeHistogram;
/// }
/// # }
/// ```
/// Adds an [ExtraProducer] that runs whenever Prometheus metrics are scraped.
/// The producer appends metrics into a provided buffer to make them available.
///
/// The motivation for this is enabling metrics export from third party libraries that
/// do not integrate with `rustfoundry`` directly in a forward and backward compatible way.
///
/// One can ask "why not expose a `Registry` from `prometheus_client`?" The reason is that
/// it would require compatibility between `prometheus_client` version that `rustfoundry`
/// depend on and the version that the third party crates depend on. With a producer
/// that simply appends bytes into a buffer we avoid the need to have this match,
/// at the cost of requiring the consumers to do the encoding themselves.
///
/// # Example
///
/// In this example we have a `Cache` that would be provided from an external crate, which
/// does not expose metrics directly, but allows registering them in a provided `Registry`.
///
/// The consumer code would make a `Registry` with whatever version they want and do
/// the encoding in a text format to make a suitable [ExtraProducer].
///
/// ```
/// #[derive(Default)]
/// struct Cache {
/// calls: prometheus_client::metrics::counter::Counter,
/// }
///
/// impl Cache {
/// fn register_metrics(&self, registry: &mut prometheus_client::registry::Registry) {
/// registry.register(
/// "calls",
/// "The number of calls into cache",
/// Box::new(self.calls.clone()),
/// )
/// }
/// }
///
/// let cache = Cache::default();
///
/// let mut registry = prometheus_client::registry::Registry::default();
/// let mut sub_registry = registry.sub_registry_with_prefix("cache").sub_registry_with_label((
/// std::borrow::Cow::Borrowed("cache"),
/// std::borrow::Cow::Borrowed("things"),
/// ));
///
/// cache.register_metrics(&mut sub_registry);
///
/// rustfoundry::telemetry::metrics::add_extra_producer(move |buffer: &mut Vec<u8>| {
/// prometheus_client::encoding::text::encode(buffer, ®istry).unwrap();
/// });
/// ```
/// Describes something that can expand prometheus metrics but appending
/// them in a text format to a provided buffer.