1use vector_lib::configurable::configurable_component;
2use vector_lib::{compile_vrl, emit, TimeZone};
3use vrl::compiler::runtime::{Runtime, RuntimeResult, Terminate};
4use vrl::compiler::{CompilationResult, CompileConfig, Program, TypeState, VrlRuntime};
5use vrl::diagnostic::Formatter;
6use vrl::value::Value;
7
8use crate::config::LogNamespace;
9use crate::event::TargetEvents;
10use crate::{
11 conditions::{Condition, Conditional, ConditionalConfig},
12 event::{Event, VrlTarget},
13 internal_events::VrlConditionExecutionError,
14};
15
16#[configurable_component]
18#[derive(Clone, Debug, Default, PartialEq, Eq)]
19pub struct VrlConfig {
20 pub(crate) source: String,
22
23 #[configurable(derived, metadata(docs::hidden))]
24 #[serde(default, skip_serializing_if = "crate::serde::is_default")]
25 pub(crate) runtime: VrlRuntime,
26}
27
28impl_generate_config_from_default!(VrlConfig);
29
30impl ConditionalConfig for VrlConfig {
31 fn build(
32 &self,
33 enrichment_tables: &vector_lib::enrichment::TableRegistry,
34 ) -> crate::Result<Condition> {
35 let functions = vrl::stdlib::all()
46 .into_iter()
47 .chain(vector_lib::enrichment::vrl_functions());
48 #[cfg(feature = "sources-dnstap")]
49 let functions = functions.chain(dnstap_parser::vrl_functions());
50
51 let functions = functions
52 .chain(vector_vrl_functions::all())
53 .collect::<Vec<_>>();
54
55 let state = TypeState::default();
56
57 let mut config = CompileConfig::default();
58 config.set_custom(enrichment_tables.clone());
59 config.set_read_only();
60
61 let CompilationResult {
62 program,
63 warnings,
64 config: _,
65 } = compile_vrl(&self.source, &functions, &state, config).map_err(|diagnostics| {
66 Formatter::new(&self.source, diagnostics)
67 .colored()
68 .to_string()
69 })?;
70
71 if !program.final_type_info().result.is_boolean() {
72 return Err("VRL conditions must return a boolean.".into());
73 }
74
75 if !warnings.is_empty() {
76 let warnings = Formatter::new(&self.source, warnings).colored().to_string();
77 warn!(message = "VRL compilation warning.", %warnings);
78 }
79
80 match self.runtime {
81 VrlRuntime::Ast => Ok(Condition::Vrl(Vrl {
82 program,
83 source: self.source.clone(),
84 })),
85 }
86 }
87}
88
89#[derive(Debug, Clone)]
90pub struct Vrl {
91 pub(super) program: Program,
92 pub(super) source: String,
93}
94
95impl Vrl {
96 fn run(&self, event: Event) -> (Event, RuntimeResult) {
97 let log_namespace = event
98 .maybe_as_log()
99 .map(|log| log.namespace())
100 .unwrap_or(LogNamespace::Legacy);
101 let mut target = VrlTarget::new(event, self.program.info(), false);
102 let timezone = TimeZone::default();
104
105 let result = Runtime::default().resolve(&mut target, &self.program, &timezone);
106 let original_event = match target.into_events(log_namespace) {
107 TargetEvents::One(event) => event,
108 _ => panic!("Event was modified in a condition. This is an internal compiler error."),
109 };
110 (original_event, result)
111 }
112}
113
114impl Conditional for Vrl {
115 fn check(&self, event: Event) -> (bool, Event) {
116 let (event, result) = self.run(event);
117
118 let result = result
119 .map(|value| match value {
120 Value::Boolean(boolean) => boolean,
121 _ => panic!("VRL condition did not return a boolean type"),
122 })
123 .unwrap_or_else(|err| {
124 emit!(VrlConditionExecutionError {
125 error: err.to_string().as_ref()
126 });
127 false
128 });
129 (result, event)
130 }
131
132 fn check_with_context(&self, event: Event) -> (Result<(), String>, Event) {
133 let (event, result) = self.run(event);
134
135 let value_result = result.map_err(|err| match err {
136 Terminate::Abort(err) => {
137 let err = Formatter::new(
138 &self.source,
139 vrl::diagnostic::Diagnostic::from(
140 Box::new(err) as Box<dyn vrl::diagnostic::DiagnosticMessage>
141 ),
142 )
143 .colored()
144 .to_string();
145 format!("source execution aborted: {err}")
146 }
147 Terminate::Error(err) => {
148 let err = Formatter::new(
149 &self.source,
150 vrl::diagnostic::Diagnostic::from(
151 Box::new(err) as Box<dyn vrl::diagnostic::DiagnosticMessage>
152 ),
153 )
154 .colored()
155 .to_string();
156 format!("source execution failed: {err}")
157 }
158 });
159
160 let value = match value_result {
161 Ok(value) => value,
162 Err(err) => {
163 return (Err(err), event);
164 }
165 };
166
167 let result = match value {
168 Value::Boolean(v) if v => Ok(()),
169 Value::Boolean(v) if !v => Err("source execution resolved to false".into()),
170 _ => Err("source execution resolved to a non-boolean value".into()),
171 };
172 (result, event)
173 }
174}
175
176#[cfg(test)]
177mod test {
178 use vector_lib::metric_tags;
179
180 use super::*;
181 use crate::{
182 event::{Metric, MetricKind, MetricValue},
183 log_event,
184 };
185
186 #[test]
187 fn generate_config() {
188 crate::test_util::test_generate_config::<VrlConfig>();
189 }
190
191 #[test]
192 fn check_vrl() {
193 let checks = vec![
194 (
195 log_event![], "true == true", Ok(()), Ok(()), ),
200 (
201 log_event!["foo" => true, "bar" => false],
202 "to_bool(.bar || .foo) ?? false",
203 Ok(()),
204 Ok(()),
205 ),
206 (
207 log_event![],
208 "true == false",
209 Ok(()),
210 Err("source execution resolved to false"),
211 ),
212 (
232 Event::Metric(
233 Metric::new(
234 "zork",
235 MetricKind::Incremental,
236 MetricValue::Counter { value: 1.0 },
237 )
238 .with_namespace(Some("zerk"))
239 .with_tags(Some(metric_tags!("host" => "zoobub"))),
240 ),
241 r#".name == "zork" && .tags.host == "zoobub" && .kind == "incremental""#,
242 Ok(()),
243 Ok(()),
244 ),
245 (
246 log_event![],
247 r#""i_return_a_string""#,
248 Err("VRL conditions must return a boolean.".into()),
249 Ok(()),
250 ),
251 ];
252
253 for (event, source, build, check) in checks {
254 let source = source.to_owned();
255 let config = VrlConfig {
256 source,
257 runtime: Default::default(),
258 };
259
260 assert_eq!(
261 config
262 .build(&Default::default())
263 .map(|_| ())
264 .map_err(|e| e.to_string()),
265 build
266 );
267
268 if let Ok(cond) = config.build(&Default::default()) {
269 assert_eq!(
270 cond.check_with_context(event.clone()).0,
271 check.map_err(|e| e.to_string())
272 );
273 }
274 }
275 }
276}