prometheus/encoder/
mod.rs

1// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3#[cfg(feature = "protobuf")]
4mod pb;
5mod text;
6
7#[cfg(feature = "protobuf")]
8pub use self::pb::{ProtobufEncoder, PROTOBUF_FORMAT};
9pub use self::text::{TextEncoder, TEXT_FORMAT};
10
11use std::io::Write;
12
13use crate::errors::{Error, Result};
14use crate::proto::MetricFamily;
15
16/// An interface for encoding metric families into an underlying wire protocol.
17pub trait Encoder {
18    /// `encode` converts a slice of MetricFamily proto messages into target
19    /// format and writes the resulting lines to `writer`. This function does not
20    /// perform checks on the content of the metrics and label names,
21    /// i.e. invalid metrics or label names will result in invalid text format
22    /// output.
23    fn encode<W: Write>(&self, mfs: &[MetricFamily], writer: &mut W) -> Result<()>;
24
25    /// `format_type` returns target format.
26    fn format_type(&self) -> &str;
27}
28
29fn check_metric_family(mf: &MetricFamily) -> Result<()> {
30    if mf.get_metric().is_empty() {
31        return Err(Error::Msg(format!("MetricFamily has no metrics: {:?}", mf)));
32    }
33    if mf.get_name().is_empty() {
34        return Err(Error::Msg(format!("MetricFamily has no name: {:?}", mf)));
35    }
36    Ok(())
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::counter::CounterVec;
43    use crate::encoder::Encoder;
44    use crate::metrics::Collector;
45    use crate::metrics::Opts;
46
47    #[test]
48    #[cfg(feature = "protobuf")]
49    fn test_bad_proto_metrics() {
50        let mut writer = Vec::<u8>::new();
51        let pb_encoder = ProtobufEncoder::new();
52        let cv = CounterVec::new(
53            Opts::new("test_counter_vec", "help information"),
54            &["labelname"],
55        )
56        .unwrap();
57
58        // Empty metrics
59        let mfs = cv.collect();
60        check_metric_family(&mfs[0]).unwrap_err();
61        pb_encoder.encode(&mfs, &mut writer).unwrap_err();
62        assert_eq!(writer.len(), 0);
63
64        // Add a sub metric
65        cv.with_label_values(&["foo"]).inc();
66        let mut mfs = cv.collect();
67
68        // Empty name
69        (&mut mfs[0]).clear_name();
70        check_metric_family(&mfs[0]).unwrap_err();
71        pb_encoder.encode(&mfs, &mut writer).unwrap_err();
72        assert_eq!(writer.len(), 0);
73    }
74
75    #[test]
76    fn test_bad_text_metrics() {
77        let mut writer = Vec::<u8>::new();
78        let text_encoder = TextEncoder::new();
79        let cv = CounterVec::new(
80            Opts::new("test_counter_vec", "help information"),
81            &["labelname"],
82        )
83        .unwrap();
84
85        // Empty metrics
86        let mfs = cv.collect();
87        check_metric_family(&mfs[0]).unwrap_err();
88        text_encoder.encode(&mfs, &mut writer).unwrap_err();
89        assert_eq!(writer.len(), 0);
90
91        // Add a sub metric
92        cv.with_label_values(&["foo"]).inc();
93        let mut mfs = cv.collect();
94
95        // Empty name
96        (&mut mfs[0]).clear_name();
97        check_metric_family(&mfs[0]).unwrap_err();
98        text_encoder.encode(&mfs, &mut writer).unwrap_err();
99        assert_eq!(writer.len(), 0);
100    }
101}