vector/conditions/
mod.rs

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