Skip to content

Commit 0269c79

Browse files
committed
Merge #1574: Overhaul stats: Fix Prometheus export to include only one HELP and TYPE lines per metric
02433cb fix: [#1569] Prometheus txt export format. Only one HELP and TYPE header per metric (Jose Celano) Pull request description: Fix Prometheus export to include only one `HELP` and `TYPE` lines per metric. Current format: ``` # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (BC)",client_software_version="0087"} 4 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (FD66)",client_software_version=""} 1 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (SP)",client_software_version="3605"} 631 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (TIX0325)",client_software_version=""} 14 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (BC)",client_software_version="0202"} 6754 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (XF)",client_software_version="9400"} 1 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (BC)",client_software_version="0090"} 7 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Transmission",client_software_version="2.32"} 1 # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (61-b39e)",client_software_version=""} 1 ``` Expected format: ``` # HELP udp_tracker_server_connection_id_errors_total Total number of requests with connection ID errors # TYPE udp_tracker_server_connection_id_errors_total counter udp_tracker_server_connection_id_errors_total{client_software_name="Other (BC)",client_software_version="0087"} 4 udp_tracker_server_connection_id_errors_total{client_software_name="Other (FD66)",client_software_version=""} 1 udp_tracker_server_connection_id_errors_total{client_software_name="Other (SP)",client_software_version="3605"} 631 udp_tracker_server_connection_id_errors_total{client_software_name="Other (TIX0325)",client_software_version=""} 14 udp_tracker_server_connection_id_errors_total{client_software_name="Other (BC)",client_software_version="0202"} 6754 udp_tracker_server_connection_id_errors_total{client_software_name="Other (XF)",client_software_version="9400"} 1 udp_tracker_server_connection_id_errors_total{client_software_name="Other (BC)",client_software_version="0090"} 7 udp_tracker_server_connection_id_errors_total{client_software_name="Transmission",client_software_version="2.32"} 1 udp_tracker_server_connection_id_errors_total{client_software_name="Other (61-b39e)",client_software_version=""} 1 ``` A line break has also been added after each metric to improve readability. ACKs for top commit: josecelano: ACK 02433cb Tree-SHA512: 246c473aef425e3560d6ae99dde279aad0637315fbb6351b1ea7fa100f00b00eb6b5dee85fac141892dbb3b5d4c548fdd4b421bc6721c910fd106bc01b5670f9
2 parents d7af961 + 02433cb commit 0269c79

File tree

6 files changed

+64
-95
lines changed

6 files changed

+64
-95
lines changed

packages/metrics/src/label/set.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ impl LabelSet {
1616
pub fn upsert(&mut self, key: LabelName, value: LabelValue) {
1717
self.items.insert(key, value);
1818
}
19+
20+
pub fn is_empty(&self) -> bool {
21+
self.items.is_empty()
22+
}
1923
}
2024

2125
impl Display for LabelSet {
@@ -157,6 +161,10 @@ impl<'de> Deserialize<'de> for LabelSet {
157161

158162
impl PrometheusSerializable for LabelSet {
159163
fn to_prometheus(&self) -> String {
164+
if self.is_empty() {
165+
return String::new();
166+
}
167+
160168
let items = self.items.iter().fold(String::new(), |mut output, label_pair| {
161169
if !output.is_empty() {
162170
output.push(',');

packages/metrics/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ pub const METRICS_TARGET: &str = "METRICS";
1212

1313
#[cfg(test)]
1414
mod tests {
15-
/// It removes leading and trailing whitespace from each line, and empty lines.
15+
/// It removes leading and trailing whitespace from each line.
1616
pub fn format_prometheus_output(output: &str) -> String {
1717
output
1818
.lines()
19-
.map(str::trim)
20-
.filter(|line| !line.is_empty())
19+
.map(str::trim_start)
20+
.map(str::trim_end)
2121
.collect::<Vec<_>>()
2222
.join("\n")
2323
}

packages/metrics/src/metric/mod.rs

Lines changed: 28 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,6 @@ impl Metric<Gauge> {
103103
}
104104
}
105105

106-
/// `PrometheusMetricSample` is a wrapper around types that provides methods to
107-
/// convert the metric and its measurement into a Prometheus-compatible format.
108-
///
109-
/// In Prometheus, a metric is a time series that consists of a name, a set of
110-
/// labels, and a value. The sample value needs data from the `Metric` and
111-
/// `Measurement` structs, as well as the `LabelSet` that defines the labels for
112-
/// the metric.
113-
struct PrometheusMetricSample<'a, T> {
114-
metric: &'a Metric<T>,
115-
measurement: &'a Measurement<T>,
116-
label_set: &'a LabelSet,
117-
}
118-
119106
enum PrometheusType {
120107
Counter,
121108
Gauge,
@@ -130,91 +117,58 @@ impl PrometheusSerializable for PrometheusType {
130117
}
131118
}
132119

133-
impl<T: PrometheusSerializable> PrometheusMetricSample<'_, T> {
134-
fn to_prometheus(&self, prometheus_type: &PrometheusType) -> String {
135-
format!(
136-
// Format:
137-
// # HELP <metric_name> <description>
138-
// # TYPE <metric_name> <type>
139-
// <metric_name>{label_set} <value>
140-
"{}{}{}",
141-
self.help_line(),
142-
self.type_line(prometheus_type),
143-
self.metric_line()
144-
)
145-
}
146-
147-
fn help_line(&self) -> String {
148-
if let Some(description) = &self.metric.opt_description {
149-
format!(
150-
// Format: # HELP <metric_name> <description>
151-
"# HELP {} {}\n",
152-
self.metric.name().to_prometheus(),
153-
description.to_prometheus()
154-
)
120+
impl<T: PrometheusSerializable> Metric<T> {
121+
#[must_use]
122+
fn prometheus_help_line(&self) -> String {
123+
if let Some(description) = &self.opt_description {
124+
format!("# HELP {} {}", self.name.to_prometheus(), description.to_prometheus())
155125
} else {
156126
String::new()
157127
}
158128
}
159129

160-
fn type_line(&self, kind: &PrometheusType) -> String {
161-
format!("# TYPE {} {}\n", self.metric.name().to_prometheus(), kind.to_prometheus())
130+
#[must_use]
131+
fn prometheus_type_line(&self, prometheus_type: &PrometheusType) -> String {
132+
format!("# TYPE {} {}", self.name.to_prometheus(), prometheus_type.to_prometheus())
162133
}
163134

164-
fn metric_line(&self) -> String {
135+
#[must_use]
136+
fn prometheus_sample_line(&self, label_set: &LabelSet, measurement: &Measurement<T>) -> String {
165137
format!(
166-
// Format: <metric_name>{label_set} <value>
167138
"{}{} {}",
168-
self.metric.name.to_prometheus(),
169-
self.label_set.to_prometheus(),
170-
self.measurement.value().to_prometheus()
139+
self.name.to_prometheus(),
140+
label_set.to_prometheus(),
141+
measurement.to_prometheus()
171142
)
172143
}
173-
}
174144

175-
impl<'a> PrometheusMetricSample<'a, Counter> {
176-
pub fn new(metric: &'a Metric<Counter>, measurement: &'a Measurement<Counter>, label_set: &'a LabelSet) -> Self {
177-
Self {
178-
metric,
179-
measurement,
180-
label_set,
181-
}
145+
#[must_use]
146+
fn prometheus_samples(&self) -> String {
147+
self.sample_collection
148+
.iter()
149+
.map(|(label_set, measurement)| self.prometheus_sample_line(label_set, measurement))
150+
.collect::<Vec<_>>()
151+
.join("\n")
182152
}
183-
}
184153

185-
impl<'a> PrometheusMetricSample<'a, Gauge> {
186-
pub fn new(metric: &'a Metric<Gauge>, measurement: &'a Measurement<Gauge>, label_set: &'a LabelSet) -> Self {
187-
Self {
188-
metric,
189-
measurement,
190-
label_set,
191-
}
154+
fn to_prometheus(&self, prometheus_type: &PrometheusType) -> String {
155+
let help_line = self.prometheus_help_line();
156+
let type_line = self.prometheus_type_line(prometheus_type);
157+
let samples = self.prometheus_samples();
158+
159+
format!("{help_line}\n{type_line}\n{samples}")
192160
}
193161
}
194162

195163
impl PrometheusSerializable for Metric<Counter> {
196164
fn to_prometheus(&self) -> String {
197-
let samples: Vec<String> = self
198-
.sample_collection
199-
.iter()
200-
.map(|(label_set, measurement)| {
201-
PrometheusMetricSample::<Counter>::new(self, measurement, label_set).to_prometheus(&PrometheusType::Counter)
202-
})
203-
.collect();
204-
samples.join("\n")
165+
self.to_prometheus(&PrometheusType::Counter)
205166
}
206167
}
207168

208169
impl PrometheusSerializable for Metric<Gauge> {
209170
fn to_prometheus(&self) -> String {
210-
let samples: Vec<String> = self
211-
.sample_collection
212-
.iter()
213-
.map(|(label_set, measurement)| {
214-
PrometheusMetricSample::<Gauge>::new(self, measurement, label_set).to_prometheus(&PrometheusType::Gauge)
215-
})
216-
.collect();
217-
samples.join("\n")
171+
self.to_prometheus(&PrometheusType::Gauge)
218172
}
219173
}
220174

packages/metrics/src/metric_collection.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ impl PrometheusSerializable for MetricCollection {
322322
.map(Metric::<Gauge>::to_prometheus),
323323
)
324324
.collect::<Vec<String>>()
325-
.join("\n")
325+
.join("\n\n")
326326
}
327327
}
328328

@@ -629,14 +629,14 @@ mod tests {
629629

630630
fn prometheus() -> String {
631631
format_prometheus_output(
632-
r#"
633-
# HELP http_tracker_core_announce_requests_received_total The number of announce requests received.
634-
# TYPE http_tracker_core_announce_requests_received_total counter
635-
http_tracker_core_announce_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="7070",server_binding_protocol="http"} 1
636-
# HELP udp_tracker_server_performance_avg_announce_processing_time_ns The average announce processing time in nanoseconds.
637-
# TYPE udp_tracker_server_performance_avg_announce_processing_time_ns gauge
638-
udp_tracker_server_performance_avg_announce_processing_time_ns{server_binding_ip="0.0.0.0",server_binding_port="7070",server_binding_protocol="http"} 1
639-
"#,
632+
r#"# HELP http_tracker_core_announce_requests_received_total The number of announce requests received.
633+
# TYPE http_tracker_core_announce_requests_received_total counter
634+
http_tracker_core_announce_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="7070",server_binding_protocol="http"} 1
635+
636+
# HELP udp_tracker_server_performance_avg_announce_processing_time_ns The average announce processing time in nanoseconds.
637+
# TYPE udp_tracker_server_performance_avg_announce_processing_time_ns gauge
638+
udp_tracker_server_performance_avg_announce_processing_time_ns{server_binding_ip="0.0.0.0",server_binding_port="7070",server_binding_protocol="http"} 1
639+
"#,
640640
)
641641
}
642642
}
@@ -750,7 +750,7 @@ mod tests {
750750
MetricKindCollection::new(vec![Metric::new(
751751
metric_name!("http_tracker_core_announce_requests_received_total"),
752752
None,
753-
None,
753+
Some(MetricDescription::new("The number of announce requests received.")),
754754
SampleCollection::new(vec![
755755
Sample::new(Counter::new(1), time, label_set_1.clone()),
756756
Sample::new(Counter::new(2), time, label_set_2.clone()),
@@ -765,12 +765,11 @@ mod tests {
765765
let prometheus_output = metric_collection.to_prometheus();
766766

767767
let expected_prometheus_output = format_prometheus_output(
768-
r#"
769-
# TYPE http_tracker_core_announce_requests_received_total counter
770-
http_tracker_core_announce_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="7171",server_binding_protocol="http"} 2
771-
# TYPE http_tracker_core_announce_requests_received_total counter
772-
http_tracker_core_announce_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="7070",server_binding_protocol="http"} 1
773-
"#,
768+
r#"# HELP http_tracker_core_announce_requests_received_total The number of announce requests received.
769+
# TYPE http_tracker_core_announce_requests_received_total counter
770+
http_tracker_core_announce_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="7070",server_binding_protocol="http"} 1
771+
http_tracker_core_announce_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="7171",server_binding_protocol="http"} 2
772+
"#,
774773
);
775774

776775
// code-review: samples are not serialized in the same order as they are created.

packages/metrics/src/sample.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ impl<T> Sample<T> {
5050

5151
impl<T: PrometheusSerializable> PrometheusSerializable for Sample<T> {
5252
fn to_prometheus(&self) -> String {
53-
format!("{} {}", self.label_set.to_prometheus(), self.measurement.to_prometheus())
53+
if self.label_set.is_empty() {
54+
format!(" {}", self.measurement.to_prometheus())
55+
} else {
56+
format!("{} {}", self.label_set.to_prometheus(), self.measurement.to_prometheus())
57+
}
5458
}
5559
}
5660

packages/metrics/src/sample_collection.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,11 @@ impl<T: PrometheusSerializable> PrometheusSerializable for SampleCollection<T> {
155155
let mut output = String::new();
156156

157157
for (label_set, sample_data) in &self.samples {
158-
let _ = write!(output, "{} {}", label_set.to_prometheus(), sample_data.to_prometheus());
158+
if label_set.is_empty() {
159+
let _ = write!(output, "{}", sample_data.to_prometheus());
160+
} else {
161+
let _ = write!(output, "{} {}", label_set.to_prometheus(), sample_data.to_prometheus());
162+
}
159163
}
160164

161165
output

0 commit comments

Comments
 (0)