metrics_observer_yaml/
lib.rs1#![deny(missing_docs)]
43use hdrhistogram::Histogram;
44use metrics_core::{Builder, Drain, Key, Label, Observer};
45use metrics_util::{parse_quantiles, MetricsTree, Quantile};
46use std::collections::HashMap;
47
48pub struct YamlBuilder {
50 quantiles: Vec<Quantile>,
51}
52
53impl YamlBuilder {
54 pub fn new() -> Self {
56 let quantiles = parse_quantiles(&[0.0, 0.5, 0.9, 0.95, 0.99, 0.999, 1.0]);
57
58 Self { quantiles }
59 }
60
61 pub fn set_quantiles(mut self, quantiles: &[f64]) -> Self {
68 self.quantiles = parse_quantiles(quantiles);
69 self
70 }
71}
72
73impl Builder for YamlBuilder {
74 type Output = YamlObserver;
75
76 fn build(&self) -> Self::Output {
77 YamlObserver {
78 quantiles: self.quantiles.clone(),
79 tree: MetricsTree::default(),
80 histos: HashMap::new(),
81 }
82 }
83}
84
85impl Default for YamlBuilder {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91pub struct YamlObserver {
93 pub(crate) quantiles: Vec<Quantile>,
94 pub(crate) tree: MetricsTree,
95 pub(crate) histos: HashMap<Key, Histogram<u64>>,
96}
97
98impl Observer for YamlObserver {
99 fn observe_counter(&mut self, key: Key, value: u64) {
100 let (levels, name) = key_to_parts(key);
101 self.tree.insert_value(levels, name, value);
102 }
103
104 fn observe_gauge(&mut self, key: Key, value: i64) {
105 let (levels, name) = key_to_parts(key);
106 self.tree.insert_value(levels, name, value);
107 }
108
109 fn observe_histogram(&mut self, key: Key, values: &[u64]) {
110 let entry = self
111 .histos
112 .entry(key)
113 .or_insert_with(|| Histogram::<u64>::new(3).expect("failed to create histogram"));
114
115 for value in values {
116 entry
117 .record(*value)
118 .expect("failed to observe histogram value");
119 }
120 }
121}
122
123impl Drain<String> for YamlObserver {
124 fn drain(&mut self) -> String {
125 for (key, h) in self.histos.drain() {
126 let (levels, name) = key_to_parts(key);
127 let values = hist_to_values(name, h.clone(), &self.quantiles);
128 self.tree.insert_values(levels, values);
129 }
130
131 let rendered = serde_yaml::to_string(&self.tree).expect("failed to render yaml output");
132 self.tree.clear();
133 rendered
134 }
135}
136
137fn key_to_parts(key: Key) -> (Vec<String>, String) {
138 let (name, labels) = key.into_parts();
139 let mut parts = name.split('.').map(ToOwned::to_owned).collect::<Vec<_>>();
140 let name = parts.pop().expect("name didn't have a single part");
141
142 let labels = labels
143 .into_iter()
144 .map(Label::into_parts)
145 .map(|(k, v)| format!("{}=\"{}\"", k, v))
146 .collect::<Vec<_>>()
147 .join(",");
148 let label = if labels.is_empty() {
149 String::new()
150 } else {
151 format!("{{{}}}", labels)
152 };
153
154 let fname = format!("{}{}", name, label);
155
156 (parts, fname)
157}
158
159fn hist_to_values(
160 name: String,
161 hist: Histogram<u64>,
162 quantiles: &[Quantile],
163) -> Vec<(String, u64)> {
164 let mut values = Vec::new();
165
166 values.push((format!("{} count", name), hist.len()));
167 for quantile in quantiles {
168 let value = hist.value_at_quantile(quantile.value());
169 values.push((format!("{} {}", name, quantile.label()), value));
170 }
171
172 values
173}