From 1d1d76aa5b5e3b2ba715b9e64fcd8bb12dd64384 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Thu, 16 Aug 2018 10:51:03 -0700 Subject: [PATCH] Introduce the FmtMetrics and FmtLabels traits (#67) There are many different types of things that implement `fmt::Display` in the metrics library: lists of key-value labels, individial label keys or values, groups of metrics output, etc. While modifying metrics code, it can be very easy to use something that's not a label in the context of a label. --- src/telemetry/metrics/counter.rs | 12 ++- src/telemetry/metrics/gauge.rs | 12 ++- src/telemetry/metrics/histogram.rs | 33 +++---- src/telemetry/metrics/http.rs | 9 +- src/telemetry/metrics/labels.rs | 106 ++++++++++++---------- src/telemetry/metrics/mod.rs | 82 +++-------------- src/telemetry/metrics/prom.rs | 139 +++++++++++++++++++++++++++++ src/telemetry/metrics/scopes.rs | 13 +-- src/telemetry/metrics/serve.rs | 6 +- src/telemetry/metrics/transport.rs | 33 +++---- src/telemetry/mod.rs | 4 +- src/telemetry/process.rs | 20 +++-- src/telemetry/tls_config_reload.rs | 18 ++-- 13 files changed, 281 insertions(+), 206 deletions(-) create mode 100644 src/telemetry/metrics/prom.rs diff --git a/src/telemetry/metrics/counter.rs b/src/telemetry/metrics/counter.rs index 00a924a85..cbe4d9699 100644 --- a/src/telemetry/metrics/counter.rs +++ b/src/telemetry/metrics/counter.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display}; use std::num::Wrapping; use std::ops; -use super::FmtMetric; +use super::prom::{FmtMetric, FmtLabels}; /// A Prometheus counter is represented by a `Wrapping` unsigned 64-bit int. /// @@ -77,13 +77,11 @@ impl FmtMetric for Counter { fn fmt_metric_labeled(&self, f: &mut fmt::Formatter, name: N, labels: L) -> fmt::Result where - L: Display, + L: FmtLabels, N: Display, { - writeln!(f, "{name}{{{labels}}} {value}", - name = name, - labels = labels, - value = self.0, - ) + write!(f, "{}{{", name)?; + labels.fmt_labels(f)?; + writeln!(f, "}} {}", self.0) } } diff --git a/src/telemetry/metrics/gauge.rs b/src/telemetry/metrics/gauge.rs index 064b2cec9..0ae0eeb8c 100644 --- a/src/telemetry/metrics/gauge.rs +++ b/src/telemetry/metrics/gauge.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display}; -use super::FmtMetric; +use super::{FmtMetric, FmtLabels}; /// An instaneous metric value. #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] @@ -47,13 +47,11 @@ impl FmtMetric for Gauge { fn fmt_metric_labeled(&self, f: &mut fmt::Formatter, name: N, labels: L) -> fmt::Result where + L: FmtLabels, N: Display, - L: Display, { - writeln!(f, "{name}{{{labels}}} {value}", - name = name, - labels = labels, - value = self.0, - ) + write!(f, "{}{{", name)?; + labels.fmt_labels(f)?; + writeln!(f, "}} {}", self.0) } } diff --git a/src/telemetry/metrics/histogram.rs b/src/telemetry/metrics/histogram.rs index f14ae5356..1b25de116 100644 --- a/src/telemetry/metrics/histogram.rs +++ b/src/telemetry/metrics/histogram.rs @@ -1,8 +1,8 @@ use std::{cmp, iter, slice}; -use std::fmt::{self, Display}; +use std::fmt; use std::marker::PhantomData; -use super::{Counter, FmtMetric}; +use super::{Counter, FmtMetric, FmtLabels}; /// A series of latency values and counts. #[derive(Debug, Clone)] @@ -46,13 +46,10 @@ pub enum Bucket { pub struct Bounds(pub &'static [Bucket]); /// Helper that lazily formats metric keys as {0}_{1}. -struct Key(A, B); - -/// Helper that lazily formats comma-separated labels `A,B`. -struct Labels(A, B); +struct Key(A, B); /// Helper that lazily formats an `{K}="{V}"`" label. -struct Label(K, V); +struct Label(K, V); // ===== impl Histogram ===== @@ -195,7 +192,7 @@ impl<'a, V: Into> IntoIterator for &'a Histogram { impl> FmtMetric for Histogram { const KIND: &'static str = "histogram"; - fn fmt_metric(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result { + fn fmt_metric(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result { let mut total = Counter::default(); for (le, count) in self { total += *count; @@ -209,13 +206,13 @@ impl> FmtMetric for Histogram { fn fmt_metric_labeled(&self, f: &mut fmt::Formatter, name: N, labels: L) -> fmt::Result where - N: Display, - L: Display, + N: fmt::Display, + L: FmtLabels, { let mut total = Counter::default(); for (le, count) in self { total += *count; - total.fmt_metric_labeled(f, Key(&name, "bucket"), Labels(&labels, Label("le", le)))?; + total.fmt_metric_labeled(f, Key(&name, "bucket"), (&labels, Label("le", le)))?; } total.fmt_metric_labeled(f, Key(&name, "count"), &labels)?; self.sum.fmt_metric_labeled(f, Key(&name, "sum"), &labels)?; @@ -226,7 +223,7 @@ impl> FmtMetric for Histogram { // ===== impl Key ===== -impl fmt::Display for Key { +impl fmt::Display for Key { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}_{}", self.0, self.1) } @@ -234,20 +231,12 @@ impl fmt::Display for Key { // ===== impl Label ===== -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for Label { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}=\"{}\"", self.0, self.1) } } -// ===== impl Labels ===== - -impl fmt::Display for Labels { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{},{}", self.0, self.1) - } -} - // ===== impl Bucket ===== impl fmt::Display for Bucket { diff --git a/src/telemetry/metrics/http.rs b/src/telemetry/metrics/http.rs index fd4a9024c..8a07e9be9 100644 --- a/src/telemetry/metrics/http.rs +++ b/src/telemetry/metrics/http.rs @@ -3,6 +3,7 @@ use std::time::Duration; use super::{ latency, + prom::FmtMetrics, Counter, Histogram, RequestLabels, @@ -34,8 +35,8 @@ impl RequestScopes { } } -impl fmt::Display for RequestScopes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtMetrics for RequestScopes { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.is_empty() { return Ok(()); } @@ -72,8 +73,8 @@ impl ResponseScopes { } } -impl fmt::Display for ResponseScopes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtMetrics for ResponseScopes { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.is_empty() { return Ok(()); } diff --git a/src/telemetry/metrics/labels.rs b/src/telemetry/metrics/labels.rs index df35b4239..2775dbbe2 100644 --- a/src/telemetry/metrics/labels.rs +++ b/src/telemetry/metrics/labels.rs @@ -9,6 +9,7 @@ use http; use ctx; use conditional::Conditional; +use super::prom::FmtLabels; use transport::tls; #[derive(Clone, Debug, Eq, PartialEq, Hash)] @@ -23,7 +24,7 @@ pub struct RequestLabels { /// The value of the `:authority` (HTTP/2) or `Host` (HTTP/1.1) header of /// the request. - authority: Option, + authority: Authority, /// Whether or not the request was made over TLS. tls_status: TlsStatus, @@ -35,11 +36,11 @@ pub struct ResponseLabels { request_labels: RequestLabels, /// The HTTP status code of the response. - status_code: u16, + status_code: StatusCode, /// The value of the grpc-status trailer. Only applicable to response /// metrics for gRPC responses. - grpc_status_code: Option, + grpc_status: Option, /// Was the response a success or failure? classification: Classification, @@ -63,6 +64,15 @@ pub struct DstLabels { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct TlsStatus(ctx::transport::TlsStatus); +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct Authority(Option); + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +struct StatusCode(u16); + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +struct GrpcStatus(u32); + // ===== impl RequestLabels ===== impl RequestLabels { @@ -78,7 +88,7 @@ impl RequestLabels { RequestLabels { direction, outbound_labels, - authority, + authority: Authority(authority), tls_status: TlsStatus(req.tls_status()), } } @@ -89,27 +99,24 @@ impl RequestLabels { } } -impl fmt::Display for RequestLabels { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.authority { - Some(ref authority) => - write!(f, "authority=\"{}\",{}", authority, self.direction), - None => - write!(f, "authority=\"\",{}", self.direction), - }?; +impl FmtLabels for RequestLabels { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + let dst = (self.outbound_labels.as_ref(), &self.tls_status); - if let Some(ref outbound) = self.outbound_labels { - // leading comma added between the direction label and the - // destination labels, if there are destination labels. - write!(f, ",{}", outbound)?; - } - - write!(f, ",{}", self.tls_status)?; - - Ok(()) + ((&self.authority, &self.direction), dst).fmt_labels(f) } } +impl<'a> FmtLabels for Authority { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + Some(ref a) => write!(f, "authority=\"{}\"", a), + None => write!(f, "authority=\"\""), + } + } +} + + // ===== impl ResponseLabels ===== impl ResponseLabels { @@ -119,8 +126,8 @@ impl ResponseLabels { let classification = Classification::classify(rsp, grpc_status_code); ResponseLabels { request_labels, - status_code: rsp.status.as_u16(), - grpc_status_code, + status_code: StatusCode(rsp.status.as_u16()), + grpc_status: grpc_status_code.map(GrpcStatus), classification, } } @@ -132,8 +139,8 @@ impl ResponseLabels { request_labels, // TODO: is it correct to always treat this as 500? // Alternatively, the status_code field could be made optional... - status_code: 500, - grpc_status_code: None, + status_code: StatusCode(500), + grpc_status: None, classification: Classification::Failure, } } @@ -144,21 +151,23 @@ impl ResponseLabels { } } -impl fmt::Display for ResponseLabels { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{},{},status_code=\"{}\"", - self.request_labels, - self.classification, - self.status_code - )?; +impl FmtLabels for ResponseLabels { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + let status = (&self.status_code, self.grpc_status.as_ref()); + let class = (&self.classification, status); + (&self.request_labels, class).fmt_labels(f) + } +} - if let Some(ref status) = self.grpc_status_code { - // leading comma added between the status code label and the - // gRPC status code labels, if there is a gRPC status code. - write!(f, ",grpc_status_code=\"{}\"", status)?; - } +impl FmtLabels for StatusCode { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "status_code=\"{}\"", self.0) + } +} - Ok(()) +impl FmtLabels for GrpcStatus { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "grpc_status_code=\"{}\"", self.0) } } @@ -190,8 +199,8 @@ impl Classification { } } -impl fmt::Display for Classification { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for Classification { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &Classification::Success => f.pad("classification=\"success\""), &Classification::Failure => f.pad("classification=\"failure\""), @@ -207,8 +216,8 @@ impl Direction { } } -impl fmt::Display for Direction { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for Direction { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { ctx::Proxy::Inbound => f.pad("direction=\"inbound\""), ctx::Proxy::Outbound => f.pad("direction=\"outbound\""), @@ -268,16 +277,16 @@ impl hash::Hash for DstLabels { } } -impl fmt::Display for DstLabels { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.formatted.fmt(f) +impl FmtLabels for DstLabels { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.formatted, f) } } // ===== impl TlsStatus ===== -impl fmt::Display for TlsStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for TlsStatus { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { Conditional::None(tls::ReasonForNoTls::NoIdentity(why)) => write!(f, "tls=\"no_identity\",no_tls_reason=\"{}\"", why), @@ -292,12 +301,13 @@ impl From for TlsStatus { } } - +#[cfg(test)] impl Into for TlsStatus { fn into(self) -> ctx::transport::TlsStatus { self.0 } } + impl fmt::Display for tls::ReasonForNoIdentity { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/src/telemetry/metrics/mod.rs b/src/telemetry/metrics/mod.rs index f9926f2e6..39a218b92 100644 --- a/src/telemetry/metrics/mod.rs +++ b/src/telemetry/metrics/mod.rs @@ -27,8 +27,7 @@ //! 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::default::Default; -use std::fmt::{self, Display}; -use std::marker::PhantomData; +use std::fmt; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant, SystemTime}; @@ -38,6 +37,7 @@ mod histogram; mod http; mod labels; pub mod latency; +pub mod prom; mod record; mod scopes; mod serve; @@ -51,6 +51,7 @@ use self::labels::{ ResponseLabels, }; pub use self::labels::DstLabels; +pub use self::prom::{FmtMetrics, FmtLabels, FmtMetric}; pub use self::record::Record; pub use self::scopes::Scopes; pub use self::serve::Serve; @@ -58,34 +59,6 @@ pub use self::transport::Transports; use super::process; use super::tls_config_reload; -/// Writes a metric in prometheus-formatted output. -/// -/// This trait is implemented by `Counter`, `Gauge`, and `Histogram` to account for the -/// differences in formatting each type of metric. Specifically, `Histogram` formats a -/// counter for each bucket, as well as a count and total sum. -pub trait FmtMetric { - /// The metric's `TYPE` in help messages. - const KIND: &'static str; - - /// Writes a metric with the given name and no labels. - fn fmt_metric(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result; - - /// Writes a metric with the given name and labels. - fn fmt_metric_labeled(&self, f: &mut fmt::Formatter, name: N, labels: L) -> fmt::Result - where - N: Display, - L: Display; -} - -/// Describes a metric statically. -/// -/// Formats help messages and metric values for prometheus output. -pub struct Metric<'a, M: FmtMetric> { - pub name: &'a str, - pub help: &'a str, - pub _p: PhantomData, -} - /// The root scope for all runtime metrics. #[derive(Debug, Default)] struct Root { @@ -115,41 +88,6 @@ pub fn new(start_time: SystemTime, idle_retain: Duration, tls: tls_config_reload (Record::new(&metrics), Serve::new(&metrics, idle_retain)) } -// ===== impl Metric ===== - -impl<'a, M: FmtMetric> Metric<'a, M> { - /// Formats help messages for this metric. - pub fn fmt_help(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "# HELP {} {}", self.name, self.help)?; - writeln!(f, "# TYPE {} {}", self.name, M::KIND)?; - Ok(()) - } - - /// Formats a single metric without labels. - pub fn fmt_metric(&self, f: &mut fmt::Formatter, metric: M) -> fmt::Result { - metric.fmt_metric(f, self.name) - } - - /// Formats a single metric across labeled scopes. - pub fn fmt_scopes<'s, L, S: 's, I, F>( - &self, - f: &mut fmt::Formatter, - scopes: I, - to_metric: F - ) -> fmt::Result - where - L: Display, - I: IntoIterator, - F: Fn(&S) -> &M - { - for (labels, scope) in scopes { - to_metric(scope).fmt_metric_labeled(f, self.name, labels)?; - } - - Ok(()) - } -} - // ===== impl Root ===== impl Root { @@ -179,13 +117,13 @@ impl Root { } } -impl fmt::Display for Root { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.requests.fmt(f)?; - self.responses.fmt(f)?; - self.transports.fmt(f)?; - self.tls_config_reload.fmt(f)?; - self.process.fmt(f)?; +impl FmtMetrics for Root { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.requests.fmt_metrics(f)?; + self.responses.fmt_metrics(f)?; + self.transports.fmt_metrics(f)?; + self.tls_config_reload.fmt_metrics(f)?; + self.process.fmt_metrics(f)?; Ok(()) } diff --git a/src/telemetry/metrics/prom.rs b/src/telemetry/metrics/prom.rs new file mode 100644 index 000000000..9ccc5584f --- /dev/null +++ b/src/telemetry/metrics/prom.rs @@ -0,0 +1,139 @@ +use std::fmt; +use std::marker::{PhantomData, Sized}; + +/// Writes a block of metrics in prometheus-formatted output. +pub trait FmtMetrics { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result; + + fn as_display(&self) -> DisplayMetrics<&Self> where Self: Sized { + DisplayMetrics(self) + } +} + +/// Adapts `FmtMetrics` to `fmt::Display`. +pub struct DisplayMetrics(F); + +impl fmt::Display for DisplayMetrics { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt_metrics(f) + } +} + +/// Writes a series of key-quoted-val pairs for use as prometheus labels. +pub trait FmtLabels { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result; +} + +/// Writes a metric in prometheus-formatted output. +/// +/// This trait is implemented by `Counter`, `Gauge`, and `Histogram` to account for the +/// differences in formatting each type of metric. Specifically, `Histogram` formats a +/// counter for each bucket, as well as a count and total sum. +pub trait FmtMetric { + /// The metric's `TYPE` in help messages. + const KIND: &'static str; + + /// Writes a metric with the given name and no labels. + fn fmt_metric(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result; + + /// Writes a metric with the given name and labels. + fn fmt_metric_labeled(&self, f: &mut fmt::Formatter, name: N, labels: L) -> fmt::Result + where + N: fmt::Display, + L: FmtLabels; +} + +/// Describes a metric statically. +/// +/// Formats help messages and metric values for prometheus output. +pub struct Metric<'a, M: FmtMetric> { + pub name: &'a str, + pub help: &'a str, + pub _p: PhantomData, +} + +// ===== impl Metric ===== + +impl<'a, M: FmtMetric> Metric<'a, M> { + /// Formats help messages for this metric. + pub fn fmt_help(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "# HELP {} {}", self.name, self.help)?; + writeln!(f, "# TYPE {} {}", self.name, M::KIND)?; + Ok(()) + } + + /// Formats a single metric without labels. + pub fn fmt_metric(&self, f: &mut fmt::Formatter, metric: M) -> fmt::Result { + metric.fmt_metric(f, self.name) + } + + /// Formats a single metric across labeled scopes. + pub fn fmt_scopes<'s, L, S: 's, I, F>( + &self, + f: &mut fmt::Formatter, + scopes: I, + to_metric: F + ) -> fmt::Result + where + L: FmtLabels, + I: IntoIterator, + F: Fn(&S) -> &M + { + for (labels, scope) in scopes { + to_metric(scope).fmt_metric_labeled(f, self.name, labels)?; + } + + Ok(()) + } +} + +// ===== impl FmtLabels ===== + +impl<'a, A: FmtLabels + 'a> FmtLabels for &'a A { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + (*self).fmt_labels(f) + } +} + +impl FmtLabels for (A, B) { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt_labels(f)?; + f.pad(",")?; + self.1.fmt_labels(f)?; + + Ok(()) + } +} + +impl FmtLabels for (A, Option) { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt_labels(f)?; + if let Some(ref b) = self.1 { + f.pad(",")?; + b.fmt_labels(f)?; + } + + Ok(()) + } +} + +impl FmtLabels for (Option, B) { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(ref a) = self.0 { + a.fmt_labels(f)?; + f.pad(",")?; + } + self.1.fmt_labels(f)?; + + Ok(()) + } +} + +// ===== impl FmtMetrics ===== + +impl<'a, A: FmtMetrics + 'a> FmtMetrics for &'a A { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { + (*self).fmt_metrics(f) + } +} + diff --git a/src/telemetry/metrics/scopes.rs b/src/telemetry/metrics/scopes.rs index b451ab7ae..24821b914 100644 --- a/src/telemetry/metrics/scopes.rs +++ b/src/telemetry/metrics/scopes.rs @@ -1,19 +1,20 @@ use indexmap::IndexMap; -use std::{fmt::Display, hash::Hash}; +use super::prom::FmtLabels; +use std::hash::Hash; /// Holds an `S`-typed scope for each `L`-typed label set. /// /// An `S` type typically holds one or more metrics. #[derive(Debug)] -pub struct Scopes(IndexMap); +pub struct Scopes(IndexMap); -impl Default for Scopes { +impl Default for Scopes { fn default() -> Self { Scopes(IndexMap::default()) } } -impl Scopes { +impl Scopes { pub fn get(&self, key: &L) -> Option<&S> { self.0.get(key) } @@ -34,13 +35,13 @@ impl Scopes { } } -impl Scopes { +impl Scopes { pub fn get_or_default(&mut self, key: L) -> &mut S { self.0.entry(key).or_insert_with(|| S::default()) } } -impl<'a, L: Display + Hash + Eq, S> IntoIterator for &'a Scopes { +impl<'a, L: FmtLabels + Hash + Eq, S> IntoIterator for &'a Scopes { type Item = <&'a IndexMap as IntoIterator>::Item; type IntoIter = <&'a IndexMap as IntoIterator>::IntoIter; diff --git a/src/telemetry/metrics/serve.rs b/src/telemetry/metrics/serve.rs index 352ef7910..89291eedf 100644 --- a/src/telemetry/metrics/serve.rs +++ b/src/telemetry/metrics/serve.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use tokio::executor::current_thread::TaskExecutor; -use super::Root; +use super::{prom::FmtMetrics, Root}; use task; use transport::BoundPort; @@ -109,7 +109,7 @@ impl Service for Serve { let resp = if Self::is_gzip(&req) { trace!("gzipping metrics"); let mut writer = GzEncoder::new(Vec::::new(), CompressionOptions::fast()); - write!(&mut writer, "{}", *metrics) + write!(&mut writer, "{}", (*metrics).as_display()) .and_then(|_| writer.finish()) .map_err(ServeError::from) .and_then(|body| { @@ -121,7 +121,7 @@ impl Service for Serve { }) } else { let mut writer = Vec::::new(); - write!(&mut writer, "{}", *metrics) + write!(&mut writer, "{}", (*metrics).as_display()) .map_err(ServeError::from) .and_then(|_| { Response::builder() diff --git a/src/telemetry/metrics/transport.rs b/src/telemetry/metrics/transport.rs index 0f1e01061..3432e36eb 100644 --- a/src/telemetry/metrics/transport.rs +++ b/src/telemetry/metrics/transport.rs @@ -6,6 +6,7 @@ use ctx; use super::{ labels::{Direction, TlsStatus}, latency, + prom::{FmtLabels, FmtMetrics}, Counter, Gauge, Histogram, @@ -69,8 +70,6 @@ struct EosMetrics { connection_duration: Histogram, } -struct FmtEos<'a>(&'a Key, &'a Eos); - // ===== impl Transports ===== impl Transports { @@ -101,11 +100,11 @@ impl Transports { } /// Iterates over all end-of-stream metrics. - fn iter_eos(&self) -> impl Iterator { + fn iter_eos(&self) -> impl Iterator { self.metrics .iter() .flat_map(|(k, t)| { - t.by_eos.iter().map(move |(e, m)| (FmtEos(k ,e), m)) + t.by_eos.iter().map(move |(e, m)| ((k ,e), m)) }) } @@ -150,8 +149,8 @@ impl Transports { } } -impl fmt::Display for Transports { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtMetrics for Transports { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.metrics.is_empty() { return Ok(()); } @@ -180,9 +179,9 @@ impl fmt::Display for Transports { // ===== impl Key ===== -impl fmt::Display for Key { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{},{},{}", self.direction, self.peer, self.tls_status) +impl FmtLabels for Key { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { + ((self.direction, self.peer), self.tls_status).fmt_labels(f) } } @@ -199,8 +198,8 @@ impl Key { } } -impl fmt::Display for Peer { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for Peer { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Peer::Src => f.pad("peer=\"src\""), Peer::Dst => f.pad("peer=\"dst\""), @@ -211,8 +210,8 @@ impl fmt::Display for Peer { // ===== impl Eos ===== -impl fmt::Display for Eos { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for Eos { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self { Eos::Clean => f.pad("classification=\"success\""), Eos::Error { errno } => { @@ -225,11 +224,3 @@ impl fmt::Display for Eos { } } } - -// ===== impl FmtEos ===== - -impl<'a> fmt::Display for FmtEos<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{},{}", self.0, self.1) - } -} diff --git a/src/telemetry/mod.rs b/src/telemetry/mod.rs index c2173e2c3..075584f47 100644 --- a/src/telemetry/mod.rs +++ b/src/telemetry/mod.rs @@ -5,8 +5,8 @@ macro_rules! metrics { { $( $name:ident : $kind:ty { $help:expr } ),+ } => { $( #[allow(non_upper_case_globals)] - const $name: ::telemetry::metrics::Metric<'static, $kind> = - ::telemetry::metrics::Metric { + const $name: ::telemetry::metrics::prom::Metric<'static, $kind> = + ::telemetry::metrics::prom::Metric { name: stringify!($name), help: $help, _p: ::std::marker::PhantomData, diff --git a/src/telemetry/process.rs b/src/telemetry/process.rs index 44d04d89a..d9b0078d2 100644 --- a/src/telemetry/process.rs +++ b/src/telemetry/process.rs @@ -1,7 +1,7 @@ use std::fmt; use std::time::{SystemTime, UNIX_EPOCH}; -use super::metrics::Gauge; +use super::metrics::{Gauge, prom::FmtMetrics}; use self::system::System; @@ -38,13 +38,13 @@ impl Report { } } -impl fmt::Display for Report { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtMetrics for Report { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { process_start_time_seconds.fmt_help(f)?; process_start_time_seconds.fmt_metric(f, self.start_time)?; if let Some(ref sys) = self.system { - sys.fmt(f)?; + sys.fmt_metrics(f)?; } Ok(()) @@ -58,7 +58,7 @@ mod system { use std::{io, fs}; use super::*; - use super::super::metrics::{Counter, Gauge}; + use super::super::metrics::{Counter, Gauge, FmtMetrics}; metrics! { process_cpu_seconds_total: Counter { @@ -119,8 +119,8 @@ mod system { } } - impl fmt::Display for System { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + impl FmtMetrics for System { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { // XXX potentially blocking call let stat = match pid::stat_self() { Ok(stat) => stat, @@ -179,6 +179,8 @@ mod system { mod system { use std::{fmt, io}; + use super::super::metrics::FmtMetrics; + #[derive(Debug)] pub(super) struct System {} @@ -191,8 +193,8 @@ mod system { } } - impl fmt::Display for System { - fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + impl FmtMetrics for System { + fn fmt_metrics(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) } } diff --git a/src/telemetry/tls_config_reload.rs b/src/telemetry/tls_config_reload.rs index ed6b3b261..e7d07ceb3 100644 --- a/src/telemetry/tls_config_reload.rs +++ b/src/telemetry/tls_config_reload.rs @@ -5,7 +5,15 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use telemetry::{Errno, metrics::{Counter, Gauge, Scopes}}; +use telemetry::{ + metrics::{ + prom::{FmtLabels, FmtMetrics}, + Counter, + Gauge, + Scopes, + }, + Errno, +}; use transport::tls; metrics! { @@ -74,8 +82,8 @@ impl Sensor { // ===== impl Report ===== -impl fmt::Display for Report { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtMetrics for Report { + fn fmt_metrics(&self, f: &mut fmt::Formatter) -> fmt::Result { let lock = match self.0.upgrade() { None => return Ok(()), Some(lock) => lock, @@ -115,8 +123,8 @@ impl From for Status { } } -impl fmt::Display for Status { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl FmtLabels for Status { + fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Status::Reloaded => f.pad("status=\"reloaded\""), Status::Io {