Skip to main content

vector_core/event/lua/
event.rs

1use mlua::prelude::*;
2
3use super::{
4    super::{Event, LogEvent, Metric},
5    metric::LuaMetric,
6};
7use crate::event::MetricTagMode;
8
9pub struct LuaEvent {
10    pub event: Event,
11    pub metric_tag_mode: MetricTagMode,
12}
13
14impl IntoLua for LuaEvent {
15    #![allow(clippy::wrong_self_convention)] // this trait is defined by mlua
16    fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
17        let table = lua.create_table()?;
18        match self.event {
19            Event::Log(log) => table.raw_set("log", log.into_lua(lua)?)?,
20            Event::Metric(metric) => table.raw_set(
21                "metric",
22                LuaMetric {
23                    metric,
24                    tag_mode: self.metric_tag_mode,
25                }
26                .into_lua(lua)?,
27            )?,
28            Event::Trace(_) => {
29                return Err(LuaError::ToLuaConversionError {
30                    from: String::from("Event"),
31                    to: "table",
32                    message: Some("Trace are not supported".to_string()),
33                });
34            }
35        }
36        Ok(LuaValue::Table(table))
37    }
38}
39
40impl FromLua for Event {
41    fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
42        let LuaValue::Table(table) = &value else {
43            return Err(LuaError::FromLuaConversionError {
44                from: value.type_name(),
45                to: String::from("Event"),
46                message: Some("Event should be a Lua table".to_string()),
47            });
48        };
49        match (table.raw_get("log")?, table.raw_get("metric")?) {
50            (LuaValue::Table(log), LuaValue::Nil) => {
51                Ok(Event::Log(LogEvent::from_lua(LuaValue::Table(log), lua)?))
52            }
53            (LuaValue::Nil, LuaValue::Table(metric)) => Ok(Event::Metric(Metric::from_lua(
54                LuaValue::Table(metric),
55                lua,
56            )?)),
57            _ => Err(LuaError::FromLuaConversionError {
58                from: value.type_name(),
59                to: String::from("Event"),
60                message: Some(
61                    "Event should contain either \"log\" or \"metric\" key at the top level"
62                        .to_string(),
63                ),
64            }),
65        }
66    }
67}
68
69#[cfg(test)]
70mod test {
71    use super::*;
72    use crate::event::{
73        Metric, Value,
74        metric::{MetricKind, MetricValue},
75    };
76
77    fn assert_event(event: Event, assertions: Vec<&'static str>) {
78        let lua = Lua::new();
79        lua.globals()
80            .set(
81                "event",
82                LuaEvent {
83                    event,
84                    metric_tag_mode: MetricTagMode::Single,
85                },
86            )
87            .unwrap();
88        for assertion in assertions {
89            assert!(
90                lua.load(assertion).eval::<bool>().expect(assertion),
91                "{}",
92                assertion
93            );
94        }
95    }
96
97    #[test]
98    fn into_lua_log() {
99        use vrl::event_path;
100        let mut event = LogEvent::default();
101        event.insert(event_path!("field"), "value");
102
103        let assertions = vec![
104            "type(event) == 'table'",
105            "event.metric == nil",
106            "type(event.log) == 'table'",
107            "event.log.field == 'value'",
108        ];
109
110        assert_event(event.into(), assertions);
111    }
112
113    #[test]
114    fn into_lua_metric() {
115        let event = Event::Metric(Metric::new(
116            "example counter",
117            MetricKind::Absolute,
118            MetricValue::Counter {
119                value: 0.577_215_66,
120            },
121        ));
122
123        let assertions = vec![
124            "type(event) == 'table'",
125            "event.log == nil",
126            "type(event.metric) == 'table'",
127            "event.metric.name == 'example counter'",
128            "event.metric.counter.value == 0.57721566",
129        ];
130
131        assert_event(event, assertions);
132    }
133
134    #[test]
135    fn from_lua_log() {
136        let lua_event = r#"
137        {
138            log = {
139                field = "example",
140                nested = {
141                    field = "another example"
142                }
143            }
144        }"#;
145
146        let event = Lua::new().load(lua_event).eval::<Event>().unwrap();
147        let log = event.as_log();
148        assert_eq!(log["field"], Value::Bytes("example".into()));
149        assert_eq!(log["nested.field"], Value::Bytes("another example".into()));
150    }
151
152    #[test]
153    fn from_lua_metric() {
154        let lua_event = r#"
155        {
156            metric = {
157                name = "example counter",
158                counter = {
159                    value = 0.57721566
160                }
161            }
162        }"#;
163        let expected = Event::Metric(Metric::new(
164            "example counter",
165            MetricKind::Absolute,
166            MetricValue::Counter {
167                value: 0.577_215_66,
168            },
169        ));
170
171        let event = Lua::new().load(lua_event).eval::<Event>().unwrap();
172        vector_common::assert_event_data_eq!(event, expected);
173    }
174
175    #[test]
176    // the panic message a) is platform dependent and b) can change if any code is added before this function.
177    #[allow(clippy::should_panic_without_expect)]
178    #[should_panic]
179    fn from_lua_missing_log_and_metric() {
180        let lua_event = r"{
181            some_field: {}
182        }";
183        Lua::new().load(lua_event).eval::<Event>().unwrap();
184    }
185}