Skip to content

Commit 0e3a305

Browse files
committed
Merge #1559: Overhaul stats: add TYPE and HELP to Prometheus export
bb2392d test: [#1514] add tests to metrics package (Jose Celano) 507b480 fix: [#1514] bug. Don't allow merge metric collections with the same metrin name (Jose Celano) 376f242 test: [#1514] add tests to metrics package (Jose Celano) 642d774 fix: [#1514] HELP line in Prometheus export must contain metric name (Jose Celano) 748e6a5 test: [#1514] add tests to metrics package (Jose Celano) a89406d refactor: [#1514] remove duplicate code in Metric type (Jose Celano) ed1322b refactor: [#1514] reorganize SampleCollection tests (Jose Celano) 842739f feat: [#1514] add HELP and TYPE to prometehous metric export (Jose Celano) 458497b feat: [#1514] add unit and description to metrics (Jose Celano) 031bf65 refactor: [#1514] remove unused code (Jose Celano) 2ee3111 refactor: [#1514] ensure_metric_exists method to pass the whole metric (Jose Celano) 552697b feat!: [#1514] rename ffield kind to type in JSON metrics (Jose Celano) Pull request description: Add metadata describing the metric to the Prometheus export format. For example: ``` # HELP udp_tracker_server_requests_received_total Total number of UDP requests received # TYPE udp_tracker_server_requests_received_total counter udp_tracker_server_requests_received_total{server_binding_ip="0.0.0.0",server_binding_port="6868",server_binding_protocol="udp"} 36661 ``` ### Subtasks - [x] Rename `kind` JSON field to `type` to use the same name in JSON and Prometheus export formats. - [x] Change `ensure_metric_exists` to pass the whole metric, not only the name. - [x] Add metadata fields to generic metric: `opt_unit: Option<Unit>` and `opt_description: Option<&MetricDescription>` to `Metric<T>` - [x] Change JSON serialization to include the metric description (`description`). - [x] Change Prometheus serialization to include the metric type (`TYPE`) and description (`HELP`). - [x] Review test coverage. ACKs for top commit: josecelano: ACK bb2392d Tree-SHA512: 8778d5b65b027a88441f4746926ade66a7959ca4818de1a52c54b018f302c125633491ca2e7f304ed429c501bee300cd5be2e69f19dd6b6866852050e0e4511f
2 parents 33e69ff + bb2392d commit 0e3a305

File tree

12 files changed

+843
-223
lines changed

12 files changed

+843
-223
lines changed

packages/http-tracker-core/src/statistics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn describe_metrics() -> Metrics {
1717
metrics.metric_collection.describe_counter(
1818
&metric_name!(HTTP_TRACKER_CORE_REQUESTS_RECEIVED_TOTAL),
1919
Some(Unit::Count),
20-
Some(&MetricDescription::new("Total number of HTTP requests received")),
20+
Some(MetricDescription::new("Total number of HTTP requests received")),
2121
);
2222

2323
metrics

packages/metrics/src/label/set.rs

Lines changed: 152 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ impl PrometheusSerializable for LabelSet {
175175
mod tests {
176176

177177
use std::collections::BTreeMap;
178+
use std::hash::{DefaultHasher, Hash};
178179

179180
use pretty_assertions::assert_eq;
180181

@@ -195,54 +196,6 @@ mod tests {
195196
]
196197
}
197198

198-
#[test]
199-
fn it_should_allow_instantiation_from_an_array_of_label_pairs() {
200-
let label_set: LabelSet = sample_array_of_label_pairs().into();
201-
202-
assert_eq!(
203-
label_set,
204-
LabelSet {
205-
items: BTreeMap::from(sample_array_of_label_pairs())
206-
}
207-
);
208-
}
209-
210-
#[test]
211-
fn it_should_allow_instantiation_from_a_vec_of_label_pairs() {
212-
let label_set: LabelSet = sample_vec_of_label_pairs().into();
213-
214-
assert_eq!(
215-
label_set,
216-
LabelSet {
217-
items: BTreeMap::from(sample_array_of_label_pairs())
218-
}
219-
);
220-
}
221-
222-
#[test]
223-
fn it_should_allow_instantiation_from_a_b_tree_map() {
224-
let label_set: LabelSet = BTreeMap::from(sample_array_of_label_pairs()).into();
225-
226-
assert_eq!(
227-
label_set,
228-
LabelSet {
229-
items: BTreeMap::from(sample_array_of_label_pairs())
230-
}
231-
);
232-
}
233-
234-
#[test]
235-
fn it_should_allow_instantiation_from_a_label_pair() {
236-
let label_set: LabelSet = (label_name!("label_name"), LabelValue::new("value")).into();
237-
238-
assert_eq!(
239-
label_set,
240-
LabelSet {
241-
items: BTreeMap::from([(label_name!("label_name"), LabelValue::new("value"))])
242-
}
243-
);
244-
}
245-
246199
#[test]
247200
fn it_should_allow_inserting_a_new_label_pair() {
248201
let mut label_set = LabelSet::default();
@@ -338,4 +291,155 @@ mod tests {
338291

339292
assert_eq!(label_set.to_string(), r#"{label_name="label value"}"#);
340293
}
294+
295+
#[test]
296+
fn it_should_allow_instantiation_from_an_array_of_label_pairs() {
297+
let label_set: LabelSet = sample_array_of_label_pairs().into();
298+
299+
assert_eq!(
300+
label_set,
301+
LabelSet {
302+
items: BTreeMap::from(sample_array_of_label_pairs())
303+
}
304+
);
305+
}
306+
307+
#[test]
308+
fn it_should_allow_instantiation_from_a_vec_of_label_pairs() {
309+
let label_set: LabelSet = sample_vec_of_label_pairs().into();
310+
311+
assert_eq!(
312+
label_set,
313+
LabelSet {
314+
items: BTreeMap::from(sample_array_of_label_pairs())
315+
}
316+
);
317+
}
318+
319+
#[test]
320+
fn it_should_allow_instantiation_from_a_b_tree_map() {
321+
let label_set: LabelSet = BTreeMap::from(sample_array_of_label_pairs()).into();
322+
323+
assert_eq!(
324+
label_set,
325+
LabelSet {
326+
items: BTreeMap::from(sample_array_of_label_pairs())
327+
}
328+
);
329+
}
330+
331+
#[test]
332+
fn it_should_allow_instantiation_from_a_label_pair() {
333+
let label_set: LabelSet = (label_name!("label_name"), LabelValue::new("value")).into();
334+
335+
assert_eq!(
336+
label_set,
337+
LabelSet {
338+
items: BTreeMap::from([(label_name!("label_name"), LabelValue::new("value"))])
339+
}
340+
);
341+
}
342+
343+
#[test]
344+
fn it_should_allow_instantiation_from_vec_of_str_tuples() {
345+
let label_set: LabelSet = vec![("foo", "bar"), ("baz", "qux")].into();
346+
347+
let mut expected = BTreeMap::new();
348+
expected.insert(LabelName::new("foo"), LabelValue::new("bar"));
349+
expected.insert(LabelName::new("baz"), LabelValue::new("qux"));
350+
351+
assert_eq!(label_set, LabelSet { items: expected });
352+
}
353+
354+
#[test]
355+
fn it_should_allow_instantiation_from_vec_of_string_tuples() {
356+
let label_set: LabelSet = vec![("foo".to_string(), "bar".to_string()), ("baz".to_string(), "qux".to_string())].into();
357+
358+
let mut expected = BTreeMap::new();
359+
expected.insert(LabelName::new("foo"), LabelValue::new("bar"));
360+
expected.insert(LabelName::new("baz"), LabelValue::new("qux"));
361+
362+
assert_eq!(label_set, LabelSet { items: expected });
363+
}
364+
365+
#[test]
366+
fn it_should_allow_instantiation_from_vec_of_serialized_label() {
367+
use super::SerializedLabel;
368+
let label_set: LabelSet = vec![
369+
SerializedLabel {
370+
name: LabelName::new("foo"),
371+
value: LabelValue::new("bar"),
372+
},
373+
SerializedLabel {
374+
name: LabelName::new("baz"),
375+
value: LabelValue::new("qux"),
376+
},
377+
]
378+
.into();
379+
380+
let mut expected = BTreeMap::new();
381+
expected.insert(LabelName::new("foo"), LabelValue::new("bar"));
382+
expected.insert(LabelName::new("baz"), LabelValue::new("qux"));
383+
384+
assert_eq!(label_set, LabelSet { items: expected });
385+
}
386+
387+
#[test]
388+
fn it_should_allow_instantiation_from_array_of_string_tuples() {
389+
let arr: [(String, String); 2] = [("foo".to_string(), "bar".to_string()), ("baz".to_string(), "qux".to_string())];
390+
let label_set: LabelSet = arr.into();
391+
392+
let mut expected = BTreeMap::new();
393+
394+
expected.insert(LabelName::new("foo"), LabelValue::new("bar"));
395+
expected.insert(LabelName::new("baz"), LabelValue::new("qux"));
396+
397+
assert_eq!(label_set, LabelSet { items: expected });
398+
}
399+
400+
#[test]
401+
fn it_should_allow_instantiation_from_array_of_str_tuples() {
402+
let arr: [(&str, &str); 2] = [("foo", "bar"), ("baz", "qux")];
403+
let label_set: LabelSet = arr.into();
404+
405+
let mut expected = BTreeMap::new();
406+
407+
expected.insert(LabelName::new("foo"), LabelValue::new("bar"));
408+
expected.insert(LabelName::new("baz"), LabelValue::new("qux"));
409+
410+
assert_eq!(label_set, LabelSet { items: expected });
411+
}
412+
413+
#[test]
414+
fn it_should_be_comparable() {
415+
let a: LabelSet = (label_name!("x"), LabelValue::new("1")).into();
416+
let b: LabelSet = (label_name!("x"), LabelValue::new("1")).into();
417+
let c: LabelSet = (label_name!("y"), LabelValue::new("2")).into();
418+
419+
assert_eq!(a, b);
420+
assert_ne!(a, c);
421+
}
422+
423+
#[test]
424+
fn it_should_be_allow_ordering() {
425+
let a: LabelSet = (label_name!("x"), LabelValue::new("1")).into();
426+
let b: LabelSet = (label_name!("y"), LabelValue::new("2")).into();
427+
428+
assert!(a < b);
429+
}
430+
431+
#[test]
432+
fn it_should_be_hashable() {
433+
let a: LabelSet = (label_name!("x"), LabelValue::new("1")).into();
434+
435+
let mut hasher = DefaultHasher::new();
436+
437+
a.hash(&mut hasher);
438+
}
439+
440+
#[test]
441+
fn it_should_implement_clone() {
442+
let a: LabelSet = (label_name!("x"), LabelValue::new("1")).into();
443+
let _unused = a.clone();
444+
}
341445
}

packages/metrics/src/label/value.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ impl From<String> for LabelValue {
3333

3434
#[cfg(test)]
3535
mod tests {
36+
use std::collections::hash_map::DefaultHasher;
37+
use std::hash::Hash;
38+
3639
use crate::label::value::LabelValue;
3740
use crate::prometheus::PrometheusSerializable;
3841

@@ -41,4 +44,60 @@ mod tests {
4144
let label_value = LabelValue::new("value");
4245
assert_eq!(label_value.to_prometheus(), "value");
4346
}
47+
48+
#[test]
49+
fn it_could_be_initialized_from_str() {
50+
let lv = LabelValue::new("abc");
51+
assert_eq!(lv.0, "abc");
52+
}
53+
54+
#[test]
55+
fn it_should_allow_to_create_an_ignored_label_value() {
56+
let lv = LabelValue::ignore();
57+
assert_eq!(lv.0, "");
58+
}
59+
60+
#[test]
61+
fn it_should_be_converted_from_string() {
62+
let s = String::from("foo");
63+
let lv: LabelValue = s.clone().into();
64+
assert_eq!(lv.0, s);
65+
}
66+
67+
#[test]
68+
fn it_should_be_comparable() {
69+
let a = LabelValue::new("x");
70+
let b = LabelValue::new("x");
71+
let c = LabelValue::new("y");
72+
73+
assert_eq!(a, b);
74+
assert_ne!(a, c);
75+
}
76+
77+
#[test]
78+
fn it_should_be_allow_ordering() {
79+
let a = LabelValue::new("x");
80+
let b = LabelValue::new("y");
81+
82+
assert!(a < b);
83+
}
84+
85+
#[test]
86+
fn it_should_be_hashable() {
87+
let a = LabelValue::new("x");
88+
let mut hasher = DefaultHasher::new();
89+
a.hash(&mut hasher);
90+
}
91+
92+
#[test]
93+
fn it_should_implement_clone() {
94+
let a = LabelValue::new("x");
95+
let _unused = a.clone();
96+
}
97+
98+
#[test]
99+
fn it_should_implement_display() {
100+
let a = LabelValue::new("x");
101+
assert_eq!(format!("{a}"), "x");
102+
}
44103
}

packages/metrics/src/metric/description.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use derive_more::Display;
22
use serde::{Deserialize, Serialize};
33

4+
use crate::prometheus::PrometheusSerializable;
5+
46
#[derive(Debug, Display, Clone, Eq, PartialEq, Default, Deserialize, Serialize, Hash, Ord, PartialOrd)]
57
pub struct MetricDescription(String);
68

@@ -11,6 +13,11 @@ impl MetricDescription {
1113
}
1214
}
1315

16+
impl PrometheusSerializable for MetricDescription {
17+
fn to_prometheus(&self) -> String {
18+
self.0.clone()
19+
}
20+
}
1421
#[cfg(test)]
1522
mod tests {
1623
use super::*;
@@ -21,6 +28,12 @@ mod tests {
2128
assert_eq!(metric.0, "Metric description");
2229
}
2330

31+
#[test]
32+
fn it_serializes_to_prometheus() {
33+
let label_value = MetricDescription::new("name");
34+
assert_eq!(label_value.to_prometheus(), "name");
35+
}
36+
2437
#[test]
2538
fn it_should_be_displayed() {
2639
let metric = MetricDescription::new("Metric description");

0 commit comments

Comments
 (0)