2018-10-09 20:46:15 +00:00
|
|
|
use core::name::Name;
|
|
|
|
use core::input::Kind;
|
|
|
|
use core::Value;
|
2018-10-18 13:31:53 +00:00
|
|
|
use self::LineOp::*;
|
2018-10-09 20:46:15 +00:00
|
|
|
|
|
|
|
use std::io;
|
2018-10-12 20:44:39 +00:00
|
|
|
use std::sync::Arc;
|
2018-10-09 20:46:15 +00:00
|
|
|
|
2018-10-10 18:29:30 +00:00
|
|
|
/// Print commands are steps in the execution of output templates.
|
2018-10-18 13:31:53 +00:00
|
|
|
pub enum LineOp {
|
2018-10-10 18:29:30 +00:00
|
|
|
/// Print a string.
|
2018-10-18 14:10:23 +00:00
|
|
|
Literal(Vec<u8>),
|
2018-10-11 16:32:01 +00:00
|
|
|
/// Lookup and print label value for key, if it exists.
|
2018-10-18 13:31:53 +00:00
|
|
|
LabelExists(String, Vec<LabelOp>),
|
2018-10-10 18:29:30 +00:00
|
|
|
/// Print metric value as text.
|
2018-10-09 20:46:15 +00:00
|
|
|
ValueAsText,
|
2018-10-10 18:29:30 +00:00
|
|
|
/// Print metric value, divided by the given scale, as text.
|
|
|
|
ScaledValueAsText(Value),
|
2018-10-12 20:44:39 +00:00
|
|
|
/// Print the newline character.labels.lookup(key)
|
2018-10-10 18:29:30 +00:00
|
|
|
NewLine,
|
2018-10-09 20:46:15 +00:00
|
|
|
}
|
|
|
|
|
2018-10-11 16:32:01 +00:00
|
|
|
/// Print commands are steps in the execution of output templates.
|
2018-10-18 13:31:53 +00:00
|
|
|
pub enum LabelOp {
|
2018-10-12 20:44:39 +00:00
|
|
|
/// Print a string.
|
2018-10-18 14:10:23 +00:00
|
|
|
Literal(Vec<u8>),
|
2018-10-12 20:44:39 +00:00
|
|
|
/// Print the label key.
|
|
|
|
LabelKey,
|
|
|
|
/// Print the label value.
|
|
|
|
LabelValue,
|
2018-10-11 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 18:29:30 +00:00
|
|
|
/// An sequence of print commands, embodying an output strategy for a single metric.
|
2018-10-12 20:44:39 +00:00
|
|
|
pub struct LineTemplate {
|
2018-10-18 13:31:53 +00:00
|
|
|
ops: Vec<LineOp>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Vec<LineOp>> for LineTemplate {
|
|
|
|
fn from(ops: Vec<LineOp>) -> Self {
|
|
|
|
LineTemplate { ops }
|
|
|
|
}
|
2018-10-09 20:46:15 +00:00
|
|
|
}
|
|
|
|
|
2018-10-12 20:44:39 +00:00
|
|
|
impl LineTemplate {
|
2018-10-10 18:29:30 +00:00
|
|
|
/// Template execution applies commands in turn, writing to the output.
|
2018-10-12 20:44:39 +00:00
|
|
|
pub fn print<L>(&self, output: &mut io::Write, value: Value, lookup: L) -> Result<(), io::Error>
|
|
|
|
where L: Fn(&str) -> Option<Arc<String>>
|
|
|
|
{
|
2018-10-18 13:31:53 +00:00
|
|
|
for cmd in &self.ops {
|
2018-10-09 20:46:15 +00:00
|
|
|
match cmd {
|
2018-10-10 18:29:30 +00:00
|
|
|
Literal(src) => output.write_all(src.as_ref())?,
|
2018-10-09 20:46:15 +00:00
|
|
|
ValueAsText => output.write_all(format!("{}", value).as_ref())?,
|
2018-10-10 18:29:30 +00:00
|
|
|
ScaledValueAsText(scale) => {
|
|
|
|
let scaled = value / scale;
|
|
|
|
output.write_all(format!("{}", scaled).as_ref())?
|
|
|
|
},
|
|
|
|
NewLine => writeln!(output)?,
|
2018-10-12 20:44:39 +00:00
|
|
|
LabelExists(label_key, print_label) => {
|
2018-10-11 16:32:01 +00:00
|
|
|
if let Some(label_value) = lookup(label_key.as_ref()) {
|
2018-10-12 20:44:39 +00:00
|
|
|
for label_cmd in print_label {
|
|
|
|
match label_cmd {
|
2018-10-18 13:31:53 +00:00
|
|
|
LabelOp::LabelValue =>
|
2018-10-12 20:44:39 +00:00
|
|
|
output.write_all(label_value.as_bytes())?,
|
2018-10-18 13:31:53 +00:00
|
|
|
LabelOp::LabelKey =>
|
2018-10-12 20:44:39 +00:00
|
|
|
output.write_all(label_key.as_bytes())?,
|
2018-10-18 13:31:53 +00:00
|
|
|
LabelOp::Literal(src) =>
|
2018-10-12 20:44:39 +00:00
|
|
|
output.write_all(src.as_ref())?,
|
|
|
|
}
|
|
|
|
}
|
2018-10-11 16:32:01 +00:00
|
|
|
}
|
2018-10-12 20:44:39 +00:00
|
|
|
},
|
2018-10-09 20:46:15 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-12 20:44:39 +00:00
|
|
|
/// Format output config support.
|
|
|
|
pub trait Formatting {
|
|
|
|
/// Specify formatting of output.
|
|
|
|
fn formatting(&self, format: impl LineFormat + 'static) -> Self;
|
|
|
|
}
|
2018-10-09 20:46:15 +00:00
|
|
|
|
|
|
|
/// Forges metric-specific printers
|
2018-10-12 20:44:39 +00:00
|
|
|
pub trait LineFormat: Send + Sync {
|
2018-10-09 20:46:15 +00:00
|
|
|
|
|
|
|
/// Prepare a template for output of metric values.
|
2018-10-12 20:44:39 +00:00
|
|
|
fn template(&self, name: &Name, kind: Kind) -> LineTemplate;
|
2018-10-09 20:46:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A simple metric output format of "MetricName {Value}"
|
|
|
|
#[derive(Default)]
|
2018-10-12 20:44:39 +00:00
|
|
|
pub struct SimpleFormat {
|
|
|
|
// TODO make separator configurable
|
2018-10-10 18:29:30 +00:00
|
|
|
// separator: String,
|
|
|
|
}
|
2018-10-09 20:46:15 +00:00
|
|
|
|
2018-10-12 20:44:39 +00:00
|
|
|
impl LineFormat for SimpleFormat {
|
|
|
|
fn template(&self, name: &Name, _kind: Kind) -> LineTemplate {
|
2018-10-09 20:46:15 +00:00
|
|
|
let mut header = name.join(".");
|
|
|
|
header.push(' ');
|
2018-10-12 20:44:39 +00:00
|
|
|
LineTemplate {
|
2018-10-18 13:31:53 +00:00
|
|
|
ops: vec![
|
2018-10-18 14:10:23 +00:00
|
|
|
Literal(header.into_bytes()),
|
2018-10-09 20:46:15 +00:00
|
|
|
ValueAsText,
|
2018-10-10 18:29:30 +00:00
|
|
|
NewLine,
|
2018-10-09 20:46:15 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-10 18:29:30 +00:00
|
|
|
|
2018-10-12 20:44:39 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
pub mod test {
|
|
|
|
use super::*;
|
|
|
|
use ::Labels;
|
|
|
|
|
|
|
|
pub struct TestFormat;
|
|
|
|
|
|
|
|
impl LineFormat for TestFormat {
|
|
|
|
fn template(&self, name: &Name, kind: Kind) -> LineTemplate {
|
|
|
|
let mut header: String = format!("{:?}", kind);
|
|
|
|
header.push('/');
|
|
|
|
header.push_str(&name.join("."));
|
|
|
|
header.push(' ');
|
|
|
|
LineTemplate {
|
2018-10-18 13:31:53 +00:00
|
|
|
ops: vec![
|
2018-10-18 14:10:23 +00:00
|
|
|
Literal(header.into()),
|
2018-10-12 20:44:39 +00:00
|
|
|
ValueAsText,
|
|
|
|
Literal(" ".into()),
|
|
|
|
ScaledValueAsText(1000),
|
|
|
|
Literal(" ".into()),
|
|
|
|
LabelExists("test_key".into(), vec![
|
2018-10-18 13:31:53 +00:00
|
|
|
LabelOp::LabelKey,
|
|
|
|
LabelOp::Literal("=".into()),
|
|
|
|
LabelOp::LabelValue]),
|
2018-10-12 20:44:39 +00:00
|
|
|
NewLine,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn print_label_exists() {
|
|
|
|
let labels: Labels = labels!("test_key" => "456");
|
|
|
|
let format = TestFormat {};
|
|
|
|
let mut name = Name::from("abc");
|
|
|
|
name = name.prepend("xyz");
|
|
|
|
let template = format.template(&name, Kind::Counter);
|
|
|
|
let mut out = vec![];
|
|
|
|
template.print(&mut out, 123000, |key| labels.lookup(key)).unwrap();
|
|
|
|
assert_eq!("Counter/xyz.abc 123000 123 test_key=456\n", String::from_utf8(out).unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn print_label_not_exists() {
|
|
|
|
let format = TestFormat {};
|
|
|
|
let mut name = Name::from("abc");
|
|
|
|
name = name.prepend("xyz");
|
|
|
|
let template = format.template(&name, Kind::Counter);
|
|
|
|
let mut out = vec![];
|
|
|
|
template.print(&mut out, 123000, |_key| None).unwrap();
|
|
|
|
assert_eq!("Counter/xyz.abc 123000 123 \n", String::from_utf8(out).unwrap());
|
|
|
|
}
|
|
|
|
}
|