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