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