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.
This commit is contained in:
Oliver Gould 2018-08-16 10:51:03 -07:00 committed by GitHub
parent 6dde18cf34
commit 1d1d76aa5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 281 additions and 206 deletions

View File

@ -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<N, L>(&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)
}
}

View File

@ -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<N, L>(&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)
}
}

View File

@ -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: Display, B: Display>(A, B);
/// Helper that lazily formats comma-separated labels `A,B`.
struct Labels<A: Display, B: Display>(A, B);
struct Key<A: fmt::Display, B: fmt::Display>(A, B);
/// Helper that lazily formats an `{K}="{V}"`" label.
struct Label<K: Display, V: Display>(K, V);
struct Label<K: fmt::Display, V: fmt::Display>(K, V);
// ===== impl Histogram =====
@ -195,7 +192,7 @@ impl<'a, V: Into<u64>> IntoIterator for &'a Histogram<V> {
impl<V: Into<u64>> FmtMetric for Histogram<V> {
const KIND: &'static str = "histogram";
fn fmt_metric<N: Display>(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result {
fn fmt_metric<N: fmt::Display>(&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<V: Into<u64>> FmtMetric for Histogram<V> {
fn fmt_metric_labeled<N, L>(&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<V: Into<u64>> FmtMetric for Histogram<V> {
// ===== impl Key =====
impl<A: Display, B: Display> fmt::Display for Key<A, B> {
impl<A: fmt::Display, B: fmt::Display> fmt::Display for Key<A, B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}_{}", self.0, self.1)
}
@ -234,20 +231,12 @@ impl<A: Display, B: Display> fmt::Display for Key<A, B> {
// ===== impl Label =====
impl<K: Display, V: Display> fmt::Display for Label<K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl<K: fmt::Display, V: fmt::Display> FmtLabels for Label<K, V> {
fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}=\"{}\"", self.0, self.1)
}
}
// ===== impl Labels =====
impl<A: Display, B: Display> fmt::Display for Labels<A, B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{},{}", self.0, self.1)
}
}
// ===== impl Bucket =====
impl fmt::Display for Bucket {

View File

@ -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(());
}

View File

@ -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<http::uri::Authority>,
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<u32>,
grpc_status: Option<GrpcStatus>,
/// 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<http::uri::Authority>);
#[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<ctx::transport::TlsStatus> for TlsStatus {
}
}
#[cfg(test)]
impl Into<ctx::transport::TlsStatus> 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 {

View File

@ -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<N: Display>(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result;
/// Writes a metric with the given name and labels.
fn fmt_metric_labeled<N, L>(&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<M>,
}
/// 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<Item = (L, &'s S)>,
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(())
}

View File

@ -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>(F);
impl<F: FmtMetrics> fmt::Display for DisplayMetrics<F> {
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<N: fmt::Display>(&self, f: &mut fmt::Formatter, name: N) -> fmt::Result;
/// Writes a metric with the given name and labels.
fn fmt_metric_labeled<N, L>(&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<M>,
}
// ===== 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<Item = (L, &'s S)>,
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<A: FmtLabels, B: FmtLabels> 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<A: FmtLabels, B: FmtLabels> FmtLabels for (A, Option<B>) {
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<A: FmtLabels, B: FmtLabels> FmtLabels for (Option<A>, 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)
}
}

View File

@ -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<L: Display + Hash + Eq, S>(IndexMap<L, S>);
pub struct Scopes<L: FmtLabels + Hash + Eq, S>(IndexMap<L, S>);
impl<L: Display + Hash + Eq, S> Default for Scopes<L, S> {
impl<L: FmtLabels + Hash + Eq, S> Default for Scopes<L, S> {
fn default() -> Self {
Scopes(IndexMap::default())
}
}
impl<L: Display + Hash + Eq, S> Scopes<L, S> {
impl<L: FmtLabels + Hash + Eq, S> Scopes<L, S> {
pub fn get(&self, key: &L) -> Option<&S> {
self.0.get(key)
}
@ -34,13 +35,13 @@ impl<L: Display + Hash + Eq, S> Scopes<L, S> {
}
}
impl<L: Display + Hash + Eq, S: Default> Scopes<L, S> {
impl<L: FmtLabels + Hash + Eq, S: Default> Scopes<L, S> {
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<L, S> {
impl<'a, L: FmtLabels + Hash + Eq, S> IntoIterator for &'a Scopes<L, S> {
type Item = <&'a IndexMap<L, S> as IntoIterator>::Item;
type IntoIter = <&'a IndexMap<L, S> as IntoIterator>::IntoIter;

View File

@ -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::<u8>::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::<u8>::new();
write!(&mut writer, "{}", *metrics)
write!(&mut writer, "{}", (*metrics).as_display())
.map_err(ServeError::from)
.and_then(|_| {
Response::builder()

View File

@ -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<latency::Ms>,
}
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<Item = (FmtEos, &EosMetrics)> {
fn iter_eos(&self) -> impl Iterator<Item = ((&Key, &Eos), &EosMetrics)> {
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)
}
}

View File

@ -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,

View File

@ -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(())
}
}

View File

@ -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<tls::ConfigError> 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 {