use super::Report;
use hdrsample::Histogram;
use std::fmt;
use std::sync::Arc;
pub fn string(report: &Report) -> Result<String, fmt::Error> {
let mut out = String::with_capacity(8 * 1024);
write(&mut out, report)?;
Ok(out)
}
pub fn write<W>(out: &mut W, report: &Report) -> fmt::Result
where
W: fmt::Write,
{
for (k, v) in report.counters() {
let name = FmtName::new(k.prefix(), k.name());
write_metric(out, &name, &k.labels().into(), v)?;
}
for (k, v) in report.gauges() {
let name = FmtName::new(k.prefix(), k.name());
write_metric(out, &name, &k.labels().into(), v)?;
}
for (k, h) in report.stats() {
let name = FmtName::new(k.prefix(), k.name());
let labels = k.labels().into();
let count = h.count();
write_metric(out, &format_args!("{}_{}", name, "count"), &labels, &count)?;
if count > 0 {
write_buckets(out, &name, &labels, h.histogram())?;
write_metric(out, &format_args!("{}_{}", name, "min"), &labels, &h.min())?;
write_metric(out, &format_args!("{}_{}", name, "max"), &labels, &h.max())?;
write_metric(out, &format_args!("{}_{}", name, "sum"), &labels, &h.sum())?;
}
}
Ok(())
}
fn write_buckets<N, W>(
out: &mut W,
name: &N,
labels: &FmtLabels,
h: &Histogram<usize>,
) -> fmt::Result
where
N: fmt::Display,
W: fmt::Write,
{
let mut accum = 0;
let mut count = 0;
for bucket in h.iter_recorded() {
if count > 0 {
write_bucket(out, name, labels, &(bucket.value() - 1), accum)?;
}
count = bucket.count_at_value();
accum += count;
}
if count > 0 {
write_bucket(out, name, labels, &h.max(), accum)?;
}
if accum > 0 {
write_bucket(out, name, labels, &"+Inf", accum)?;
}
Ok(())
}
fn write_bucket<N, M, W>(
out: &mut W,
name: &N,
labels: &FmtLabels,
le: &M,
count: usize,
) -> fmt::Result
where
N: fmt::Display,
M: fmt::Display,
W: fmt::Write,
{
write_metric(
out,
&format_args!("{}_bucket", name),
&labels.with_extra("le", format_args!("{}", le)),
&count,
)
}
fn write_metric<W, N, V>(out: &mut W, name: &N, labels: &FmtLabels, v: &V) -> fmt::Result
where
W: fmt::Write,
N: fmt::Display,
V: fmt::Display,
{
writeln!(out, "{}{} {}", name, labels, v)
}
fn write_prefix<W>(out: &mut W, prefix: Arc<super::Prefix>) -> fmt::Result
where
W: fmt::Write,
{
if let super::Prefix::Node { ref prefix, value } = *prefix {
write_prefix(out, prefix.clone())?;
write!(out, "{}:", value)?;
}
Ok(())
}
struct FmtName<'a> {
prefix: &'a Arc<super::Prefix>,
name: &'a str,
}
impl<'a> FmtName<'a> {
fn new(prefix: &'a Arc<super::Prefix>, name: &'a str) -> Self {
FmtName { prefix, name }
}
}
impl<'a> fmt::Display for FmtName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write_prefix(f, self.prefix.clone())?;
write!(f, "{}", self.name)?;
Ok(())
}
}
impl<'a> From<&'a super::Labels> for FmtLabels<'a> {
fn from(base: &'a super::Labels) -> Self {
FmtLabels { base, extra: None }
}
}
struct FmtLabels<'a> {
base: &'a super::Labels,
extra: Option<(&'static str, fmt::Arguments<'a>)>,
}
impl<'a> FmtLabels<'a> {
fn is_empty(&self) -> bool {
self.base.is_empty() && self.extra.is_none()
}
fn with_extra(&'a self, k: &'static str, v: fmt::Arguments<'a>) -> FmtLabels<'a> {
FmtLabels {
base: self.base,
extra: Some((k, v)),
}
}
}
impl<'a> fmt::Display for FmtLabels<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_empty() {
return Ok(());
}
let mut first = true;
write!(f, "{{")?;
if let Some((k, v)) = self.extra {
write!(f, "{}=\"{}\"", k, v)?;
first = false;
}
for (k, v) in self.base.iter() {
if !first {
write!(f, ", ")?;
}
write!(f, "{}=\"{}\"", k, v)?;
first = false;
}
write!(f, "}}")?;
Ok(())
}
}