Make telemetry::metrics application-agnostic (#82)
Now that transport details have been separated into modules, the `metrics::Root` type makes more sense as a `telemetry::Report` type. With this change, the `telemetry::metrics` module holds only the abstract structural details of metrics reporting. To this end: - `metrics::Root` is now `telemetry::Report` - `metrics::Serve` is now generic over `FmtMetrics`. It's only an implementation detail that the `telemetry::Report` type is used. - all _Report_ types now implement `Clone` so that the main report can be cloned for each connection (i.e. from prometheus).
This commit is contained in:
parent
d6d05905f1
commit
60683aeca3
|
@ -1,85 +1,16 @@
|
|||
//! Records and serves Prometheus metrics.
|
||||
//!
|
||||
//! # A note on label formatting
|
||||
//!
|
||||
//! Prometheus labels are represented as a comma-separated list of values
|
||||
//! Since the proxy labels its metrics with a fixed set of labels
|
||||
//! which we know in advance, we represent these labels using a number of
|
||||
//! `struct`s, all of which implement `fmt::Display`. Some of the label
|
||||
//! `struct`s contain other structs which represent a subset of the labels
|
||||
//! which can be present on metrics in that scope. In this case, the
|
||||
//! `fmt::Display` impls for those structs call the `fmt::Display` impls for
|
||||
//! the structs that they own. This has the potential to complicate the
|
||||
//! insertion of commas to separate label values.
|
||||
//!
|
||||
//! In order to ensure that commas are added correctly to separate labels,
|
||||
//! we expect the `fmt::Display` implementations for label types to behave in
|
||||
//! a consistent way: A label struct is *never* responsible for printing
|
||||
//! leading or trailing commas before or after the label values it contains.
|
||||
//! If it contains multiple labels, it *is* responsible for ensuring any
|
||||
//! labels it owns are comma-separated. This way, the `fmt::Display` impl for
|
||||
//! any struct that represents a subset of the labels are position-agnostic;
|
||||
//! they don't need to know if there are other labels before or after them in
|
||||
//! the formatted output. The owner is responsible for managing that.
|
||||
//!
|
||||
//! If this rule is followed consistently across all structs representing
|
||||
//! labels, we can add new labels or modify the existing ones without having
|
||||
//! to worry about missing commas, double commas, or trailing commas at the
|
||||
//! end of the label set (all of which will make Prometheus angry).
|
||||
use std::fmt;
|
||||
//! Utilties for exposing metrics to Prometheus.
|
||||
|
||||
mod counter;
|
||||
mod gauge;
|
||||
mod histogram;
|
||||
pub mod latency;
|
||||
pub mod prom;
|
||||
mod prom;
|
||||
mod scopes;
|
||||
mod serve;
|
||||
|
||||
pub use self::counter::Counter;
|
||||
pub use self::gauge::Gauge;
|
||||
pub use self::histogram::Histogram;
|
||||
pub use self::prom::{FmtMetrics, FmtLabels, FmtMetric};
|
||||
pub use self::prom::{FmtMetrics, FmtLabels, FmtMetric, Metric};
|
||||
pub use self::scopes::Scopes;
|
||||
pub use self::serve::Serve;
|
||||
use super::{http, process, tls_config_reload, transport};
|
||||
|
||||
/// The root scope for all runtime metrics.
|
||||
#[derive(Debug)]
|
||||
pub struct Root {
|
||||
http: http::Report,
|
||||
transports: transport::Report,
|
||||
tls_config_reload: tls_config_reload::Report,
|
||||
process: process::Report,
|
||||
}
|
||||
|
||||
// ===== impl Root =====
|
||||
|
||||
impl Root {
|
||||
pub(super) fn new(
|
||||
http: http::Report,
|
||||
transports: transport::Report,
|
||||
tls_config_reload: tls_config_reload::Report,
|
||||
process: process::Report,
|
||||
) -> Self {
|
||||
Self {
|
||||
http,
|
||||
transports,
|
||||
tls_config_reload,
|
||||
process,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Root =====
|
||||
|
||||
impl FmtMetrics for Root {
|
||||
fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.http.fmt_metrics(f)?;
|
||||
self.transports.fmt_metrics(f)?;
|
||||
self.tls_config_reload.fmt_metrics(f)?;
|
||||
self.process.fmt_metrics(f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,16 @@ use hyper::{
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::executor::current_thread::TaskExecutor;
|
||||
|
||||
use super::{prom::FmtMetrics, Root};
|
||||
use super::FmtMetrics;
|
||||
use task;
|
||||
use transport::BoundPort;
|
||||
|
||||
/// Serve Prometheues metrics.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Serve {
|
||||
metrics: Arc<Mutex<Root>>,
|
||||
pub struct Serve<M: FmtMetrics> {
|
||||
metrics: M,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -32,9 +31,11 @@ enum ServeError {
|
|||
|
||||
// ===== impl Serve =====
|
||||
|
||||
impl Serve {
|
||||
pub fn new(metrics: &Arc<Mutex<Root>>) -> Self {
|
||||
Self { metrics: metrics.clone() }
|
||||
impl<M: FmtMetrics> Serve<M> {
|
||||
pub fn new(metrics: M) -> Self {
|
||||
Self {
|
||||
metrics,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_gzip<B>(req: &Request<B>) -> bool {
|
||||
|
@ -46,7 +47,9 @@ impl Serve {
|
|||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: FmtMetrics + Clone + Send + 'static> Serve<M> {
|
||||
pub fn serve(self, bound_port: BoundPort) -> impl Future<Item = (), Error = ()> {
|
||||
use hyper;
|
||||
|
||||
|
@ -80,7 +83,7 @@ impl Serve {
|
|||
}
|
||||
}
|
||||
|
||||
impl Service for Serve {
|
||||
impl<M: FmtMetrics> Service for Serve<M> {
|
||||
type ReqBody = Body;
|
||||
type ResBody = Body;
|
||||
type Error = io::Error;
|
||||
|
@ -95,13 +98,10 @@ impl Service for Serve {
|
|||
return future::ok(rsp);
|
||||
}
|
||||
|
||||
let metrics = self.metrics.lock()
|
||||
.expect("metrics lock poisoned");
|
||||
|
||||
let resp = if Self::is_gzip(&req) {
|
||||
trace!("gzipping metrics");
|
||||
let mut writer = GzEncoder::new(Vec::<u8>::new(), CompressionOptions::fast());
|
||||
write!(&mut writer, "{}", (*metrics).as_display())
|
||||
write!(&mut writer, "{}", self.metrics.as_display())
|
||||
.and_then(|_| writer.finish())
|
||||
.map_err(ServeError::from)
|
||||
.and_then(|body| {
|
||||
|
@ -113,7 +113,7 @@ impl Service for Serve {
|
|||
})
|
||||
} else {
|
||||
let mut writer = Vec::<u8>::new();
|
||||
write!(&mut writer, "{}", (*metrics).as_display())
|
||||
write!(&mut writer, "{}", self.metrics.as_display())
|
||||
.map_err(ServeError::from)
|
||||
.and_then(|_| {
|
||||
Response::builder()
|
||||
|
|
|
@ -5,8 +5,8 @@ macro_rules! metrics {
|
|||
{ $( $name:ident : $kind:ty { $help:expr } ),+ } => {
|
||||
$(
|
||||
#[allow(non_upper_case_globals)]
|
||||
const $name: ::telemetry::metrics::prom::Metric<'static, $kind> =
|
||||
::telemetry::metrics::prom::Metric {
|
||||
const $name: ::telemetry::metrics::Metric<'static, $kind> =
|
||||
::telemetry::metrics::Metric {
|
||||
name: stringify!($name),
|
||||
help: $help,
|
||||
_p: ::std::marker::PhantomData,
|
||||
|
@ -19,15 +19,18 @@ mod errno;
|
|||
pub mod http;
|
||||
mod metrics;
|
||||
mod process;
|
||||
mod report;
|
||||
pub mod tap;
|
||||
pub mod tls_config_reload;
|
||||
pub mod transport;
|
||||
|
||||
use self::errno::Errno;
|
||||
pub use self::http::event::Event;
|
||||
pub use self::metrics::{Serve as ServeMetrics};
|
||||
pub use self::report::Report;
|
||||
pub use self::http::Sensors;
|
||||
|
||||
pub type ServeMetrics = metrics::Serve<Report>;
|
||||
|
||||
pub fn new(
|
||||
start_time: SystemTime,
|
||||
metrics_retain_idle: Duration,
|
||||
|
@ -36,14 +39,8 @@ pub fn new(
|
|||
let process = process::Report::new(start_time);
|
||||
let (http_sensors, http_report) = http::new(metrics_retain_idle, taps);
|
||||
let (transport_registry, transport_report) = transport::new();
|
||||
let (tls_config_sensor, tls_config_fmt) = tls_config_reload::new();
|
||||
let (tls_config_sensor, tls_config_report) = tls_config_reload::new();
|
||||
|
||||
let report = Arc::new(Mutex::new(metrics::Root::new(
|
||||
http_report,
|
||||
transport_report,
|
||||
tls_config_fmt,
|
||||
process,
|
||||
)));
|
||||
let serve = ServeMetrics::new(&report);
|
||||
(http_sensors, transport_registry, tls_config_sensor, serve)
|
||||
let report = Report::new(http_report, transport_report, tls_config_report, process);
|
||||
(http_sensors, transport_registry, tls_config_sensor, ServeMetrics::new(report))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::fmt;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use super::metrics::{Gauge, prom::FmtMetrics};
|
||||
use super::metrics::{FmtMetrics, Gauge};
|
||||
|
||||
use self::system::System;
|
||||
|
||||
|
@ -11,7 +11,7 @@ metrics! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Report {
|
||||
start_time: Gauge,
|
||||
system: Option<System>,
|
||||
|
@ -74,7 +74,7 @@ mod system {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct System {
|
||||
page_size: u64,
|
||||
clock_ticks_per_sec: u64,
|
||||
|
@ -181,7 +181,7 @@ mod system {
|
|||
|
||||
use super::super::metrics::FmtMetrics;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct System {}
|
||||
|
||||
impl System {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
use std::fmt;
|
||||
|
||||
use super::{http, process, tls_config_reload, transport};
|
||||
use super::metrics::FmtMetrics;
|
||||
|
||||
/// Implements `FmtMetrics` to report runtime metrics.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Report {
|
||||
http: http::Report,
|
||||
transports: transport::Report,
|
||||
tls_config_reload: tls_config_reload::Report,
|
||||
process: process::Report,
|
||||
}
|
||||
|
||||
// ===== impl Report =====
|
||||
|
||||
impl Report {
|
||||
pub(super) fn new(
|
||||
http: http::Report,
|
||||
transports: transport::Report,
|
||||
tls_config_reload: tls_config_reload::Report,
|
||||
process: process::Report,
|
||||
) -> Self {
|
||||
Self {
|
||||
http,
|
||||
transports,
|
||||
tls_config_reload,
|
||||
process,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Report =====
|
||||
|
||||
impl FmtMetrics for Report {
|
||||
fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.http.fmt_metrics(f)?;
|
||||
self.transports.fmt_metrics(f)?;
|
||||
self.tls_config_reload.fmt_metrics(f)?;
|
||||
self.process.fmt_metrics(f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -7,8 +7,9 @@ use std::{
|
|||
|
||||
use telemetry::{
|
||||
metrics::{
|
||||
prom::{FmtLabels, FmtMetrics},
|
||||
Counter,
|
||||
FmtLabels,
|
||||
FmtMetrics,
|
||||
Gauge,
|
||||
Scopes,
|
||||
},
|
||||
|
@ -40,7 +41,7 @@ pub fn new() -> (Sensor, Report) {
|
|||
pub struct Sensor(Arc<Mutex<Inner>>);
|
||||
|
||||
/// Formats metrics for Prometheus for a corresonding `Sensor`.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Report(Weak<Mutex<Inner>>);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
|
@ -9,10 +9,13 @@ use ctx;
|
|||
use telemetry::Errno;
|
||||
use telemetry::metrics::{
|
||||
latency,
|
||||
prom::{FmtLabels, FmtMetric, FmtMetrics, Metric},
|
||||
Counter,
|
||||
FmtLabels,
|
||||
FmtMetric,
|
||||
FmtMetrics,
|
||||
Gauge,
|
||||
Histogram,
|
||||
Metric,
|
||||
};
|
||||
use transport::Connection;
|
||||
|
||||
|
@ -36,7 +39,7 @@ pub fn new() -> (Registry, Report) {
|
|||
}
|
||||
|
||||
/// Implements `FmtMetrics` to render prometheus-formatted metrics for all transports.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Report(Arc<Mutex<Inner>>);
|
||||
|
||||
/// Instruments transports to record telemetry.
|
||||
|
|
Loading…
Reference in New Issue