metrics_exporter_prometheus/lib.rs
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
//! 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:
//!
//! ```no_run
//! # use metrics_exporter_prometheus::PrometheusBuilder;
//! // 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 builder = PrometheusBuilder::new();
//! 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 builder = PrometheusBuilder::new();
//! 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 builder = PrometheusBuilder::new();
//! let recorder = builder.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.
//!
//! ## Upkeep and maintenance
//!
//! As Prometheus is generally a pull-based exporter -- clients "scrape" metrics by making an HTTP request to the
//! exporter -- the exporter itself sometimes has few opportunities to do maintenance tasks, such as draining histogram
//! buckets, which can grow over time and consume a large amount of memory.
//!
//! In order perform this maintenance, there is a concept of an "upkeep task", which periodically runs in the background
//! and performs the necessary "upkeep" of the various data structures. When using either [`PrometheusBuilder::build`]
//! or [`PrometheusBuilder::install`], an upkeep task will automatically be spawned on the asynchronous runtime being
//! used to ensure this maintenance occurs. However, when using lower-level builder methods
//! [`PrometheusBuilder::build_recorder`] or [`PrometheusBuilder::install_recorder`], this upkeep task is _not_ spawned
//! automatically. Users are responsible for keeping a handle to the recorder ([`PrometheusHandle`]) and calling the
//! [`run_upkeep`][PrometheusHandle::run_upkeep] method at a regular interval.
//!
//! [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 exporter;
pub use self::exporter::builder::PrometheusBuilder;
#[cfg(any(feature = "http-listener", feature = "push-gateway", feature = "remote-write"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http-listener", feature = "push-gateway", feature = "remote-write"))))]
pub use self::exporter::ExporterFuture;
#[cfg(feature = "remote-write")]
mod remote_write_proto;
pub mod formatting;
mod recorder;
mod registry;
pub use self::recorder::{PrometheusHandle, PrometheusRecorder};