Consolidate telemetry::metrics::tls_config_reload (#45)

Details of TLS configuration reload metrics span several modules.

This change consolidates all of this logic into a single
module with a single public type, `TlsConfigReload`, that supports
recording successful reloads, recording errors, and formatting its
current state.

This sets up further simplifications, eventually leading to the removal
of the `Event` API.
This commit is contained in:
Oliver Gould 2018-08-08 11:03:15 -07:00 committed by GitHub
parent 47b0f0402d
commit 9912f4577b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 122 deletions

View File

@ -2,7 +2,6 @@ use std::{
collections::HashMap,
fmt::{self, Write},
hash,
path::PathBuf,
sync::Arc,
};
@ -46,7 +45,7 @@ macro_rules! mk_err_enum {
}
mod errno;
use self::errno::Errno;
pub use self::errno::Errno;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct RequestLabels {
@ -133,15 +132,6 @@ pub struct DstLabels {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct TlsStatus(ctx::transport::TlsStatus);
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum TlsConfigLabels {
Reloaded,
InvalidTrustAnchors,
InvalidPrivateKey,
InvalidEndEntityCert,
Io { path: PathBuf, errno: Option<Errno>, },
}
// ===== impl RequestLabels =====
impl RequestLabels {
@ -461,55 +451,6 @@ impl Into<ctx::transport::TlsStatus> for TlsStatus {
self.0
}
}
// ===== impl TlsConfigLabels =====
impl TlsConfigLabels {
pub fn success() -> Self {
TlsConfigLabels::Reloaded
}
}
impl From<tls::ConfigError> for TlsConfigLabels {
fn from(err: tls::ConfigError) -> Self {
match err {
tls::ConfigError::Io(path, error_code) =>
TlsConfigLabels::Io { path, errno: error_code.map(Errno::from) },
tls::ConfigError::FailedToParseTrustAnchors(_) =>
TlsConfigLabels::InvalidTrustAnchors,
tls::ConfigError::EndEntityCertIsNotValid(_) =>
TlsConfigLabels::InvalidEndEntityCert,
tls::ConfigError::InvalidPrivateKey =>
TlsConfigLabels::InvalidPrivateKey,
}
}
}
impl fmt::Display for TlsConfigLabels {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TlsConfigLabels::Reloaded =>
f.pad("status=\"reloaded\""),
TlsConfigLabels::Io { ref path, errno: Some(errno) } =>
write!(f,
"status=\"io_error\",path=\"{}\",errno=\"{}\"",
path.display(), errno
),
TlsConfigLabels::Io { ref path, errno: None } =>
write!(f,
"status=\"io_error\",path=\"{}\",errno=\"UNKNOWN\"",
path.display(),
),
TlsConfigLabels::InvalidPrivateKey =>
f.pad("status=\"invalid_private_key\""),
TlsConfigLabels::InvalidEndEntityCert =>
f.pad("status=\"invalid_end_entity_cert\""),
TlsConfigLabels::InvalidTrustAnchors =>
f.pad("status=\"invalid_trust_anchors\""),
}
}
}
impl fmt::Display for tls::ReasonForNoIdentity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {

View File

@ -59,6 +59,7 @@ mod latency;
mod process;
mod record;
mod serve;
mod tls_config_reload;
mod transport;
use self::counter::Counter;
@ -70,12 +71,10 @@ use self::labels::{
TransportLabels,
TransportCloseLabels,
};
pub use self::labels::{
DstLabels,
TlsConfigLabels,
};
pub use self::labels::DstLabels;
pub use self::record::Record;
pub use self::serve::Serve;
pub use self::tls_config_reload::TlsConfigReload;
/// Writes a metric in prometheus-formatted output.
///
@ -112,10 +111,7 @@ struct Root {
responses: http::ResponseScopes,
transports: transport::OpenScopes,
transport_closes: transport::CloseScopes,
tls_config: TlsConfigScopes,
tls_config_last_reload_seconds: Option<Gauge>,
tls_config_reload: TlsConfigReload,
process: process::Process,
}
@ -134,8 +130,6 @@ struct Stamped<T> {
inner: T,
}
type TlsConfigScopes = Scopes<labels::TlsConfigLabels, Counter>;
/// Construct the Prometheus metrics.
///
/// Returns the `Record` and `Serve` sides. The `Serve` side
@ -180,13 +174,6 @@ impl<'a, M: FmtMetric> Metric<'a, M> {
// ===== impl Root =====
impl Root {
metrics! {
tls_config_last_reload_seconds: Gauge {
"Timestamp of when the TLS configuration files were last reloaded \
successfully (in seconds since the UNIX epoch)"
}
}
pub fn new(process: &Arc<ctx::Process>) -> Self {
Self {
process: process::Process::new(&process),
@ -218,9 +205,8 @@ impl Root {
.stamped()
}
fn tls_config(&mut self, labels: TlsConfigLabels) -> &mut Counter {
self.tls_config.scopes.entry(labels)
.or_insert_with(|| Counter::default())
fn tls_config(&mut self) -> &mut TlsConfigReload {
&mut self.tls_config_reload
}
fn retain_since(&mut self, epoch: Instant) {
@ -237,40 +223,13 @@ impl fmt::Display for Root {
self.responses.fmt(f)?;
self.transports.fmt(f)?;
self.transport_closes.fmt(f)?;
self.tls_config.fmt(f)?;
if let Some(timestamp) = self.tls_config_last_reload_seconds {
Self::tls_config_last_reload_seconds.fmt_help(f)?;
Self::tls_config_last_reload_seconds.fmt_metric(f, timestamp)?;
}
self.tls_config_reload.fmt(f)?;
self.process.fmt(f)?;
Ok(())
}
}
impl TlsConfigScopes {
metrics! {
tls_config_reload_total: Counter {
"Total number of times the proxy's TLS config files were reloaded."
}
}
}
impl fmt::Display for TlsConfigScopes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.scopes.is_empty() {
return Ok(());
}
Self::tls_config_reload_total.fmt_help(f)?;
Self::tls_config_reload_total.fmt_scopes(f, &self, |s| &s)?;
Ok(())
}
}
// ===== impl Stamped =====
impl<T> Stamped<T> {

View File

@ -7,9 +7,7 @@ use super::labels::{
ResponseLabels,
TransportLabels,
TransportCloseLabels,
TlsConfigLabels,
};
use std::time::UNIX_EPOCH;
/// Tracks Prometheus metrics
#[derive(Clone, Debug)]
@ -86,20 +84,10 @@ impl Record {
},
Event::TlsConfigReloaded(ref when) =>
self.update(|metrics| {
let timestamp_secs = when
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX_EPOCH!")
.as_secs()
.into();
metrics.tls_config_last_reload_seconds = Some(timestamp_secs);
metrics.tls_config(TlsConfigLabels::success()).incr();
}),
self.update(|m| m.tls_config().success(when)),
Event::TlsConfigReloadFailed(ref err) =>
self.update(|metrics| {
metrics.tls_config(err.clone().into()).incr();
}),
self.update(|m| m.tls_config().error(err.clone())),
};
}
}

View File

@ -0,0 +1,112 @@
use std::{fmt, path::PathBuf, time::{SystemTime, UNIX_EPOCH}};
use telemetry::metrics::{Counter, Gauge, Metric, Scopes, labels::Errno};
use transport::tls;
metrics! {
tls_config_last_reload_seconds: Gauge {
"Timestamp of the last successful TLS configuration reload \
(in seconds since the UNIX epoch)"
},
tls_config_reload_total: Counter {
"Total number of TLS configuration reloads"
}
}
#[derive(Debug, Default)]
pub struct TlsConfigReload {
last_reload: Option<Gauge>,
by_status: Scopes<Status, Counter>,
}
#[derive(Debug, Eq, PartialEq, Hash)]
enum Status {
Reloaded,
InvalidTrustAnchors,
InvalidPrivateKey,
InvalidEndEntityCert,
Io { path: PathBuf, errno: Option<Errno>, },
}
// ===== impl TlsConfigReload =====
impl TlsConfigReload {
pub fn success(&mut self, when: &SystemTime) {
let t = when
.duration_since(UNIX_EPOCH)
.expect("times must be after UNIX epoch")
.as_secs();
self.last_reload = Some(t.into());
self.by_status.scopes
.entry(Status::Reloaded)
.or_insert_with(|| Counter::default())
.incr()
}
pub fn error(&mut self, e: tls::ConfigError) {
self.by_status.scopes
.entry(e.into())
.or_insert_with(|| Counter::default())
.incr()
}
}
impl fmt::Display for TlsConfigReload {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.by_status.scopes.is_empty() {
tls_config_reload_total.fmt_help(f)?;
tls_config_reload_total.fmt_scopes(f, &self.by_status, |s| &s)?;
}
if let Some(timestamp) = self.last_reload {
tls_config_last_reload_seconds.fmt_help(f)?;
tls_config_last_reload_seconds.fmt_metric(f, timestamp)?;
}
Ok(())
}
}
// ===== impl Status =====
impl From<tls::ConfigError> for Status {
fn from(err: tls::ConfigError) -> Self {
match err {
tls::ConfigError::Io(path, error_code) =>
Status::Io { path, errno: error_code.map(Errno::from) },
tls::ConfigError::FailedToParseTrustAnchors(_) =>
Status::InvalidTrustAnchors,
tls::ConfigError::EndEntityCertIsNotValid(_) =>
Status::InvalidEndEntityCert,
tls::ConfigError::InvalidPrivateKey =>
Status::InvalidPrivateKey,
}
}
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Status::Reloaded =>
f.pad("status=\"reloaded\""),
Status::Io { ref path, errno: Some(errno) } =>
write!(f,
"status=\"io_error\",path=\"{}\",errno=\"{}\"",
path.display(), errno
),
Status::Io { ref path, errno: None } =>
write!(f,
"status=\"io_error\",path=\"{}\",errno=\"UNKNOWN\"",
path.display(),
),
Status::InvalidPrivateKey =>
f.pad("status=\"invalid_private_key\""),
Status::InvalidEndEntityCert =>
f.pad("status=\"invalid_end_entity_cert\""),
Status::InvalidTrustAnchors =>
f.pad("status=\"invalid_trust_anchors\""),
}
}
}