vrl/stdlib/
encode_logfmt.rs

1use crate::compiler::prelude::*;
2use std::sync::LazyLock;
3
4use super::encode_key_value::{DEFAULT_FIELDS_ORDERING, EncodeKeyValueFn};
5
6static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
7    vec![
8        Parameter::required(
9            "value",
10            kind::OBJECT,
11            "The value to convert to a logfmt string.",
12        ),
13        Parameter::optional("fields_ordering", kind::ARRAY, "The ordering of fields to preserve. Any fields not in this list are listed unordered, after all ordered fields.")
14            .default(&DEFAULT_FIELDS_ORDERING),
15    ]
16});
17
18#[derive(Clone, Copy, Debug)]
19pub struct EncodeLogfmt;
20
21impl Function for EncodeLogfmt {
22    fn identifier(&self) -> &'static str {
23        "encode_logfmt"
24    }
25
26    fn usage(&self) -> &'static str {
27        "Encodes the `value` to [logfmt](https://brandur.org/logfmt)."
28    }
29
30    fn category(&self) -> &'static str {
31        Category::Codec.as_ref()
32    }
33
34    fn internal_failure_reasons(&self) -> &'static [&'static str] {
35        &["`fields_ordering` contains a non-string element."]
36    }
37
38    fn return_kind(&self) -> u16 {
39        kind::BYTES
40    }
41
42    fn notices(&self) -> &'static [&'static str] {
43        &["If `fields_ordering` is specified then the function is fallible else it is infallible."]
44    }
45
46    fn parameters(&self) -> &'static [Parameter] {
47        PARAMETERS.as_slice()
48    }
49
50    fn compile(
51        &self,
52        _state: &state::TypeState,
53        _ctx: &mut FunctionCompileContext,
54        arguments: ArgumentList,
55    ) -> Compiled {
56        // The encode_logfmt function is just an alias for `encode_key_value` with the following
57        // parameters for the delimiters.
58        let key_value_delimiter = Some(expr!("="));
59        let field_delimiter = Some(expr!(" "));
60        let flatten_boolean = Some(expr!(true));
61
62        let value = arguments.required("value");
63        let fields = arguments.optional("fields_ordering");
64
65        Ok(EncodeKeyValueFn {
66            value,
67            fields,
68            key_value_delimiter,
69            field_delimiter,
70            flatten_boolean,
71        }
72        .as_expr())
73    }
74
75    fn examples(&self) -> &'static [Example] {
76        &[
77            example! {
78                title: "Encode to logfmt (no ordering)",
79                source: r#"encode_logfmt({"ts": "2021-06-05T17:20:00Z", "msg": "This is a message", "lvl": "info"})"#,
80                result: Ok(r#"lvl=info msg="This is a message" ts=2021-06-05T17:20:00Z"#),
81            },
82            example! {
83                title: "Encode to logfmt (fields ordering)",
84                source: r#"encode_logfmt!({"ts": "2021-06-05T17:20:00Z", "msg": "This is a message", "lvl": "info", "log_id": 12345}, ["ts", "lvl", "msg"])"#,
85                result: Ok(r#"ts=2021-06-05T17:20:00Z lvl=info msg="This is a message" log_id=12345"#),
86            },
87            example! {
88                title: "Encode to logfmt (nested fields)",
89                source: r#"encode_logfmt({"agent": {"name": "foo"}, "log": {"file": {"path": "my.log"}}, "event": "log"})"#,
90                result: Ok(r"agent.name=foo event=log log.file.path=my.log"),
91            },
92            example! {
93                title: "Encode to logfmt (nested fields ordering)",
94                source: r#"encode_logfmt!({"agent": {"name": "foo"}, "log": {"file": {"path": "my.log"}}, "event": "log"}, ["event", "log.file.path", "agent.name"])"#,
95                result: Ok(r"event=log log.file.path=my.log agent.name=foo"),
96            },
97        ]
98    }
99}