vector/conditions/
mod.rs

1#![allow(missing_docs)]
2use vector_lib::configurable::configurable_component;
3
4use crate::event::Event;
5
6mod datadog_search;
7pub(crate) mod is_log;
8pub(crate) mod is_metric;
9pub(crate) mod is_trace;
10mod vrl;
11
12pub use self::datadog_search::{DatadogSearchConfig, DatadogSearchRunner};
13pub use self::vrl::VrlConfig;
14use self::{
15    is_log::{check_is_log, check_is_log_with_context},
16    is_metric::{check_is_metric, check_is_metric_with_context},
17    is_trace::{check_is_trace, check_is_trace_with_context},
18    vrl::Vrl,
19};
20
21#[derive(Debug, Clone)]
22#[allow(clippy::large_enum_variant)]
23pub enum Condition {
24    /// Matches an event if it is a log.
25    IsLog,
26
27    /// Matches an event if it is a metric.
28    IsMetric,
29
30    /// Matches an event if it is a trace.
31    IsTrace,
32
33    /// Matches an event with a [Vector Remap Language](https://vector.dev/docs/reference/vrl) (VRL) [boolean expression](https://vector.dev/docs/reference/vrl#boolean-expressions).
34    Vrl(Vrl),
35
36    /// Matches an event with a [Datadog Search](https://docs.datadoghq.com/logs/explorer/search_syntax/) query.
37    DatadogSearch(DatadogSearchRunner),
38
39    /// Matches any event.
40    ///
41    /// Used only for internal testing.
42    AlwaysPass,
43
44    /// Matches no event.
45    ///
46    /// Used only for internal testing.
47    AlwaysFail,
48}
49
50impl Condition {
51    /// Checks if a condition is true.
52    ///
53    /// The event should not be modified, it is only mutable so it can be passed into VRL, but VRL type checking prevents mutation.
54    #[allow(dead_code)]
55    pub fn check(&self, e: Event) -> (bool, Event) {
56        match self {
57            Condition::IsLog => check_is_log(e),
58            Condition::IsMetric => check_is_metric(e),
59            Condition::IsTrace => check_is_trace(e),
60            Condition::Vrl(x) => x.check(e),
61            Condition::DatadogSearch(x) => x.check(e),
62            Condition::AlwaysPass => (true, e),
63            Condition::AlwaysFail => (false, e),
64        }
65    }
66
67    /// Checks if a condition is true, with a `Result`-oriented return for easier composition.
68    ///
69    /// This can be mildly expensive for conditions that do not often match, as it allocates a string for the error
70    /// case. As such, it should typically be avoided in hot paths.
71    pub(crate) fn check_with_context(&self, e: Event) -> (Result<(), String>, Event) {
72        match self {
73            Condition::IsLog => check_is_log_with_context(e),
74            Condition::IsMetric => check_is_metric_with_context(e),
75            Condition::IsTrace => check_is_trace_with_context(e),
76            Condition::Vrl(x) => x.check_with_context(e),
77            Condition::DatadogSearch(x) => x.check_with_context(e),
78            Condition::AlwaysPass => (Ok(()), e),
79            Condition::AlwaysFail => (Ok(()), e),
80        }
81    }
82}
83
84/// An event matching condition.
85///
86/// Many methods exist for matching events, such as using a VRL expression, a Datadog Search query string,
87/// or hard-coded matchers like "must be a metric" or "fields A, B, and C must match these constraints".
88///
89/// They can specified with an enum-style notation:
90///
91/// ```toml
92/// condition.type = 'datadog_search'
93/// condition.source = 'NOT "foo"'
94/// ```
95#[configurable_component]
96#[derive(Clone, Debug)]
97#[serde(tag = "type", rename_all = "snake_case")]
98pub enum ConditionConfig {
99    /// Matches an event if it is a log.
100    #[configurable(metadata(docs::hidden))]
101    IsLog,
102
103    /// Matches an event if it is a metric.
104    #[configurable(metadata(docs::hidden))]
105    IsMetric,
106
107    /// Matches an event if it is a trace.
108    #[configurable(metadata(docs::hidden))]
109    IsTrace,
110
111    /// Matches an event with a [Vector Remap Language](https://vector.dev/docs/reference/vrl) (VRL) [boolean expression](https://vector.dev/docs/reference/vrl#boolean-expressions).
112    Vrl(VrlConfig),
113
114    /// Matches an event with a [Datadog Search](https://docs.datadoghq.com/logs/explorer/search_syntax/) query.
115    DatadogSearch(DatadogSearchConfig),
116}
117
118impl ConditionConfig {
119    pub fn build(
120        &self,
121        enrichment_tables: &vector_lib::enrichment::TableRegistry,
122    ) -> crate::Result<Condition> {
123        match self {
124            ConditionConfig::IsLog => Ok(Condition::IsLog),
125            ConditionConfig::IsMetric => Ok(Condition::IsMetric),
126            ConditionConfig::IsTrace => Ok(Condition::IsTrace),
127            ConditionConfig::Vrl(x) => x.build(enrichment_tables),
128            ConditionConfig::DatadogSearch(x) => x.build(enrichment_tables),
129        }
130    }
131}
132
133pub trait Conditional: std::fmt::Debug {
134    /// Checks if a condition is true.
135    ///
136    /// The event should not be modified, it is only mutable so it can be passed into VRL, but VRL type checking prevents mutation.
137    fn check(&self, event: Event) -> (bool, Event);
138
139    /// Checks if a condition is true, with a `Result`-oriented return for easier composition.
140    ///
141    /// This can be mildly expensive for conditions that do not often match, as it allocates a string for the error
142    /// case. As such, it should typically be avoided in hot paths.
143    fn check_with_context(&self, e: Event) -> (Result<(), String>, Event) {
144        let (result, event) = self.check(e);
145        if result {
146            (Ok(()), event)
147        } else {
148            (Err("condition failed".into()), event)
149        }
150    }
151}
152
153pub trait ConditionalConfig: std::fmt::Debug + Send + Sync + dyn_clone::DynClone {
154    fn build(
155        &self,
156        enrichment_tables: &vector_lib::enrichment::TableRegistry,
157    ) -> crate::Result<Condition>;
158}
159
160dyn_clone::clone_trait_object!(ConditionalConfig);
161
162/// An event matching condition.
163///
164/// Many methods exist for matching events, such as using a VRL expression, a Datadog Search query string,
165/// or hard-coded matchers like "must be a metric" or "fields A, B, and C must match these constraints".
166///
167/// As VRL is the most common way to apply conditions to events, this type provides a shortcut to define VRL expressions
168/// directly in the configuration by passing the VRL expression as a string:
169///
170/// ```toml
171/// condition = '.message == "hooray"'
172/// ```
173///
174/// When other condition types are required, they can be specified with an enum-style notation:
175///
176/// ```toml
177/// condition.type = 'datadog_search'
178/// condition.source = 'NOT "foo"'
179/// ```
180#[configurable_component]
181#[derive(Clone, Debug)]
182#[configurable(metadata(docs::type_override = "condition"))]
183#[serde(untagged)]
184pub enum AnyCondition {
185    /// A [Vector Remap Language](https://vector.dev/docs/reference/vrl) (VRL) [boolean expression](https://vector.dev/docs/reference/vrl#boolean-expressions).
186    String(String),
187
188    /// A fully-specified condition.
189    Map(ConditionConfig),
190}
191
192impl AnyCondition {
193    pub fn build(
194        &self,
195        enrichment_tables: &vector_lib::enrichment::TableRegistry,
196    ) -> crate::Result<Condition> {
197        match self {
198            AnyCondition::String(s) => {
199                let vrl_config = VrlConfig {
200                    source: s.clone(),
201                    runtime: Default::default(),
202                };
203                vrl_config.build(enrichment_tables)
204            }
205            AnyCondition::Map(m) => m.build(enrichment_tables),
206        }
207    }
208}
209
210impl From<ConditionConfig> for AnyCondition {
211    fn from(config: ConditionConfig) -> Self {
212        Self::Map(config)
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use indoc::indoc;
219    use serde::Deserialize;
220
221    use super::*;
222
223    #[derive(Deserialize, Debug)]
224    struct Test {
225        condition: AnyCondition,
226    }
227
228    #[test]
229    fn deserialize_anycondition_default() {
230        let conf: Test = toml::from_str(r#"condition = ".nork == false""#).unwrap();
231        assert_eq!(
232            r#"String(".nork == false")"#,
233            format!("{:?}", conf.condition)
234        )
235    }
236
237    #[test]
238    fn deserialize_anycondition_vrl() {
239        let conf: Test = toml::from_str(indoc! {r#"
240            condition.type = "vrl"
241            condition.source = '.nork == true'
242        "#})
243        .unwrap();
244
245        assert_eq!(
246            r#"Map(Vrl(VrlConfig { source: ".nork == true", runtime: Ast }))"#,
247            format!("{:?}", conf.condition)
248        )
249    }
250}