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
//! A [`metrics`]-compatible exporter for sending metrics to Prometheus.
//!
//! ## Basics
//!
//! `metrics-exporter-prometheus` is a [`metrics`]-compatible exporter for either exposing an HTTP
//! endpoint that can be scraped by Prometheus, or that can push metrics to a Prometheus push
//! gateway.
//!
//! ## High-level features
//!
//! - scrape endpoint support
//! - push gateway support
//! - IP-based allowlist for scrape endpoint
//! - ability to push histograms as either aggregated summaries or aggregated histograms, with
//! configurable quantiles/buckets
//! - ability to control bucket configuration on a per-metric basis
//! - configurable global labels (applied to all metrics, overridden by metric's own labels if present)
//!
//! ## Behavior
//!
//! In general, interacting with the exporter should look and feel like interacting with any other
//! implementation of a Prometheus scrape endpoint or push gateway implementation, but there are
//! some small caveats around metric naming.
//!
//! We strive to match both the Prometheus [data model] and follow the [exposition format]
//! specification, but due to the decoupled nature of [`metrics`][metrics], the exporter makes some
//! specific trade-offs when ensuring compliance with the specification when it comes to metric
//! names and label keys. Below is a matrix of scenarios where the exporter will modify a metric
//! name or label key:
//!
//! - metric name starts with, or contains, an invalid character: **replace character with
//! underscore**
//! - label key starts with, or contains, an invalid character: **replace character with
//! underscore**
//! - label key starts with two underscores: **add additional underscore** (three underscores total)
//!
//! This behavior may be confusing at first since [`metrics`][metrics] itself allows any valid UTF-8
//! string for a metric name or label, but there is no way to report to the user that a metric name
//! or label key is invalid only when using the Prometheus exporter, so we must cope with these
//! situations by replacing invalid characters at runtime.
//!
//! ## Usage
//!
//! Using the exporter is straightforward:
//!
//! ```ignore
//! // First, create a builder.
//! //
//! // The builder can configure many aspects of the exporter, such as changing the
//! // listen address, adjusting how histograms will be reported, changing how long
//! // metrics can be idle before being removed, and more.
//! let builder = PrometheusBuilder::new();
//!
//! // Normally, most users will want to "install" the exporter which sets it as the
//! // global recorder for all `metrics` calls, and installs either an HTTP listener
//! // when running as a scrape endpoint, or a simple asynchronous task which pushes
//! // to the configured push gateway on the given interval.
//! //
//! // If you're already inside a Tokio runtime, this will spawn a task for the
//! // exporter on that runtime, and otherwise, a new background thread will be
//! // spawned which a Tokio single-threaded runtime is launched on to, where we then
//! // finally launch the exporter:
//! builder.install().expect("failed to install recorder/exporter");
//!
//! // Maybe you already have an HTTP endpoint that you want to expose a metrics
//! // endpoint on.. no problem! You can build the recorder and install it, and get
//! // back a handle that can be used to generate the Prometheus scrape output on
//! // demand:
//! let handle = builder.install_recorder().expect("failed to install recorder");
//!
//! // Maybe you have a more complicated setup and want to be handed back the recorder
//! // object and a future that can run the HTTP listener / push gateway so you can
//! // install/spawn them in a specific way.. also not a problem!
//! //
//! // As this is a more advanced method, it _must_ be called from within an existing
//! // Tokio runtime when the exporter is running in HTTP listener/scrape endpoint mode.
//! let (recorder, exporter) = builder.build().expect("failed to build recorder/exporter");
//!
//! // Finally, maybe you literally only want to build the recorder and nothing else,
//! // and we've got you covered there, too:
//! let recorder = builder.build_recorder().expect("failed to build recorder");
//! ```
//!
//! ## Features
//!
//! Two main feature flags control which modes that exporter can run in:
//! - **`http-listener`**: allows running the exporter as a scrape endpoint (_enabled by default_)
//! - **`push-gateway`**: allows running the exporter in push gateway mode (_enabled by default_)
//!
//! Neither of these flags are required to create, or install, only a recorder. However, in order
//! to create or build an exporter, at least one of these feature flags must be enabled. Builder
//! methods that require certain feature flags will be documented as such.
//!
//! [metrics]: https://docs.rs/metrics/latest/metrics/
//! [data model]: https://prometheus.io/docs/concepts/data_model/
//! [exposition format]: https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::module_name_repetitions)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
mod common;
pub use self::common::{BuildError, Matcher};
mod distribution;
pub use distribution::{Distribution, DistributionBuilder};
mod builder;
pub use self::builder::PrometheusBuilder;
pub mod formatting;
mod recorder;
pub use self::recorder::{PrometheusHandle, PrometheusRecorder};