Skip to main content

rs_zero/observability/metrics/
rpc.rs

1use std::{collections::BTreeMap, fmt::Write};
2
3use super::{DurationMetricValue, escape_label, write_duration_metric};
4
5/// Low-cardinality labels recorded for each gRPC unary call.
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
7pub struct RpcMetricLabels {
8    /// gRPC service name.
9    pub service: String,
10    /// gRPC method name.
11    pub method: String,
12    /// Tonic status code name.
13    pub code: String,
14}
15
16impl RpcMetricLabels {
17    /// Creates a label set for one gRPC call.
18    pub fn new(
19        service: impl Into<String>,
20        method: impl Into<String>,
21        code: impl Into<String>,
22    ) -> Self {
23        Self {
24            service: service.into(),
25            method: method.into(),
26            code: code.into(),
27        }
28    }
29}
30
31pub(crate) fn render(
32    output: &mut String,
33    metrics: &BTreeMap<RpcMetricLabels, DurationMetricValue>,
34) {
35    output.push_str("# HELP rs_zero_rpc_requests_total Total number of gRPC requests.\n");
36    output.push_str("# TYPE rs_zero_rpc_requests_total counter\n");
37    for (labels, value) in metrics {
38        write!(output, "rs_zero_rpc_requests_total{{").ok();
39        write_labels(output, labels, None);
40        writeln!(output, "}} {}", value.count).ok();
41    }
42
43    output.push_str("# HELP rs_zero_rpc_request_duration_seconds gRPC request duration.\n");
44    output.push_str("# TYPE rs_zero_rpc_request_duration_seconds histogram\n");
45    for (labels, value) in metrics {
46        write_duration_metric(
47            output,
48            "rs_zero_rpc_request_duration_seconds",
49            labels,
50            value,
51            write_labels,
52        );
53    }
54}
55
56fn write_labels(output: &mut String, labels: &RpcMetricLabels, le: Option<&str>) {
57    write!(
58        output,
59        "service=\"{}\",method=\"{}\",code=\"{}\"",
60        escape_label(&labels.service),
61        escape_label(&labels.method),
62        escape_label(&labels.code)
63    )
64    .ok();
65    if let Some(le) = le {
66        write!(output, ",le=\"{}\"", escape_label(le)).ok();
67    }
68}