1use super::Report;
2use hdrhistogram::Histogram;
3use std::fmt;
4use std::sync::Arc;
5
6pub fn string(report: &Report) -> Result<String, fmt::Error> {
7 let mut out = String::with_capacity(8 * 1024);
8 write(&mut out, report)?;
9 Ok(out)
10}
11
12pub fn write<W>(out: &mut W, report: &Report) -> fmt::Result
14where
15 W: fmt::Write,
16{
17 for (k, v) in report.counters() {
18 let name = FmtName::new(k.prefix(), k.name());
19 write_metric(out, &name, &k.labels().into(), v)?;
20 }
21
22 for (k, v) in report.gauges() {
23 let name = FmtName::new(k.prefix(), k.name());
24 write_metric(out, &name, &k.labels().into(), v)?;
25 }
26
27 for (k, h) in report.stats() {
28 let name = FmtName::new(k.prefix(), k.name());
29 let labels = k.labels().into();
30 let count = h.count();
31 write_metric(out, &format_args!("{}_{}", name, "count"), &labels, &count)?;
32 if count > 0 {
33 write_buckets(out, &name, &labels, h.histogram())?;
34 write_metric(out, &format_args!("{}_{}", name, "min"), &labels, &h.min())?;
35 write_metric(out, &format_args!("{}_{}", name, "max"), &labels, &h.max())?;
36 write_metric(out, &format_args!("{}_{}", name, "sum"), &labels, &h.sum())?;
37 }
38 }
39
40 Ok(())
41}
42
43fn write_buckets<N, W>(out: &mut W, name: &N, labels: &FmtLabels, h: &Histogram<u64>) -> fmt::Result
44where
45 N: fmt::Display,
46 W: fmt::Write,
47{
48 let mut accum = 0u64;
54 let mut count = 0u64;
55 for bucket in h.iter_recorded() {
56 if count > 0 {
57 write_bucket(out, name, labels, &(bucket.value_iterated_to() - 1), accum)?;
58 }
59 count = bucket.count_at_value();
60 accum += count;
61 }
62 if count > 0 {
63 write_bucket(out, name, labels, &h.max(), accum)?;
65 }
66 if accum > 0 {
67 write_bucket(out, name, labels, &"+Inf", accum)?;
69 }
70 Ok(())
71}
72
73fn write_bucket<N, M, W>(
74 out: &mut W,
75 name: &N,
76 labels: &FmtLabels,
77 le: &M,
78 count: u64,
79) -> fmt::Result
80where
81 N: fmt::Display,
82 M: fmt::Display,
83 W: fmt::Write,
84{
85 write_metric(
86 out,
87 &format_args!("{}_bucket", name),
88 &labels.with_extra("le", format_args!("{}", le)),
89 &count,
90 )
91}
92
93fn write_metric<W, N, V>(out: &mut W, name: &N, labels: &FmtLabels, v: &V) -> fmt::Result
94where
95 W: fmt::Write,
96 N: fmt::Display,
97 V: fmt::Display,
98{
99 writeln!(out, "{}{} {}", name, labels, v)
100}
101
102fn write_prefix<W>(out: &mut W, prefix: Arc<super::Prefix>) -> fmt::Result
103where
104 W: fmt::Write,
105{
106 if let super::Prefix::Node { ref prefix, value } = *prefix {
107 write_prefix(out, prefix.clone())?;
108 write!(out, "{}:", value)?;
109 }
110 Ok(())
111}
112
113struct FmtName<'a> {
115 prefix: &'a Arc<super::Prefix>,
116 name: &'a str,
117}
118
119impl<'a> FmtName<'a> {
120 fn new(prefix: &'a Arc<super::Prefix>, name: &'a str) -> Self {
121 FmtName { prefix, name }
122 }
123}
124
125impl<'a> fmt::Display for FmtName<'a> {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 write_prefix(f, self.prefix.clone())?;
128 write!(f, "{}", self.name)?;
129 Ok(())
130 }
131}
132
133impl<'a> From<&'a super::Labels> for FmtLabels<'a> {
134 fn from(base: &'a super::Labels) -> Self {
135 FmtLabels { base, extra: None }
136 }
137}
138
139struct FmtLabels<'a> {
141 base: &'a super::Labels,
143 extra: Option<(&'static str, fmt::Arguments<'a>)>,
145}
146
147impl<'a> FmtLabels<'a> {
148 fn is_empty(&self) -> bool {
149 self.base.is_empty() && self.extra.is_none()
150 }
151
152 fn with_extra(&'a self, k: &'static str, v: fmt::Arguments<'a>) -> FmtLabels<'a> {
154 FmtLabels {
155 base: self.base,
156 extra: Some((k, v)),
157 }
158 }
159}
160
161impl<'a> fmt::Display for FmtLabels<'a> {
162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163 if self.is_empty() {
164 return Ok(());
165 }
166
167 let mut first = true;
168 write!(f, "{{")?;
169 if let Some((k, v)) = self.extra {
170 write!(f, "{}=\"{}\"", k, v)?;
171 first = false;
172 }
173 for (k, v) in self.base.iter() {
174 if !first {
175 write!(f, ", ")?;
176 }
177 write!(f, "{}=\"{}\"", k, v)?;
178 first = false;
179 }
180 write!(f, "}}")?;
181
182 Ok(())
183 }
184}