From 854ddced3a8b080ce813a2a80c3bf1c2452c5986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Thu, 17 Mar 2022 14:58:38 +0100 Subject: [PATCH] chore!: replace prometheus with metrics As part of the overall modernization of this application, this commit replaces the prometheus crate with the generic metrics facade crate, and the metrics_exporter_prometheus crate for exporting the metrics to prometheus. This also removes some useless http metrics that were cargo-culted in from the prometheus crate example. BREAKING CHANGE: http metrics are removed by this change --- Cargo.lock | 270 +++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 4 +- src/config.rs | 34 ++++++- src/main.rs | 12 ++- src/metrics.rs | 61 +++-------- src/ping.rs | 25 +---- 6 files changed, 308 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 917a4f3..4b5ee78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -39,6 +50,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-shim" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "atty" version = "0.2.14" @@ -106,6 +126,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + [[package]] name = "bytes" version = "1.0.1" @@ -146,6 +172,30 @@ dependencies = [ "vec_map", ] +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if", + "lazy_static", +] + [[package]] name = "darling" version = "0.13.1" @@ -309,6 +359,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + [[package]] name = "hermit-abi" version = "0.1.18" @@ -387,6 +446,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.9" @@ -402,6 +471,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -433,6 +511,15 @@ dependencies = [ "serde", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "matches" version = "0.1.9" @@ -451,6 +538,67 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metrics" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e52eb6380b6d2a10eb3434aec0885374490f5b82c8aaf5cd487a183c98be834" +dependencies = [ + "ahash", + "metrics-macros", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b93b470b04c005178058e18ac8bb2eb3fda562cf87af5ea05ba8d44190d458c" +dependencies = [ + "indexmap", + "metrics", + "metrics-util", + "parking_lot", + "quanta", + "thiserror", +] + +[[package]] +name = "metrics-macros" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e30813093f757be5cf21e50389a24dc7dbb22c49f23b7e8f51d69b508a5ffa" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "metrics-util" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107a38013e91c04ddf31826b0d0dcc2e0d4ebedded8234cc0dc2b7bbd0c121e8" +dependencies = [ + "atomic-shim", + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown", + "metrics", + "num_cpus", + "parking_lot", + "quanta", + "sketches-ddsketch", +] + [[package]] name = "mime" version = "0.3.16" @@ -517,6 +665,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + [[package]] name = "parking_lot" version = "0.11.1" @@ -560,9 +714,9 @@ dependencies = [ "fern", "futures", "futures-util", - "lazy_static", "log", - "prometheus", + "metrics", + "metrics-exporter-prometheus", "serde", "serde_with", "tokio", @@ -630,26 +784,21 @@ dependencies = [ ] [[package]] -name = "prometheus" -version = "0.12.0" +name = "quanta" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5986aa8d62380092d2f50f8b1cdba9cb9b6731ffd4b25b51fd126b6c3e05b99c" +checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "memchr", - "parking_lot", - "protobuf", - "thiserror", + "crossbeam-utils", + "libc", + "mach", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", ] -[[package]] -name = "protobuf" -version = "2.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45604fc7a88158e7d514d8e22e14ac746081e7a70d7690074dd0029ee37458d6" - [[package]] name = "quote" version = "1.0.9" @@ -699,6 +848,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "raw-cpuid" +version = "10.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_syscall" version = "0.2.6" @@ -792,6 +950,12 @@ dependencies = [ "syn", ] +[[package]] +name = "sketches-ddsketch" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a77a8fd93886010f05e7ea0720e569d6d16c65329dbe3ec033bbbccccb017b" + [[package]] name = "slab" version = "0.4.3" @@ -1039,6 +1203,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "want" version = "0.3.0" @@ -1055,6 +1225,70 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index a9e7f5d..0a00078 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,7 @@ edition = "2018" description = "Pings configured hosts in a configurable intervals and exposes metrics for prometheus." [dependencies] -prometheus = "0.12" toml = "0.5" -lazy_static = "1" futures = "0.3" tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] } clap = "2" @@ -25,3 +23,5 @@ futures-util = "0.3" anyhow = "1" async-anyhow-logger = "0.1" axum = "0.4.8" +metrics = "0.18.1" +metrics-exporter-prometheus = { version = "0.9.0", default-features = false } diff --git a/src/config.rs b/src/config.rs index 549ae12..cc3e76a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,10 +20,17 @@ use anyhow::{Context, Result}; use clap::{clap_app, crate_authors, crate_description, crate_name, crate_version}; use log::info; +use metrics::{describe_histogram, register_histogram, Unit}; +use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle}; use serde::Deserialize; use serde_with::{serde_as, DurationMilliSeconds}; use std::{collections::HashMap, time::Duration}; +pub(crate) struct App { + pub(crate) config: Config, + pub(crate) handle: PrometheusHandle, +} + #[derive(Deserialize, Clone)] pub(crate) struct Config { pub(crate) listener: std::net::SocketAddr, @@ -97,14 +104,37 @@ fn read_config(path: &str) -> Result { Ok(toml::from_str(&config_file_content).context("Couldn't parse config file")?) } -pub(crate) fn setup_app() -> Result { +pub(crate) fn setup_app() -> Result { let clap = setup_clap(); let config_path = clap .value_of("config") .context("Got no config file. clap should've catched this")?; let config = read_config(config_path).context("Couldn't read config file!")?; setup_fern(determine_level(clap.occurrences_of("v"), config.log.level)); - Ok(config) + let handle = setup_prometheus(&config)?; + Ok(App { config, handle }) +} + +pub(crate) fn setup_prometheus(config: &Config) -> Result { + let handle = PrometheusBuilder::new() + .set_buckets_for_metric(Matcher::Full("ping_rtt_milliseconds".into()), &vec![ + 0.5, 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, + 350.0, 400.0, 450.0, 500.0, 550.0, 600.0, 650.0, 700.0, 750.0, 800.0, 900.0, 1000.0, + 1250.0, 1500.0, 1750.0, 2000.0, + ])? + .install_recorder()?; + + for target in config.ping.hosts.keys() { + register_histogram!("ping_rtt_milliseconds", "target" => target.to_string()); + } + + describe_histogram!( + "ping_rtt_milliseconds", + Unit::Milliseconds, + "The ping round trip time in milliseconds" + ); + + Ok(handle) } fn determine_level(verbose_occurrences: u64, config_level: log::LevelFilter) -> log::LevelFilter { diff --git a/src/main.rs b/src/main.rs index abd8244..55466ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,16 +24,18 @@ mod config; mod metrics; mod ping; -use config::setup_app; -use metrics::start_serving_metrics; -use ping::start_pinging_hosts; +use crate::{ + config::{setup_app, App}, + metrics::start_serving_metrics, + ping::start_pinging_hosts, +}; #[tokio::main] async fn main() -> Result<()> { - let config = setup_app()?; + let App { config, handle } = setup_app()?; let ping_fut = catch(start_pinging_hosts(&config)); - let serve_fut = catch(start_serving_metrics(&config)); + let serve_fut = catch(start_serving_metrics(&config, handle)); futures::pin_mut!(ping_fut); futures::pin_mut!(serve_fut); diff --git a/src/metrics.rs b/src/metrics.rs index c5bb29d..545c496 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -19,60 +19,23 @@ ********************************************************************************/ use crate::config::Config; use anyhow::Result; -use axum::body::Body; -use axum::http::{Response, StatusCode}; -use axum::{http::header::CONTENT_TYPE, response::IntoResponse, routing::get, Router, Server}; -use lazy_static::lazy_static; +use axum::{response::IntoResponse, routing::get, Router, Server}; use log::info; -use prometheus::*; -use prometheus::{Counter, Gauge, HistogramVec, TextEncoder}; +use metrics_exporter_prometheus::PrometheusHandle; -lazy_static! { - static ref HTTP_COUNTER: Counter = register_counter!(opts!( - "http_requests_total", - "Total number of HTTP requests made.", - labels! {"handler" => "all",} - )) - .unwrap(); - static ref HTTP_BODY_GAUGE: Gauge = register_gauge!(opts!( - "http_response_size_bytes", - "The HTTP response sizes in bytes.", - labels! {"handler" => "all",} - )) - .unwrap(); - static ref HTTP_REQ_HISTOGRAM: HistogramVec = register_histogram_vec!( - "http_request_duration_seconds", - "The HTTP request latencies in seconds.", - &["handler"] - ) - .unwrap(); +async fn metrics(handle: PrometheusHandle) -> impl IntoResponse { + handle.render() } -async fn serve_metrics() -> impl IntoResponse { - let encoder = TextEncoder::new(); - - HTTP_COUNTER.inc(); - let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["all"]).start_timer(); - - let metric_families = prometheus::gather(); - let mut buffer = vec![]; - encoder.encode(&metric_families, &mut buffer).unwrap(); - HTTP_BODY_GAUGE.set(buffer.len() as f64); - - let response = Response::builder() - .status(StatusCode::OK) - .header(CONTENT_TYPE, encoder.format_type()) - .body(Body::from(buffer)) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); - - timer.observe_duration(); - - response -} - -pub(crate) async fn start_serving_metrics(config: &Config) -> Result<()> { +pub(crate) async fn start_serving_metrics(config: &Config, handle: PrometheusHandle) -> Result<()> { let app = Router::new() - .route("/metrics", get(serve_metrics)) + .route( + "/metrics", + get({ + let handle = handle.clone(); + move || metrics(handle) + }), + ) .route("/health", get(|| async { "" })); let serve_future = Server::bind(&config.listener).serve(app.into_make_service()); info!("Listening on {}", &config.listener); diff --git a/src/ping.rs b/src/ping.rs index 7151e93..946de58 100644 --- a/src/ping.rs +++ b/src/ping.rs @@ -20,27 +20,12 @@ use crate::config::Config; use anyhow::{Context, Result}; use async_anyhow_logger::catch; -use lazy_static::lazy_static; use log::{info, trace}; -use prometheus::*; +use metrics::histogram; use std::net::IpAddr; use std::time::Duration; use tokio_icmp_echo::{PingFuture, Pinger}; -lazy_static! { - static ref PING_HISTOGRAM: HistogramVec = register_histogram_vec!( - "ping_rtt_milliseconds", - "The ping round trip time in milliseconds", - &["target"], - vec![ - 0.5, 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, - 350.0, 400.0, 450.0, 500.0, 550.0, 600.0, 650.0, 700.0, 750.0, 800.0, 900.0, 1000.0, - 1250.0, 1500.0, 1750.0, 2000.0 - ] - ) - .unwrap(); -} - pub(crate) async fn start_pinging_hosts(config: &Config) -> Result<()> { let pinger = Pinger::new().await.context("Couldn't create pinger")?; let mut handles = vec![]; @@ -78,15 +63,11 @@ async fn handle_ping_result(result: PingFuture, host: String, timeout: Duration) Some(time) => { let ms = time.as_millis(); trace!("Received pong from {} after {} ms", &host, &ms); - PING_HISTOGRAM - .with_label_values(&[&host]) - .observe(ms as f64); + histogram!("ping_rtt_milliseconds", ms as f64, "target" => host); } None => { trace!("Received no response from {} within timeout", &host); - PING_HISTOGRAM - .with_label_values(&[&host]) - .observe(timeout.as_millis() as f64); + histogram!("ping_rtt_milliseconds", timeout.as_millis() as f64, "target" => host); } };