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
//! Metrics handling library based on the `prometheus-client` crate.
//!
//! # Overview
//!
//! - The crate supports defining common metric types ([`Counter`]s, [`Gauge`]s and [`Histogram`]s).
//! A single metric is represented by an instance of these types; it can be reported using methods
//! like [`Counter::inc()`], [`Gauge::set()`] or [`Histogram::observe()`].
//! - Metrics can be grouped into a [`Family`]. Essentially, a `Family` is a map in which metrics
//! are values keyed by a set of labels. See [`EncodeLabelValue`] and [`EncodeLabelSet`] derive macros
//! for more info on labels.
//! - To define metrics, a group of logically related metrics is grouped into a struct
//! and the [`Metrics`](trait@Metrics) trait is [derived](macro@Metrics) for it. This resolves
//! full metric names and records additional metadata, such as help (from doc comments), unit of measurement
//! and [`Buckets`] for histograms.
//! - Metric groups are registered in a [`Registry`], which then allows to [encode](Registry::encode())
//! metric data in the OpenMetrics text format. Registration can be automated using the [`register`]
//! attribute, but it can be manual as well.
//! - In order to allow for metrics computed during scraping, you can use [`Collector`].
//!
//! # Examples
//!
//! ## Defining metrics
//!
//! ```
//! use vise::*;
//! use std::{fmt, time::Duration};
//!
//! /// Metrics defined by the library or application. A single app / lib can define
//! /// multiple metric structs.
//! #[derive(Debug, Clone, Metrics)]
//! #[metrics(prefix = "my_app")]
//! // ^ Prefix added to all field names to get the final metric name (e.g., `my_app_latencies`).
//! pub(crate) struct MyMetrics {
//! /// Simple counter. Doc comments for the fields will be reported
//! /// as Prometheus metric descriptions.
//! pub counter: Counter,
//! /// Integer-valued gauge. Unit will be reported to Prometheus and will influence metric name
//! /// by adding the corresponding suffix to it (in this case, `_bytes`).
//! #[metrics(unit = Unit::Bytes)]
//! pub gauge: Gauge<u64>,
//! /// Group of histograms with the "method" label (see the definition below).
//! /// Each `Histogram` or `Family` of `Histogram`s must define buckets; in this case,
//! /// we use default buckets for latencies.
//! #[metrics(buckets = Buckets::LATENCIES)]
//! pub latencies: Family<Method, Histogram<Duration>>,
//! }
//!
//! /// Isolated metric label. Note the `label` name specification below.
//! #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)]
//! #[metrics(label = "method")]
//! pub(crate) struct Method(pub &'static str);
//!
//! // For the isolated metric label to work, you should implement `Display` for it:
//! impl fmt::Display for Method {
//! fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
//! write!(formatter, "{}", self.0)
//! }
//! }
//! ```
//!
//! ## Registering metrics automatically
//!
//! Commonly, metrics can be registered by defining a `static`:
//!
//! ```
//! # use vise::{Gauge, Global, Metrics, MetricsCollection, Registry};
//! #[derive(Debug, Clone, Metrics)]
//! pub(crate) struct MyMetrics {
//! # pub gauge: Gauge<u64>,
//! // defined in a previous code sample
//! }
//!
//! #[vise::register]
//! pub(crate) static MY_METRICS: Global<MyMetrics> = Global::new();
//!
//! // All registered metrics can be collected in a `Registry`:
//! let registry: Registry = MetricsCollection::default().collect();
//! // Do something with the `registry`, e.g. create an exporter.
//!
//! fn metered_logic() {
//! // method logic...
//! MY_METRICS.gauge.set(42);
//! }
//! ```
//!
//! ## Registering metrics manually
//!
//! It is possible to add metrics manually to a [`Registry`]. As a downside, this approach requires
//! boilerplate to register all necessary metrics in an app and potentially libraries
//! that it depends on.
//!
//! ```
//! # use vise::{Gauge, Metrics, Registry};
//! #[derive(Debug, Clone, Metrics)]
//! pub(crate) struct MyMetrics {
//! # pub gauge: Gauge<u64>,
//! // defined in a previous code sample
//! }
//!
//! let mut registry = Registry::empty();
//! let my_metrics = MyMetrics::default();
//! registry.register_metrics(&my_metrics);
//! // Do something with the `registry`, e.g. create an exporter.
//!
//! // After registration, metrics can be moved to logic that reports the metrics.
//! // Note that metric types trivially implement `Clone` to allow sharing
//! // them among multiple components.
//! fn metered_logic(metrics: MyMetrics) {
//! // method logic...
//! metrics.gauge.set(42);
//! }
//!
//! metered_logic(my_metrics);
//! ```
// Documentation settings.
#![doc(html_root_url = "https://docs.rs/vise/0.1.0")]
// Linter settings.
#![warn(missing_debug_implementations, missing_docs, bare_trait_objects)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::must_use_candidate, clippy::module_name_repetitions)]
pub use prometheus_client::{metrics::counter::Counter, registry::Unit};
/// Derives the [`EncodeLabelValue`] trait for a type, which encodes a metric label value.
///
/// The type for which the trait is derived must not have generics (lifetimes, type params or const params).
/// The macro can be configured using `#[metrics()]` attributes.
///
/// # Container attributes
///
/// ## `format`
///
/// **Type:** string
///
/// **Default value:** `{}`
///
/// Specifies the format for the value as used in the `format!` macro etc. when encoding it to
/// a label value. For example, `{}` means using [`Display`](std::fmt::Display).
///
/// [`EncodeLabelValue`]: trait@prometheus_client::encoding::EncodeLabelValue
///
/// ## `rename_all`
///
/// **Type:** string; one of `lowercase`, `UPPERCASE`, `camelCase`, `snake_case`, `SCREAMING_SNAKE_CASE`,
/// `kebab-case`, `SCREAMING-KEBAB-CASE`
///
/// Specifies how enum variant names should be transformed into label values. This attribute
/// can only be placed on enums in which all variants don't have fields (aka C-style enums).
/// Mutually exclusive with the `format` attribute.
///
/// Caveats:
///
/// - `rename_all` assumes that original variant names are in `PascalCase` (i.e., follow Rust naming conventions).
/// - `rename_all` requires original variant names to consist of ASCII chars.
/// - Each letter of capitalized acronyms (e.g., "HTTP" in `HTTPServer`) is treated as a separate word.
/// E.g., `rename_all = "snake_case"` will rename `HTTPServer` to `h_t_t_p_server`.
/// Note that [it is recommended][clippy-acronyms] to not capitalize acronyms (i.e., use `HttpServer`).
/// - No spacing is inserted before numbers or other non-letter chars. E.g., `rename_all = "snake_case"`
/// will rename `Status500` to `status500`, not to `status_500`.
///
/// # Variant attributes
///
/// ## `name`
///
/// **Type:** string
///
/// Specifies the name override for a particular enum variant when used with the `rename_all` attribute
/// described above.
///
/// [clippy-acronyms]: https://rust-lang.github.io/rust-clippy/master/index.html#/upper_case_acronyms
///
/// # Examples
///
/// ## Default format
///
/// Label value using the default `Display` formatting; note that `Display` itself is derived.
///
/// ```
/// use derive_more::Display;
/// use vise::EncodeLabelValue;
///
/// #[derive(Debug, Display, EncodeLabelValue)]
/// struct Method(&'static str);
/// ```
///
/// ## Custom format
///
/// Label value using `Hex` formatting with `0` padding and `0x` prepended.
///
/// ```
/// # use derive_more::LowerHex;
/// # use vise::EncodeLabelValue;
/// #[derive(Debug, LowerHex, EncodeLabelValue)]
/// #[metrics(format = "0x{:02x}")]
/// struct ResponseType(u8);
/// ```
///
/// ## Using `rename_all` on C-style enum
///
/// ```
/// # use derive_more::LowerHex;
/// # use vise::EncodeLabelValue;
/// #[derive(Debug, EncodeLabelValue)]
/// #[metrics(rename_all = "snake_case")]
/// enum Database {
/// Postgres, // renamed to "postgres"
/// MySql, // renamed to "my_sql"
/// #[metrics(name = "rocksdb")] // explicitly overrides the name
/// RocksDB,
/// }
/// ```
pub use vise_macros::EncodeLabelValue;
/// Derives the [`EncodeLabelSet`] trait for a type, which encodes a set of metric labels.
///
/// The type for which the trait is derived must not have generics (lifetimes, type params or const params).
/// The macro can be configured using `#[metrics()]` attributes.
///
/// # Container attributes
///
/// ## `label`
///
/// **Type:** string
///
/// If specified, the type will be treated as a single label with the given name. This covers
/// the common case in which a label set consists of a single label. In this case, the type
/// also needs to implement [`EncodeLabelValue`].
///
/// If this attribute is not specified (which is the default), a container must be a `struct`
/// with named fields. A label with the matching name will be created for each field.
///
/// # Field attributes
///
/// ## `skip`
///
/// **Type:** path to a function with `fn(&FieldType) -> bool` signature
///
/// This attribute works similarly to `skip_serializing_if` in `serde` – if the function it points
/// to returns `true` for the field value, the field will not be encoded as a label.
///
/// `Option` fields are skipped by default if they are `None` (i.e., they use `skip = Option::is_none`).
///
/// [`EncodeLabelSet`]: trait@prometheus_client::encoding::EncodeLabelSet
///
/// ## `unit`
///
/// **Type:** expression evaluating to [`Unit`]
///
/// Specifies unit of measurement for a label. The unit will be added to the label name as a suffix
/// (e.g., `timeout_seconds` if placed on a field named `timeout`). This is mostly useful for [`Info`] metrics.
///
/// # Examples
///
/// ## Set with a single label
///
/// ```
/// use derive_more::Display;
/// use vise::{EncodeLabelSet, EncodeLabelValue};
///
/// #[derive(Debug, Display, Clone, PartialEq, Eq, Hash)]
/// #[derive(EncodeLabelValue, EncodeLabelSet)]
/// #[metrics(label = "method")]
/// struct Method(&'static str);
/// ```
///
/// ## Set with multiple labels
///
/// ```
/// # use vise::EncodeLabelSet;
/// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet)]
/// struct Labels {
/// /// Label that is skipped when empty.
/// #[metrics(skip = str::is_empty)]
/// name: &'static str,
/// /// Numeric label.
/// num: u8,
/// }
/// ```
///
/// ## Set for info metric
///
/// ```
/// use vise::{EncodeLabelSet, DurationAsSecs, Unit};
/// use std::time::Duration;
///
/// #[derive(Debug, EncodeLabelSet)]
/// struct InfoLabels {
/// /// Simple label.
/// version: &'static str,
/// /// Label with a unit.
/// #[metrics(unit = Unit::Seconds)]
/// request_timeout: DurationAsSecs,
/// /// Another label with a unit.
/// #[metrics(unit = Unit::Bytes)]
/// buffer_capacity: u64,
/// }
///
/// let labels = InfoLabels {
/// version: "0.1.0",
/// request_timeout: Duration::from_millis(100).into(),
/// buffer_capacity: 1_024,
/// };
/// // will be exported as the following labels:
/// // { version="0.1.0", request_timeout_seconds="0.1", buffer_capacity="1024" }
/// ```
pub use vise_macros::EncodeLabelSet;
/// Derives the [`Metrics`](trait@Metrics) trait for a type.
///
/// This macro must be placed on a struct with named fields and with no generics (lifetimes, type params
/// or const params). Each field will be registered as metric or a family of metrics.
/// The macro can be configured using `#[metrics()]` attributes.
///
/// # Container attributes
///
/// ## `prefix`
///
/// **Type:** string
///
/// Specifies a common prefix for all metrics defined in the type. If specified, the prefix will
/// be prepended together with a `_` separator to a field name to get the metric name. Note that
/// the metric name may be additionally transformed depending on the unit and metric type.
///
/// # Field attributes
///
/// ## `buckets`
///
/// **Type:** expression evaluating to a type implementing `Into<`[`Buckets`]`>`
///
/// Specifies buckets for a [`Histogram`] or a [`Family`] of `Histogram`s. This attribute is mandatory
/// for these metric types and will result in a compile-time error if used on counters / gauges.
///
/// ## `unit`
///
/// **Type:** expression evaluating to [`Unit`]
///
/// Specifies unit of measurement for a metric. Note that specifying a unit influences the metric naming.
///
/// ## `labels`
///
/// **Type:** expression evaluating to an array `[&'static str; _]`
///
/// Specifies label names for a [`LabeledFamily`]. This attribute is mandatory for [`LabeledFamily`]
/// and will result in a compile-time error if used with other metric types. The number of label names
/// must match the number of label values in the `LabeledFamily` (i.e., its type).
///
/// # Examples
///
/// See crate-level docs and other crate docs for the examples of usage.
pub use vise_macros::Metrics;
/// Registers a [`Global`] metrics instance or [`Collector`], so that it will be included
/// into registries instantiated using [`MetricsCollection`].
///
/// This macro must be placed on a static item of a type implementing [`CollectToRegistry`].
///
/// # Examples
///
/// ## Usage with global metrics
///
/// ```
/// use vise::{Gauge, Global, Metrics};
///
/// #[derive(Debug, Metrics)]
/// #[metrics(prefix = "test")]
/// pub(crate) struct TestMetrics {
/// gauge: Gauge,
/// }
///
/// #[vise::register]
/// static TEST_METRICS: Global<TestMetrics> = Global::new();
/// ```
///
/// ## Usage with collectors
///
/// ```
/// use vise::{Collector, Gauge, Global, Metrics};
///
/// #[derive(Debug, Metrics)]
/// #[metrics(prefix = "dynamic")]
/// pub(crate) struct DynamicMetrics {
/// gauge: Gauge,
/// }
///
/// #[vise::register]
/// static TEST_COLLECTOR: Collector<DynamicMetrics> = Collector::new();
/// ```
pub use vise_macros::register;
#[doc(hidden)] // only used by the proc macros
pub mod _reexports {
pub use linkme;
pub use prometheus_client::{encoding, metrics::TypedMetric};
}
mod buckets;
mod builder;
mod collector;
pub mod descriptors;
mod format;
mod metrics;
mod registry;
#[cfg(test)]
mod tests;
pub mod traits;
#[doc(hidden)]
pub mod validation;
mod wrappers;
pub use crate::{
buckets::Buckets,
builder::{BuildMetric, MetricBuilder},
collector::{BeforeScrapeError, Collector},
format::Format,
metrics::{Global, Metrics},
registry::{
CollectToRegistry, MetricsCollection, MetricsVisitor, RegisteredDescriptors, Registry,
METRICS_REGISTRATIONS,
},
wrappers::{
DurationAsSecs, Family, Gauge, GaugeGuard, Histogram, Info, LabelWithUnit, LabeledFamily,
LatencyObserver, SetInfoError,
},
};
#[cfg(doctest)]
doc_comment::doctest!("../README.md");