Skip to content

Commit

Permalink
convert histograms
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Bosquet committed Jul 16, 2024
1 parent 5819971 commit 2674eba
Showing 1 changed file with 92 additions and 29 deletions.
121 changes: 92 additions & 29 deletions src/svc/telemetry/prometheus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use urlencoding::encode;
enum MetricType {
Counter,
Gauge,
// Histogram,
Histogram,
Unsupported,
}

Expand All @@ -19,6 +19,7 @@ impl Display for MetricType {
match *self {
MetricType::Counter => write!(f, "counter"),
MetricType::Gauge => write!(f, "gauge"),
MetricType::Histogram => write!(f, "histogram"),
MetricType::Unsupported => write!(f, "unsupported"), // should never happen
}
}
Expand Down Expand Up @@ -49,36 +50,85 @@ impl LabeledMetric {
self.metric_name.replace('.', "_")
}

/// Create a type line, typically:
///
/// # TYPE protocol_https gauge
fn type_line(&self) -> String {
let printable_metric_name = self.printable_name();
format!("# TYPE {} {}", printable_metric_name, self.metric_type)
}

/// Format labels in a comma-separated list:
///
/// ```plain
/// "label"="value","other"="value"
/// ```
fn formatted_labels(&self) -> String {
self.labels
.iter()
.map(|(name, value)| format!("{}=\"{}\"", name, value))
.collect::<Vec<_>>()
.join(",")
}

/// Create a metric line, typically:
///
/// ```plain
/// http_active_requests{worker="0"} 0
/// ```
/// For histograms, several lines are produced: sum, count, buckets
fn metric_line(&self) -> String {
let value = match &self.value.inner {
let printable_metric_name = self.printable_name();
let formatted_labels = self.formatted_labels();
match &self.value.inner {
Some(inner) => {
match inner {
Inner::Gauge(value) => value.to_string(),
Inner::Count(value) => value.to_string(),
Inner::Histogram(_)
| Inner::Time(_)
| Inner::Percentiles(_)
| Inner::TimeSerie(_) => {
Inner::Gauge(value) => format!(
"{}{{{}}} {}",
printable_metric_name, formatted_labels, value
),
Inner::Count(value) => format!(
"{}{{{}}} {}",
printable_metric_name, formatted_labels, value
),
Inner::Histogram(hist) => {
let mut lines = String::new();

for bucket in &hist.buckets {
if formatted_labels.is_empty() {
lines.push_str(&format!(
"{}_bucket{{le=\"{}\"}} {}\n",
printable_metric_name, bucket.le, bucket.count
));
} else {
lines.push_str(&format!(
"{}_bucket{{{}, le=\"{}\"}} {}\n",
printable_metric_name,
formatted_labels,
bucket.le,
bucket.count
));
}
}
lines.push_str(&format!(
"{}_sum{{{}}} {}\n",
printable_metric_name, formatted_labels, hist.sum
));
lines.push_str(&format!(
"{}_count{{{}}} {}",
printable_metric_name, formatted_labels, hist.count
));

lines
}
Inner::Time(_) | Inner::Percentiles(_) | Inner::TimeSerie(_) => {
// should not happen at that point
return String::new();
String::new()
}
}
}
None => return String::new(),
};
let printable_metric_name = self.printable_name();
let formatted_labels: String = format_labels(&self.labels);
format!(
"{}{{{}}} {}",
printable_metric_name, formatted_labels, value
)
None => String::new(),
}
}
}

Expand All @@ -88,10 +138,10 @@ impl From<FilteredMetrics> for LabeledMetric {
Some(inner) => match inner {
Inner::Gauge(_) => MetricType::Gauge,
Inner::Count(_) => MetricType::Counter,
Inner::Histogram(_)
| Inner::Time(_)
| Inner::Percentiles(_)
| Inner::TimeSerie(_) => MetricType::Unsupported,
Inner::Histogram(_) => MetricType::Histogram,
Inner::Time(_) | Inner::Percentiles(_) | Inner::TimeSerie(_) => {
MetricType::Unsupported
}
},
None => MetricType::Unsupported,
};
Expand Down Expand Up @@ -219,14 +269,6 @@ fn replace_dots_with_underscores(str: &str) -> String {
str.replace('.', "_")
}

fn format_labels(labels: &[(String, String)]) -> String {
labels
.iter()
.map(|(name, value)| format!("{}=\"{}\"", name, value))
.collect::<Vec<_>>()
.join(",")
}

#[cfg(test)]
mod test {
use std::collections::BTreeMap;
Expand Down Expand Up @@ -269,4 +311,25 @@ http_response_status{cluster_id="http%3A%2F%2Fmy-cluster-id.com%2Fapi%3Fparam%3D

assert_eq!(expected.to_string(), prometheus_metrics);
}

#[test]
fn format_labels() {
let metric = FilteredMetrics {
inner: Some(Inner::Count(3)),
};
let mut labeled_metric = LabeledMetric::from(metric);

assert_eq!(labeled_metric.formatted_labels(), "");

labeled_metric.with_label("le", "3");

assert_eq!(labeled_metric.formatted_labels(), r#"le="3""#);

labeled_metric.with_label("cluster_id", "http://my-cluster-id.com/api?param=value");

assert_eq!(
labeled_metric.formatted_labels(),
r#"le="3",cluster_id="http%3A%2F%2Fmy-cluster-id.com%2Fapi%3Fparam%3Dvalue""#
)
}
}

0 comments on commit 2674eba

Please sign in to comment.