vector/conditions/
datadog_search.rs

1use std::{borrow::Cow, str::FromStr};
2
3use bytes::Bytes;
4use vector_lib::{
5    configurable::configurable_component,
6    event::{Event, EventRef, LogEvent, Value},
7};
8use vector_vrl_metrics::MetricsStorage;
9use vrl::{
10    datadog_filter::{
11        Filter, Matcher, Resolver, Run, build_matcher,
12        regex::{wildcard_regex, word_regex},
13    },
14    datadog_search_syntax::{Comparison, ComparisonValue, Field, QueryNode},
15    path::PathParseError,
16};
17
18use super::{Condition, Conditional, ConditionalConfig};
19
20/// A condition that uses the [Datadog Search](https://docs.datadoghq.com/logs/explorer/search_syntax/) query syntax against an event.
21#[configurable_component]
22#[derive(Clone, Debug, PartialEq)]
23pub struct DatadogSearchConfig {
24    /// The query string.
25    source: QueryNode,
26}
27
28impl Default for DatadogSearchConfig {
29    fn default() -> Self {
30        Self {
31            source: QueryNode::MatchAllDocs,
32        }
33    }
34}
35
36impl DatadogSearchConfig {
37    pub fn build_matcher(&self) -> crate::Result<Box<dyn Matcher<LogEvent>>> {
38        Ok(build_matcher(&self.source, &EventFilter)?)
39    }
40}
41
42impl FromStr for DatadogSearchConfig {
43    type Err = <QueryNode as FromStr>::Err;
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        s.parse().map(|source| Self { source })
46    }
47}
48
49impl From<QueryNode> for DatadogSearchConfig {
50    fn from(source: QueryNode) -> Self {
51        Self { source }
52    }
53}
54
55impl_generate_config_from_default!(DatadogSearchConfig);
56
57/// Runner that contains the boxed `Matcher` function to check whether an `Event` matches
58/// a [Datadog Search Syntax query](https://docs.datadoghq.com/logs/explorer/search_syntax/).
59#[derive(Debug, Clone)]
60pub struct DatadogSearchRunner {
61    matcher: Box<dyn Matcher<LogEvent>>,
62}
63
64impl TryFrom<&DatadogSearchConfig> for DatadogSearchRunner {
65    type Error = crate::Error;
66    fn try_from(config: &DatadogSearchConfig) -> Result<Self, Self::Error> {
67        config.build_matcher().map(|matcher| Self { matcher })
68    }
69}
70
71impl DatadogSearchRunner {
72    pub fn matches<'a>(&self, event: impl Into<EventRef<'a>>) -> bool {
73        match event.into() {
74            EventRef::Log(log) => self.matcher.run(log),
75            _ => false,
76        }
77    }
78}
79
80impl Conditional for DatadogSearchRunner {
81    fn check(&self, event: Event) -> (bool, Event) {
82        (self.matches(&event), event)
83    }
84}
85
86impl ConditionalConfig for DatadogSearchConfig {
87    fn build(
88        &self,
89        _enrichment_tables: &vector_lib::enrichment::TableRegistry,
90        _: &MetricsStorage,
91    ) -> crate::Result<Condition> {
92        Ok(Condition::DatadogSearch(self.try_into()?))
93    }
94}
95
96#[derive(Default, Clone)]
97struct EventFilter;
98
99/// Uses the default `Resolver`, to build a `Vec<Field>`.
100impl Resolver for EventFilter {}
101
102impl Filter<LogEvent> for EventFilter {
103    fn exists(&self, field: Field) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
104        Ok(match field {
105            Field::Tag(tag) => {
106                let starts_with = format!("{tag}:");
107
108                any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
109                    value == tag || value.starts_with(&starts_with)
110                })
111            }
112            // Literal field 'tags' needs to be compared by key.
113            Field::Reserved(field) if field == "tags" => {
114                any_string_match_multiple(vec!["ddtags", "tags"], move |value| value == field)
115            }
116            // A literal "source" field should string match in "source" and "ddsource" fields (OR condition).
117            Field::Reserved(field) if field == "source" => {
118                exists_match_multiple(vec!["ddsource", "source"])
119            }
120
121            Field::Default(f) | Field::Attribute(f) | Field::Reserved(f) => exists_match(f),
122        })
123    }
124
125    fn equals(
126        &self,
127        field: Field,
128        to_match: &str,
129    ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
130        Ok(match field {
131            // Default fields are compared by word boundary.
132            Field::Default(field) => {
133                let re = word_regex(to_match);
134
135                string_match(&field, move |value| re.is_match(&value))
136            }
137            // A literal "tags" field should match by key.
138            Field::Reserved(field) if field == "tags" => {
139                let to_match = to_match.to_owned();
140
141                array_match_multiple(vec!["ddtags", "tags"], move |values| {
142                    values.contains(&Value::Bytes(Bytes::copy_from_slice(to_match.as_bytes())))
143                })
144            }
145            // Individual tags are compared by element key:value.
146            Field::Tag(tag) => {
147                let value_bytes = Value::Bytes(format!("{tag}:{to_match}").into());
148
149                array_match_multiple(vec!["ddtags", "tags"], move |values| {
150                    values.contains(&value_bytes)
151                })
152            }
153            // A literal "source" field should string match in "source" and "ddsource" fields (OR condition).
154            Field::Reserved(field) if field == "source" => {
155                let to_match = to_match.to_owned();
156
157                string_match_multiple(vec!["ddsource", "source"], move |value| value == to_match)
158            }
159            // Reserved values are matched by string equality.
160            Field::Reserved(field) => {
161                let to_match = to_match.to_owned();
162
163                string_match(field, move |value| value == to_match)
164            }
165            // Attribute values can be strings or numeric types
166            Field::Attribute(field) => {
167                let to_match = to_match.to_owned();
168
169                simple_scalar_match(field, move |value| value == to_match)
170            }
171        })
172    }
173
174    fn prefix(
175        &self,
176        field: Field,
177        prefix: &str,
178    ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
179        Ok(match field {
180            // Default fields are matched by word boundary.
181            Field::Default(field) => {
182                let re = word_regex(&format!("{prefix}*"));
183
184                string_match(field, move |value| re.is_match(&value))
185            }
186            // Tags are recursed until a match is found.
187            Field::Tag(tag) => {
188                let starts_with = format!("{tag}:{prefix}");
189
190                any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
191                    value.starts_with(&starts_with)
192                })
193            }
194            // A literal "source" field should string match in "source" and "ddsource" fields (OR condition).
195            Field::Reserved(field) if field == "source" => {
196                let prefix = prefix.to_owned();
197
198                string_match_multiple(vec!["ddsource", "source"], move |value| {
199                    value.starts_with(&prefix)
200                })
201            }
202
203            // All other field types are compared by complete value.
204            Field::Reserved(field) | Field::Attribute(field) => {
205                let prefix = prefix.to_owned();
206
207                string_match(field, move |value| value.starts_with(&prefix))
208            }
209        })
210    }
211
212    fn wildcard(
213        &self,
214        field: Field,
215        wildcard: &str,
216    ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
217        Ok(match field {
218            Field::Default(field) => {
219                let re = word_regex(wildcard);
220
221                string_match(field, move |value| re.is_match(&value))
222            }
223            Field::Tag(tag) => {
224                let re = wildcard_regex(&format!("{tag}:{wildcard}"));
225
226                any_string_match_multiple(vec!["ddtags", "tags"], move |value| re.is_match(&value))
227            }
228            // A literal "source" field should string match in "source" and "ddsource" fields (OR condition).
229            Field::Reserved(field) if field == "source" => {
230                let re = wildcard_regex(wildcard);
231
232                string_match_multiple(vec!["ddsource", "source"], move |value| re.is_match(&value))
233            }
234            Field::Reserved(field) | Field::Attribute(field) => {
235                let re = wildcard_regex(wildcard);
236
237                string_match(field, move |value| re.is_match(&value))
238            }
239        })
240    }
241
242    fn compare(
243        &self,
244        field: Field,
245        comparator: Comparison,
246        comparison_value: ComparisonValue,
247    ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
248        let rhs = Cow::from(comparison_value.to_string());
249
250        Ok(match field {
251            // Attributes are compared numerically if the value is numeric, or as strings otherwise.
252            Field::Attribute(f) => {
253                Run::boxed(move |log: &LogEvent| {
254                    match (
255                        log.parse_path_and_get_value(f.as_str()).ok().flatten(),
256                        &comparison_value,
257                    ) {
258                        // Integers.
259                        (Some(Value::Integer(lhs)), ComparisonValue::Integer(rhs)) => {
260                            match comparator {
261                                Comparison::Lt => lhs < rhs,
262                                Comparison::Lte => lhs <= rhs,
263                                Comparison::Gt => lhs > rhs,
264                                Comparison::Gte => lhs >= rhs,
265                            }
266                        }
267                        // Integer value - Float boundary
268                        (Some(Value::Integer(lhs)), ComparisonValue::Float(rhs)) => {
269                            match comparator {
270                                Comparison::Lt => (*lhs as f64) < *rhs,
271                                Comparison::Lte => *lhs as f64 <= *rhs,
272                                Comparison::Gt => *lhs as f64 > *rhs,
273                                Comparison::Gte => *lhs as f64 >= *rhs,
274                            }
275                        }
276                        // Floats.
277                        (Some(Value::Float(lhs)), ComparisonValue::Float(rhs)) => {
278                            match comparator {
279                                Comparison::Lt => lhs.into_inner() < *rhs,
280                                Comparison::Lte => lhs.into_inner() <= *rhs,
281                                Comparison::Gt => lhs.into_inner() > *rhs,
282                                Comparison::Gte => lhs.into_inner() >= *rhs,
283                            }
284                        }
285                        // Float value - Integer boundary
286                        (Some(Value::Float(lhs)), ComparisonValue::Integer(rhs)) => {
287                            match comparator {
288                                Comparison::Lt => lhs.into_inner() < *rhs as f64,
289                                Comparison::Lte => lhs.into_inner() <= *rhs as f64,
290                                Comparison::Gt => lhs.into_inner() > *rhs as f64,
291                                Comparison::Gte => lhs.into_inner() >= *rhs as f64,
292                            }
293                        }
294                        // Where the rhs is a string ref, the lhs is coerced into a string.
295                        (Some(Value::Bytes(v)), ComparisonValue::String(rhs)) => {
296                            let lhs = String::from_utf8_lossy(v);
297                            let rhs = Cow::from(rhs);
298
299                            match comparator {
300                                Comparison::Lt => lhs < rhs,
301                                Comparison::Lte => lhs <= rhs,
302                                Comparison::Gt => lhs > rhs,
303                                Comparison::Gte => lhs >= rhs,
304                            }
305                        }
306                        // Otherwise, compare directly as strings.
307                        (Some(Value::Bytes(v)), _) => {
308                            let lhs = String::from_utf8_lossy(v);
309
310                            match comparator {
311                                Comparison::Lt => lhs < rhs,
312                                Comparison::Lte => lhs <= rhs,
313                                Comparison::Gt => lhs > rhs,
314                                Comparison::Gte => lhs >= rhs,
315                            }
316                        }
317                        _ => false,
318                    }
319                })
320            }
321            // Tag values need extracting by "key:value" to be compared.
322            Field::Tag(tag) => any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
323                match value.split_once(':') {
324                    Some((t, lhs)) if t == tag => {
325                        let lhs = Cow::from(lhs);
326
327                        match comparator {
328                            Comparison::Lt => lhs < rhs,
329                            Comparison::Lte => lhs <= rhs,
330                            Comparison::Gt => lhs > rhs,
331                            Comparison::Gte => lhs >= rhs,
332                        }
333                    }
334                    _ => false,
335                }
336            }),
337            // A literal "source" field should string match in "source" and "ddsource" fields (OR condition).
338            Field::Reserved(field) if field == "source" => {
339                string_match_multiple(vec!["ddsource", "source"], move |lhs| match comparator {
340                    Comparison::Lt => lhs < rhs,
341                    Comparison::Lte => lhs <= rhs,
342                    Comparison::Gt => lhs > rhs,
343                    Comparison::Gte => lhs >= rhs,
344                })
345            }
346            // All other tag types are compared by string.
347            Field::Default(field) | Field::Reserved(field) => {
348                string_match(field, move |lhs| match comparator {
349                    Comparison::Lt => lhs < rhs,
350                    Comparison::Lte => lhs <= rhs,
351                    Comparison::Gt => lhs > rhs,
352                    Comparison::Gte => lhs >= rhs,
353                })
354            }
355        })
356    }
357}
358
359// Returns a `Matcher` that returns true if the field exists.
360fn exists_match<S>(field: S) -> Box<dyn Matcher<LogEvent>>
361where
362    S: Into<String>,
363{
364    let field = field.into();
365
366    Run::boxed(move |log: &LogEvent| {
367        log.parse_path_and_get_value(field.as_str())
368            .ok()
369            .flatten()
370            .is_some()
371    })
372}
373
374/// Returns a `Matcher` that returns true if the field resolves to a string,
375/// numeric, or boolean which matches the provided `func`.
376fn simple_scalar_match<S, F>(field: S, func: F) -> Box<dyn Matcher<LogEvent>>
377where
378    S: Into<String>,
379    F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
380{
381    let field = field.into();
382
383    Run::boxed(move |log: &LogEvent| {
384        match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
385            Some(Value::Boolean(v)) => func(v.to_string().into()),
386            Some(Value::Bytes(v)) => func(String::from_utf8_lossy(v)),
387            Some(Value::Integer(v)) => func(v.to_string().into()),
388            Some(Value::Float(v)) => func(v.to_string().into()),
389            _ => false,
390        }
391    })
392}
393
394/// Returns a `Matcher` that returns true if the field resolves to a string which
395/// matches the provided `func`.
396fn string_match<S, F>(field: S, func: F) -> Box<dyn Matcher<LogEvent>>
397where
398    S: Into<String>,
399    F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
400{
401    let field = field.into();
402
403    Run::boxed(move |log: &LogEvent| {
404        match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
405            Some(Value::Bytes(v)) => func(String::from_utf8_lossy(v)),
406            _ => false,
407        }
408    })
409}
410
411// Returns a `Matcher` that returns true if any provided field exists.
412fn exists_match_multiple<S>(fields: Vec<S>) -> Box<dyn Matcher<LogEvent>>
413where
414    S: Into<String> + Clone + Send + Sync + 'static,
415{
416    Run::boxed(move |log: &LogEvent| {
417        fields
418            .iter()
419            .any(|field| exists_match(field.clone()).run(log))
420    })
421}
422
423/// Returns a `Matcher` that returns true if any provided field resolves to a string which
424/// matches the provided `func`.
425fn string_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
426where
427    S: Into<String> + Clone + Send + Sync + 'static,
428    F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
429{
430    Run::boxed(move |log: &LogEvent| {
431        fields
432            .iter()
433            .any(|field| string_match(field.clone(), func.clone()).run(log))
434    })
435}
436
437fn any_string_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
438where
439    S: Into<String> + Clone + Send + Sync + 'static,
440    F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
441{
442    any_match_multiple(fields, move |value| {
443        let bytes = value.coerce_to_bytes();
444        func(String::from_utf8_lossy(&bytes))
445    })
446}
447
448/// Returns a `Matcher` that returns true if any provided field of the log event resolves to an array, where
449/// at least one `Value` it contains matches the provided `func`.
450fn any_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
451where
452    S: Into<String> + Clone + Send + Sync + 'static,
453    F: Fn(&Value) -> bool + Send + Sync + Clone + 'static,
454{
455    array_match_multiple(fields, move |values| values.iter().any(&func))
456}
457
458/// Returns a `Matcher` that returns true if any provided field of the log event resolves to an array, where
459/// the vector of `Value`s the array contains matches the provided `func`.
460fn array_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
461where
462    S: Into<String> + Clone + Send + Sync + 'static,
463    F: Fn(&Vec<Value>) -> bool + Send + Sync + Clone + 'static,
464{
465    Run::boxed(move |log: &LogEvent| {
466        fields.iter().any(|field| {
467            let field = field.clone().into();
468            match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
469                Some(Value::Array(values)) => func(values),
470                _ => false,
471            }
472        })
473    })
474}
475
476#[cfg(test)]
477mod test {
478    use super::*;
479    use crate::log_event;
480
481    /// Returns the following: Datadog Search Syntax source (to be parsed), an `Event` that
482    /// should pass when matched against the compiled source, and an `Event` that should fail.
483    /// This is exported as public so any implementor of this lib can assert that each check
484    /// still passes/fails in the context it's used.
485    fn get_checks() -> Vec<(&'static str, Event, Event)> {
486        vec![
487            // Tag exists.
488            (
489                "_exists_:a",                        // Source
490                log_event!["tags" => vec!["a:foo"]], // Pass
491                log_event!["tags" => vec!["b:foo"]], // Fail
492            ),
493            // Tag exists with - in name.
494            (
495                "_exists_:a-b",                        // Source
496                log_event!["tags" => vec!["a-b:foo"]], // Pass
497                log_event!["tags" => vec!["ab:foo"]],  // Fail
498            ),
499            // Tag exists (negate).
500            (
501                "NOT _exists_:a",
502                log_event!["tags" => vec!["b:foo"]],
503                log_event!("tags" => vec!["a:foo"]),
504            ),
505            // Tag exists (negate w/-).
506            (
507                "-_exists_:a",
508                log_event!["tags" => vec!["b:foo"]],
509                log_event!["tags" => vec!["a:foo"]],
510            ),
511            // Attribute exists.
512            (
513                "_exists_:@b",
514                log_event!["b" => "foo"],
515                log_event!["a" => "foo"],
516            ),
517            // Attribute with - in name, exists.
518            // TODO: this is a test case which exists in the Datadog implementation of the feature.
519            //       in our implementation, it fails because parse_path_and_get_value, indicates that
520            //       the `-` in the field name is an invalid field name.
521            // (
522            //     "_exists_:@foo-bar",
523            //     log_event!["foo-bar" => "foo"],
524            //     log_event!["foobar" => "foo"],
525            // ),
526            // Attribute exists (negate).
527            (
528                "NOT _exists_:@b",
529                log_event!["a" => "foo"],
530                log_event!["b" => "foo"],
531            ),
532            // Attribute exists (negate w/-).
533            (
534                "-_exists_:@b",
535                log_event!["a" => "foo"],
536                log_event!["b" => "foo"],
537            ),
538            // Tag doesn't exist.
539            (
540                "_missing_:a",
541                log_event![],
542                log_event!["tags" => vec!["a:foo"]],
543            ),
544            // Tag doesn't exist (negate).
545            (
546                "NOT _missing_:a",
547                log_event!["tags" => vec!["a:foo"]],
548                log_event![],
549            ),
550            // Tag doesn't exist (negate w/-).
551            (
552                "-_missing_:a",
553                log_event!["tags" => vec!["a:foo"]],
554                log_event![],
555            ),
556            // Attribute doesn't exist.
557            (
558                "_missing_:@b",
559                log_event!["a" => "foo"],
560                log_event!["b" => "foo"],
561            ),
562            // Attribute doesn't exist (negate).
563            (
564                "NOT _missing_:@b",
565                log_event!["b" => "foo"],
566                log_event!["a" => "foo"],
567            ),
568            // Attribute doesn't exist (negate w/-).
569            (
570                "-_missing_:@b",
571                log_event!["b" => "foo"],
572                log_event!["a" => "foo"],
573            ),
574            // Keyword.
575            ("bla", log_event!["message" => "bla"], log_event![]),
576            (
577                "foo",
578                log_event!["message" => r#"{"key": "foo"}"#],
579                log_event![],
580            ),
581            (
582                "bar",
583                log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
584                log_event![],
585            ),
586            // Keyword (negate).
587            (
588                "NOT bla",
589                log_event!["message" => "nothing"],
590                log_event!["message" => "bla"],
591            ),
592            (
593                "NOT foo",
594                log_event![],
595                log_event!["message" => r#"{"key": "foo"}"#],
596            ),
597            (
598                "NOT bar",
599                log_event![],
600                log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
601            ),
602            // Keyword (negate w/-).
603            (
604                "-bla",
605                log_event!["message" => "nothing"],
606                log_event!["message" => "bla"],
607            ),
608            (
609                "-foo",
610                log_event![],
611                log_event!["message" => r#"{"key": "foo"}"#],
612            ),
613            (
614                "-bar",
615                log_event![],
616                log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
617            ),
618            // Quoted keyword.
619            (r#""bla""#, log_event!["message" => "bla"], log_event![]),
620            (
621                r#""foo""#,
622                log_event!["message" => r#"{"key": "foo"}"#],
623                log_event![],
624            ),
625            (
626                r#""bar""#,
627                log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
628                log_event![],
629            ),
630            // Quoted keyword (negate).
631            (r#"NOT "bla""#, log_event![], log_event!["message" => "bla"]),
632            (
633                r#"NOT "foo""#,
634                log_event![],
635                log_event!["message" => r#"{"key": "foo"}"#],
636            ),
637            (
638                r#"NOT "bar""#,
639                log_event![],
640                log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
641            ),
642            // Quoted keyword (negate w/-).
643            (r#"-"bla""#, log_event![], log_event!["message" => "bla"]),
644            (
645                r#"NOT "foo""#,
646                log_event![],
647                log_event!["message" => r#"{"key": "foo"}"#],
648            ),
649            (
650                r#"NOT "bar""#,
651                log_event![],
652                log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
653            ),
654            // Tag match.
655            (
656                "a:bla",
657                log_event!["tags" => vec!["a:bla"]],
658                log_event!["tags" => vec!["b:bla"]],
659            ),
660            // Reserved tag match.
661            (
662                "host:foo",
663                log_event!["host" => "foo"],
664                log_event!["tags" => vec!["host:foo"]],
665            ),
666            (
667                "host:foo",
668                log_event!["host" => "foo"],
669                log_event!["host" => "foobar"],
670            ),
671            (
672                "host:foo",
673                log_event!["host" => "foo"],
674                log_event!["host" => r#"{"value": "foo"}"#],
675            ),
676            // Tag match (negate).
677            (
678                "NOT a:bla",
679                log_event!["tags" => vec!["b:bla"]],
680                log_event!["tags" => vec!["a:bla"]],
681            ),
682            // Reserved tag match (negate).
683            (
684                "NOT host:foo",
685                log_event!["tags" => vec!["host:fo  o"]],
686                log_event!["host" => "foo"],
687            ),
688            // Tag match (negate w/-).
689            (
690                "-a:bla",
691                log_event!["tags" => vec!["b:bla"]],
692                log_event!["tags" => vec!["a:bla"]],
693            ),
694            // Reserved tag match (negate w/-).
695            (
696                "-trace_id:foo",
697                log_event![],
698                log_event!["trace_id" => "foo"],
699            ),
700            // Quoted tag match.
701            (
702                r#"a:"bla""#,
703                log_event!["tags" => vec!["a:bla"]],
704                log_event!["a" => "bla"],
705            ),
706            // Quoted tag match (negate).
707            (
708                r#"NOT a:"bla""#,
709                log_event!["a" => "bla"],
710                log_event!["tags" => vec!["a:bla"]],
711            ),
712            // Quoted tag match (negate w/-).
713            (
714                r#"-a:"bla""#,
715                log_event!["a" => "bla"],
716                log_event!["tags" => vec!["a:bla"]],
717            ),
718            // Boolean attribute match.
719            ("@a:true", log_event!["a" => true], log_event!["a" => false]),
720            // Boolean attribute match (negate).
721            (
722                "NOT @a:false",
723                log_event!["a" => true],
724                log_event!["a" => false],
725            ),
726            // String attribute match.
727            (
728                "@a:bla",
729                log_event!["a" => "bla"],
730                log_event!["tags" => vec!["a:bla"]],
731            ),
732            // String attribute match (negate).
733            (
734                "NOT @a:bla",
735                log_event!["tags" => vec!["a:bla"]],
736                log_event!["a" => "bla"],
737            ),
738            // String attribute match single character.
739            ("@a:b", log_event!["a" => "b"], log_event!["a" => "c"]),
740            // String attribute match special chars
741            (
742                "@a:va\\/lue",
743                log_event!["a" => "va/lue"],
744                log_event!["a" => "value"],
745            ),
746            // String attribute match escaped && chars
747            (
748                "@a:va\\&&lue",
749                log_event!["a" => "va&&lue"],
750                log_event!["a" => "value"],
751            ),
752            // String attribute match escaped spaces
753            (
754                "@a:va\\ lue",
755                log_event!["a" => "va lue"],
756                log_event!["a" => "value"],
757            ),
758            // String attribute match escaped || chars
759            (
760                "@a:va\\||lue",
761                log_event!["a" => "va||lue"],
762                log_event!["a" => "value"],
763            ),
764            // String attribute match escaped () chars
765            (
766                "@a:va\\(lue",
767                log_event!["a" => "va(lue"],
768                log_event!["a" => "value"],
769            ),
770            // String attribute match escaped * chars
771            (
772                "@a:va\\*lue",
773                log_event!["a" => "va*lue"],
774                log_event!["a" => "value"],
775            ),
776            // String attribute match ~ chars
777            // TODO: in Datadog, this character does not need to be escaped.
778            (
779                "@a:va\\~lue",
780                log_event!["a" => "va~lue"],
781                log_event!["a" => "value"],
782            ),
783            // String attribute match ^ chars
784            // TODO: in Datadog, this character does not need to be escaped.
785            (
786                "@a:va\\^lue",
787                log_event!["a" => "va^lue"],
788                log_event!["a" => "value"],
789            ),
790            // String attribute match / chars
791            (
792                "@a:va/lue",
793                log_event!["a" => "va/lue"],
794                log_event!["a" => "value"],
795            ),
796            // String attribute match (negate w/-).
797            (
798                "-@a:bla",
799                log_event!["tags" => vec!["a:bla"]],
800                log_event!["a" => "bla"],
801            ),
802            // Quoted attribute match.
803            (
804                r#"@a:"bla""#,
805                log_event!["a" => "bla"],
806                log_event!["tags" => vec!["a:bla"]],
807            ),
808            // Quoted attribute match (negate).
809            (
810                r#"NOT @a:"bla""#,
811                log_event!["tags" => vec!["a:bla"]],
812                log_event!["a" => "bla"],
813            ),
814            // Quoted attribute match (negate w/-).
815            (
816                r#"-@a:"bla""#,
817                log_event!["tags" => vec!["a:bla"]],
818                log_event!["a" => "bla"],
819            ),
820            // Integer attribute match.
821            (
822                "@a:200",
823                log_event!["a" => 200],
824                log_event!["tags" => vec!["a:200"]],
825            ),
826            // Integer attribute match (negate w/-).
827            ("-@a:200", log_event!["a" => 199], log_event!["a" => 200]),
828            // Float attribute match.
829            (
830                "@a:0.75",
831                log_event!["a" => 0.75],
832                log_event!["tags" => vec!["a:0.75"]],
833            ),
834            // Float attribute match (negate w/-).
835            ("-@a:0.75", log_event!["a" => 0.74], log_event!["a" => 0.75]),
836            // Attribute match with dot in name
837            (
838                "@a.b:x",
839                log_event!["a" => serde_json::json!({"b": "x"})],
840                Event::Log(LogEvent::from(Value::from(serde_json::json!({"a.b": "x"})))),
841            ),
842            // Attribute with dot in name (flattened key) - requires escaped quotes.
843            (
844                r#"@\"a.b\":x"#,
845                Event::Log(LogEvent::from(Value::from(serde_json::json!({"a.b": "x"})))),
846                log_event!["a" => serde_json::json!({"b": "x"})],
847            ),
848            // Wildcard prefix.
849            (
850                "*bla",
851                log_event!["message" => "foobla"],
852                log_event!["message" => "blafoo"],
853            ),
854            // Wildcard prefix (negate).
855            (
856                "NOT *bla",
857                log_event!["message" => "blafoo"],
858                log_event!["message" => "foobla"],
859            ),
860            // Wildcard prefix (negate w/-).
861            (
862                "-*bla",
863                log_event!["message" => "blafoo"],
864                log_event!["message" => "foobla"],
865            ),
866            // Wildcard suffix.
867            (
868                "bla*",
869                log_event!["message" => "blafoo"],
870                log_event!["message" => "foobla"],
871            ),
872            // Wildcard suffix (negate).
873            (
874                "NOT bla*",
875                log_event!["message" => "foobla"],
876                log_event!["message" => "blafoo"],
877            ),
878            // Wildcard suffix (negate w/-).
879            (
880                "-bla*",
881                log_event!["message" => "foobla"],
882                log_event!["message" => "blafoo"],
883            ),
884            // Multiple wildcards.
885            ("*b*la*", log_event!["message" => "foobla"], log_event![]),
886            // Multiple wildcards (negate).
887            (
888                "NOT *b*la*",
889                log_event![],
890                log_event!["message" => "foobla"],
891            ),
892            // Multiple wildcards (negate w/-).
893            ("-*b*la*", log_event![], log_event!["message" => "foobla"]),
894            // Wildcard prefix - tag.
895            (
896                "a:*bla",
897                log_event!["tags" => vec!["a:foobla"]],
898                log_event!["tags" => vec!["a:blafoo"]],
899            ),
900            // Wildcard prefix - tag (negate).
901            (
902                "NOT a:*bla",
903                log_event!["tags" => vec!["a:blafoo"]],
904                log_event!["tags" => vec!["a:foobla"]],
905            ),
906            // Wildcard prefix - tag (negate w/-).
907            (
908                "-a:*bla",
909                log_event!["tags" => vec!["a:blafoo"]],
910                log_event!["tags" => vec!["a:foobla"]],
911            ),
912            // Wildcard suffix - tag.
913            (
914                "b:bla*",
915                log_event!["tags" => vec!["b:blabop"]],
916                log_event!["tags" => vec!["b:bopbla"]],
917            ),
918            // Wildcard suffix - tag (negate).
919            (
920                "NOT b:bla*",
921                log_event!["tags" => vec!["b:bopbla"]],
922                log_event!["tags" => vec!["b:blabop"]],
923            ),
924            // Wildcard suffix - tag (negate w/-).
925            (
926                "-b:bla*",
927                log_event!["tags" => vec!["b:bopbla"]],
928                log_event!["tags" => vec!["b:blabop"]],
929            ),
930            // Multiple wildcards - tag.
931            (
932                "c:*b*la*",
933                log_event!["tags" => vec!["c:foobla"]],
934                log_event!["custom" => r#"{"title" => "foobla"}"#],
935            ),
936            // Multiple wildcards - tag (negate).
937            (
938                "NOT c:*b*la*",
939                log_event!["custom" => r#"{"title" => "foobla"}"#],
940                log_event!["tags" => vec!["c:foobla"]],
941            ),
942            // Multiple wildcards - tag (negate w/-).
943            (
944                "-c:*b*la*",
945                log_event!["custom" => r#"{"title" => "foobla"}"#],
946                log_event!["tags" => vec!["c:foobla"]],
947            ),
948            // Wildcard prefix - attribute.
949            (
950                "@a:*bla",
951                log_event!["a" => "foobla"],
952                log_event!["tags" => vec!["a:foobla"]],
953            ),
954            // Wildcard prefix - attribute (negate).
955            (
956                "NOT @a:*bla",
957                log_event!["tags" => vec!["a:foobla"]],
958                log_event!["a" => "foobla"],
959            ),
960            // Wildcard prefix - attribute (negate w/-).
961            (
962                "-@a:*bla",
963                log_event!["tags" => vec!["a:foobla"]],
964                log_event!["a" => "foobla"],
965            ),
966            // Wildcard suffix - attribute.
967            (
968                "@b:bla*",
969                log_event!["b" => "blabop"],
970                log_event!["tags" => vec!["b:blabop"]],
971            ),
972            // Wildcard suffix - attribute (negate).
973            (
974                "NOT @b:bla*",
975                log_event!["tags" => vec!["b:blabop"]],
976                log_event!["b" => "blabop"],
977            ),
978            // Wildcard suffix - attribute (negate w/-).
979            (
980                "-@b:bla*",
981                log_event!["tags" => vec!["b:blabop"]],
982                log_event!["b" => "blabop"],
983            ),
984            // Multiple wildcards - attribute.
985            (
986                "@c:*b*la*",
987                log_event!["c" => "foobla"],
988                log_event!["tags" => vec!["c:foobla"]],
989            ),
990            // Multiple wildcards - attribute (negate).
991            (
992                "NOT @c:*b*la*",
993                log_event!["tags" => vec!["c:foobla"]],
994                log_event!["c" => "foobla"],
995            ),
996            // Multiple wildcards - attribute (negate w/-).
997            (
998                "-@c:*b*la*",
999                log_event!["tags" => vec!["c:foobla"]],
1000                log_event!["c" => "foobla"],
1001            ),
1002            // Special case for tags.
1003            (
1004                "tags:a",
1005                log_event!["tags" => vec!["a", "b", "c"]],
1006                log_event!["tags" => vec!["d", "e", "f"]],
1007            ),
1008            // Special case for tags (negate).
1009            (
1010                "NOT tags:a",
1011                log_event!["tags" => vec!["d", "e", "f"]],
1012                log_event!["tags" => vec!["a", "b", "c"]],
1013            ),
1014            // Special case for tags (negate w/-).
1015            (
1016                "-tags:a",
1017                log_event!["tags" => vec!["d", "e", "f"]],
1018                log_event!["tags" => vec!["a", "b", "c"]],
1019            ),
1020            // Range - numeric, inclusive.
1021            (
1022                "[1 TO 10]",
1023                log_event!["message" => "1"],
1024                log_event!["message" => "2"],
1025            ),
1026            // Range - numeric, inclusive (negate).
1027            (
1028                "NOT [1 TO 10]",
1029                log_event!["message" => "2"],
1030                log_event!["message" => "1"],
1031            ),
1032            // Range - numeric, inclusive (negate w/-).
1033            (
1034                "-[1 TO 10]",
1035                log_event!["message" => "2"],
1036                log_event!["message" => "1"],
1037            ),
1038            // Range - numeric, inclusive, unbounded (upper).
1039            (
1040                "[50 TO *]",
1041                log_event!["message" => "6"],
1042                log_event!["message" => "40"],
1043            ),
1044            // Range - numeric, inclusive, unbounded (upper) (negate).
1045            (
1046                "NOT [50 TO *]",
1047                log_event!["message" => "40"],
1048                log_event!["message" => "6"],
1049            ),
1050            // Range - numeric, inclusive, unbounded (upper) (negate w/-).
1051            (
1052                "-[50 TO *]",
1053                log_event!["message" => "40"],
1054                log_event!["message" => "6"],
1055            ),
1056            // Range - numeric, inclusive, unbounded (lower).
1057            (
1058                "[* TO 50]",
1059                log_event!["message" => "3"],
1060                log_event!["message" => "6"],
1061            ),
1062            // Range - numeric, inclusive, unbounded (lower) (negate).
1063            (
1064                "NOT [* TO 50]",
1065                log_event!["message" => "6"],
1066                log_event!["message" => "3"],
1067            ),
1068            // Range - numeric, inclusive, unbounded (lower) (negate w/-).
1069            (
1070                "-[* TO 50]",
1071                log_event!["message" => "6"],
1072                log_event!["message" => "3"],
1073            ),
1074            // Range - numeric, inclusive, unbounded (both).
1075            ("[* TO *]", log_event!["message" => "foo"], log_event![]),
1076            // Range - numeric, inclusive, unbounded (both) (negate).
1077            ("NOT [* TO *]", log_event![], log_event!["message" => "foo"]),
1078            // Range - numeric, inclusive, unbounded (both) (negate w/-i).
1079            ("-[* TO *]", log_event![], log_event!["message" => "foo"]),
1080            // Range - numeric, inclusive, tag.
1081            (
1082                "a:[1 TO 10]",
1083                log_event!["tags" => vec!["a:1"]],
1084                log_event!["tags" => vec!["a:2"]],
1085            ),
1086            // Range - numeric, inclusive, tag (negate).
1087            (
1088                "NOT a:[1 TO 10]",
1089                log_event!["tags" => vec!["a:2"]],
1090                log_event!["tags" => vec!["a:1"]],
1091            ),
1092            // Range - numeric, inclusive, tag (negate w/-).
1093            (
1094                "-a:[1 TO 10]",
1095                log_event!["tags" => vec!["a:2"]],
1096                log_event!["tags" => vec!["a:1"]],
1097            ),
1098            // Range - numeric, inclusive, unbounded (upper), tag.
1099            (
1100                "a:[50 TO *]",
1101                log_event!["tags" => vec!["a:6"]],
1102                log_event!["tags" => vec!["a:40"]],
1103            ),
1104            // Range - numeric, inclusive, unbounded (upper), tag (negate).
1105            (
1106                "NOT a:[50 TO *]",
1107                log_event!["tags" => vec!["a:40"]],
1108                log_event!["tags" => vec!["a:6"]],
1109            ),
1110            // Range - numeric, inclusive, unbounded (upper), tag (negate w/-).
1111            (
1112                "-a:[50 TO *]",
1113                log_event!["tags" => vec!["a:40"]],
1114                log_event!["tags" => vec!["a:6"]],
1115            ),
1116            // Range - numeric, inclusive, unbounded (lower), tag.
1117            (
1118                "a:[* TO 50]",
1119                log_event!["tags" => vec!["a:400"]],
1120                log_event!["tags" => vec!["a:600"]],
1121            ),
1122            // Range - numeric, inclusive, unbounded (lower), tag (negate).
1123            (
1124                "NOT a:[* TO 50]",
1125                log_event!["tags" => vec!["a:600"]],
1126                log_event!["tags" => vec!["a:400"]],
1127            ),
1128            // Range - numeric, inclusive, unbounded (lower), tag (negate w/-).
1129            (
1130                "-a:[* TO 50]",
1131                log_event!["tags" => vec!["a:600"]],
1132                log_event!["tags" => vec!["a:400"]],
1133            ),
1134            // Range - numeric, inclusive, unbounded (both), tag.
1135            (
1136                "a:[* TO *]",
1137                log_event!["tags" => vec!["a:test"]],
1138                log_event!["tags" => vec!["b:test"]],
1139            ),
1140            // Range - numeric, inclusive, unbounded (both), tag (negate).
1141            (
1142                "NOT a:[* TO *]",
1143                log_event!["tags" => vec!["b:test"]],
1144                log_event!["tags" => vec!["a:test"]],
1145            ),
1146            // Range - numeric, inclusive, unbounded (both), tag (negate w/-).
1147            (
1148                "-a:[* TO *]",
1149                log_event!["tags" => vec!["b:test"]],
1150                log_event!["tags" => vec!["a:test"]],
1151            ),
1152            // Range - numeric, inclusive, attribute.
1153            ("@b:[1 TO 10]", log_event!["b" => 5], log_event!["b" => 11]),
1154            (
1155                "@b:[1 TO 100]",
1156                log_event!["b" => "10"],
1157                log_event!["b" => "2"],
1158            ),
1159            // Range - numeric, inclusive, attribute (negate).
1160            (
1161                "NOT @b:[1 TO 10]",
1162                log_event!["b" => 11],
1163                log_event!["b" => 5],
1164            ),
1165            (
1166                "NOT @b:[1 TO 100]",
1167                log_event!["b" => "2"],
1168                log_event!["b" => "10"],
1169            ),
1170            // Range - numeric, inclusive, attribute (negate w/-).
1171            ("-@b:[1 TO 10]", log_event!["b" => 11], log_event!["b" => 5]),
1172            (
1173                "NOT @b:[1 TO 100]",
1174                log_event!["b" => "2"],
1175                log_event!["b" => "10"],
1176            ),
1177            // Range - alpha, inclusive, attribute.
1178            ("@b:[a TO z]", log_event!["b" => "c"], log_event!["b" => 5]),
1179            // Range - alphanumeric, inclusive, attribute.
1180            (
1181                r#"@b:["1" TO "100"]"#,
1182                log_event!["b" => "10"],
1183                log_event!["b" => "2"],
1184            ),
1185            // Range - alphanumeric, inclusive, attribute (negate).
1186            (
1187                r#"NOT @b:["1" TO "100"]"#,
1188                log_event!["b" => "2"],
1189                log_event!["b" => "10"],
1190            ),
1191            // Range - alphanumeric, inclusive, attribute (negate).
1192            (
1193                r#"-@b:["1" TO "100"]"#,
1194                log_event!["b" => "2"],
1195                log_event!["b" => "10"],
1196            ),
1197            // Range - tag, exclusive.
1198            (
1199                "f:{1 TO 100}",
1200                log_event!["tags" => vec!["f:10"]],
1201                log_event!["tags" => vec!["f:1"]],
1202            ),
1203            (
1204                "f:{1 TO 100}",
1205                log_event!["tags" => vec!["f:10"]],
1206                log_event!["tags" => vec!["f:100"]],
1207            ),
1208            // Range - tag, exclusive (negate).
1209            (
1210                "NOT f:{1 TO 100}",
1211                log_event!["tags" => vec!["f:1"]],
1212                log_event!["tags" => vec!["f:10"]],
1213            ),
1214            (
1215                "NOT f:{1 TO 100}",
1216                log_event!["tags" => vec!["f:100"]],
1217                log_event!["tags" => vec!["f:10"]],
1218            ),
1219            // Range - tag, exclusive (negate w/-).
1220            (
1221                "-f:{1 TO 100}",
1222                log_event!["tags" => vec!["f:1"]],
1223                log_event!["tags" => vec!["f:10"]],
1224            ),
1225            (
1226                "-f:{1 TO 100}",
1227                log_event!["tags" => vec!["f:100"]],
1228                log_event!["tags" => vec!["f:10"]],
1229            ),
1230            // Range - attribute, exclusive.
1231            ("@f:{1 TO 100}", log_event!["f" => 50], log_event!["f" => 1]),
1232            (
1233                "@f:{1 TO 100}",
1234                log_event!["f" => 50],
1235                log_event!["f" => 100],
1236            ),
1237            // Range - attribute, exclusive (negate).
1238            (
1239                "NOT @f:{1 TO 100}",
1240                log_event!["f" => 1],
1241                log_event!["f" => 50],
1242            ),
1243            (
1244                "NOT @f:{1 TO 100}",
1245                log_event!["f" => 100],
1246                log_event!["f" => 50],
1247            ),
1248            // Range - attribute, exclusive (negate w/-).
1249            (
1250                "-@f:{1 TO 100}",
1251                log_event!["f" => 1],
1252                log_event!["f" => 50],
1253            ),
1254            (
1255                "-@f:{1 TO 100}",
1256                log_event!["f" => 100],
1257                log_event!["f" => 50],
1258            ),
1259            // OR of two values
1260            (
1261                "@field:(value1 OR value2)",
1262                log_event!["field" => "value1"],
1263                log_event!["field" => "value"],
1264            ),
1265            // OR of two values
1266            (
1267                "@field:value1 OR @field:value2",
1268                log_event!["field" => "value1"],
1269                log_event!["field" => "value"],
1270            ),
1271            // negate OR of two values
1272            (
1273                "-@field1:value1 OR -@field2:value2",
1274                log_event!["field1" => "value1"],
1275                log_event!["field1" => "value1", "field2" => "value2"],
1276            ),
1277            // default AND of two values
1278            (
1279                "@field:value @field2:value2",
1280                log_event!["field" => "value", "field2" => "value2"],
1281                log_event!["field" => "value", "field2" => "value3"],
1282            ),
1283            // handles newline
1284            (
1285                "@field:(value1 OR \n value2)",
1286                log_event!["field" => "value1"],
1287                log_event!["field" => "value"],
1288            ),
1289            // negate AND of bool and string
1290            (
1291                "NOT (@field:true AND @field2:value2)",
1292                log_event!["field" => false, "field2" => "value2"],
1293                log_event!["field" => true, "field2" => "value2"],
1294            ),
1295            // tags checks with 'ddtags' (DD Agent Source naming)
1296
1297            // Tag exists.
1298            (
1299                "_exists_:a",                          // Source
1300                log_event!["ddtags" => vec!["a:foo"]], // Pass
1301                log_event!["ddtags" => vec!["b:foo"]], // Fail
1302            ),
1303            // Tag exists with - in name.
1304            (
1305                "_exists_:a-b",                          // Source
1306                log_event!["ddtags" => vec!["a-b:foo"]], // Pass
1307                log_event!["ddtags" => vec!["ab:foo"]],  // Fail
1308            ),
1309            // Tag exists (negate).
1310            (
1311                "NOT _exists_:a",
1312                log_event!["ddtags" => vec!["b:foo"]],
1313                log_event!("ddtags" => vec!["a:foo"]),
1314            ),
1315            // Tag exists (negate w/-).
1316            (
1317                "-_exists_:a",
1318                log_event!["ddtags" => vec!["b:foo"]],
1319                log_event!["ddtags" => vec!["a:foo"]],
1320            ),
1321            // Tag doesn't exist.
1322            (
1323                "_missing_:a",
1324                log_event![],
1325                log_event!["ddtags" => vec!["a:foo"]],
1326            ),
1327            // Tag doesn't exist (negate).
1328            (
1329                "NOT _missing_:a",
1330                log_event!["ddtags" => vec!["a:foo"]],
1331                log_event![],
1332            ),
1333            // Tag doesn't exist (negate w/-).
1334            (
1335                "-_missing_:a",
1336                log_event!["ddtags" => vec!["a:foo"]],
1337                log_event![],
1338            ),
1339            // Tag match.
1340            (
1341                "a:bla",
1342                log_event!["ddtags" => vec!["a:bla"]],
1343                log_event!["ddtags" => vec!["b:bla"]],
1344            ),
1345            // Tag match (negate).
1346            (
1347                "NOT a:bla",
1348                log_event!["ddtags" => vec!["b:bla"]],
1349                log_event!["ddtags" => vec!["a:bla"]],
1350            ),
1351            // Reserved tag match (negate).
1352            (
1353                "NOT host:foo",
1354                log_event!["ddtags" => vec!["host:fo  o"]],
1355                log_event!["host" => "foo"],
1356            ),
1357            // Tag match (negate w/-).
1358            (
1359                "-a:bla",
1360                log_event!["ddtags" => vec!["b:bla"]],
1361                log_event!["ddtags" => vec!["a:bla"]],
1362            ),
1363            // Quoted tag match.
1364            (
1365                r#"a:"bla""#,
1366                log_event!["ddtags" => vec!["a:bla"]],
1367                log_event!["a" => "bla"],
1368            ),
1369            // Quoted tag match (negate).
1370            (
1371                r#"NOT a:"bla""#,
1372                log_event!["a" => "bla"],
1373                log_event!["ddtags" => vec!["a:bla"]],
1374            ),
1375            // Quoted tag match (negate w/-).
1376            (
1377                r#"-a:"bla""#,
1378                log_event!["a" => "bla"],
1379                log_event!["ddtags" => vec!["a:bla"]],
1380            ),
1381            // String attribute match.
1382            (
1383                "@a:bla",
1384                log_event!["a" => "bla"],
1385                log_event!["ddtags" => vec!["a:bla"]],
1386            ),
1387            // String attribute match (negate).
1388            (
1389                "NOT @a:bla",
1390                log_event!["ddtags" => vec!["a:bla"]],
1391                log_event!["a" => "bla"],
1392            ),
1393            // String attribute match (negate w/-).
1394            (
1395                "-@a:bla",
1396                log_event!["ddtags" => vec!["a:bla"]],
1397                log_event!["a" => "bla"],
1398            ),
1399            // Quoted attribute match.
1400            (
1401                r#"@a:"bla""#,
1402                log_event!["a" => "bla"],
1403                log_event!["ddtags" => vec!["a:bla"]],
1404            ),
1405            // Quoted attribute match (negate).
1406            (
1407                r#"NOT @a:"bla""#,
1408                log_event!["ddtags" => vec!["a:bla"]],
1409                log_event!["a" => "bla"],
1410            ),
1411            // Quoted attribute match (negate w/-).
1412            (
1413                r#"-@a:"bla""#,
1414                log_event!["ddtags" => vec!["a:bla"]],
1415                log_event!["a" => "bla"],
1416            ),
1417            // Integer attribute match.
1418            (
1419                "@a:200",
1420                log_event!["a" => 200],
1421                log_event!["ddtags" => vec!["a:200"]],
1422            ),
1423            // Float attribute match.
1424            (
1425                "@a:0.75",
1426                log_event!["a" => 0.75],
1427                log_event!["ddtags" => vec!["a:0.75"]],
1428            ),
1429            (
1430                "a:*bla",
1431                log_event!["ddtags" => vec!["a:foobla"]],
1432                log_event!["ddtags" => vec!["a:blafoo"]],
1433            ),
1434            // Wildcard prefix - tag (negate).
1435            (
1436                "NOT a:*bla",
1437                log_event!["ddtags" => vec!["a:blafoo"]],
1438                log_event!["ddtags" => vec!["a:foobla"]],
1439            ),
1440            // Wildcard prefix - tag (negate w/-).
1441            (
1442                "-a:*bla",
1443                log_event!["ddtags" => vec!["a:blafoo"]],
1444                log_event!["ddtags" => vec!["a:foobla"]],
1445            ),
1446            // Wildcard suffix - tag.
1447            (
1448                "b:bla*",
1449                log_event!["ddtags" => vec!["b:blabop"]],
1450                log_event!["ddtags" => vec!["b:bopbla"]],
1451            ),
1452            // Wildcard suffix - tag (negate).
1453            (
1454                "NOT b:bla*",
1455                log_event!["ddtags" => vec!["b:bopbla"]],
1456                log_event!["ddtags" => vec!["b:blabop"]],
1457            ),
1458            // Wildcard suffix - tag (negate w/-).
1459            (
1460                "-b:bla*",
1461                log_event!["ddtags" => vec!["b:bopbla"]],
1462                log_event!["ddtags" => vec!["b:blabop"]],
1463            ),
1464            // Multiple wildcards - tag.
1465            (
1466                "c:*b*la*",
1467                log_event!["ddtags" => vec!["c:foobla"]],
1468                log_event!["custom" => r#"{"title" => "foobla"}"#],
1469            ),
1470            // Multiple wildcards - tag (negate).
1471            (
1472                "NOT c:*b*la*",
1473                log_event!["custom" => r#"{"title" => "foobla"}"#],
1474                log_event!["ddtags" => vec!["c:foobla"]],
1475            ),
1476            // Multiple wildcards - tag (negate w/-).
1477            (
1478                "-c:*b*la*",
1479                log_event!["custom" => r#"{"title" => "foobla"}"#],
1480                log_event!["ddtags" => vec!["c:foobla"]],
1481            ),
1482            // Wildcard prefix - attribute.
1483            (
1484                "@a:*bla",
1485                log_event!["a" => "foobla"],
1486                log_event!["ddtags" => vec!["a:foobla"]],
1487            ),
1488            // Wildcard prefix - attribute (negate).
1489            (
1490                "NOT @a:*bla",
1491                log_event!["ddtags" => vec!["a:foobla"]],
1492                log_event!["a" => "foobla"],
1493            ),
1494            // Wildcard prefix - attribute (negate w/-).
1495            (
1496                "-@a:*bla",
1497                log_event!["ddtags" => vec!["a:foobla"]],
1498                log_event!["a" => "foobla"],
1499            ),
1500            // Wildcard suffix - attribute.
1501            (
1502                "@b:bla*",
1503                log_event!["b" => "blabop"],
1504                log_event!["ddtags" => vec!["b:blabop"]],
1505            ),
1506            // Wildcard suffix - attribute (negate).
1507            (
1508                "NOT @b:bla*",
1509                log_event!["ddtags" => vec!["b:blabop"]],
1510                log_event!["b" => "blabop"],
1511            ),
1512            // Wildcard suffix - attribute (negate w/-).
1513            (
1514                "-@b:bla*",
1515                log_event!["ddtags" => vec!["b:blabop"]],
1516                log_event!["b" => "blabop"],
1517            ),
1518            // Multiple wildcards - attribute.
1519            (
1520                "@c:*b*la*",
1521                log_event!["c" => "foobla"],
1522                log_event!["ddtags" => vec!["c:foobla"]],
1523            ),
1524            // Multiple wildcards - attribute (negate).
1525            (
1526                "NOT @c:*b*la*",
1527                log_event!["ddtags" => vec!["c:foobla"]],
1528                log_event!["c" => "foobla"],
1529            ),
1530            // Multiple wildcards - attribute (negate w/-).
1531            (
1532                "-@c:*b*la*",
1533                log_event!["ddtags" => vec!["c:foobla"]],
1534                log_event!["c" => "foobla"],
1535            ),
1536            // Special case for tags.
1537            (
1538                "tags:a",
1539                log_event!["ddtags" => vec!["a", "b", "c"]],
1540                log_event!["ddtags" => vec!["d", "e", "f"]],
1541            ),
1542            // Special case for tags (negate).
1543            (
1544                "NOT tags:a",
1545                log_event!["ddtags" => vec!["d", "e", "f"]],
1546                log_event!["ddtags" => vec!["a", "b", "c"]],
1547            ),
1548            // Special case for tags (negate w/-).
1549            (
1550                "-tags:a",
1551                log_event!["ddtags" => vec!["d", "e", "f"]],
1552                log_event!["ddtags" => vec!["a", "b", "c"]],
1553            ),
1554            // Special case: 'source' looks up on 'source' and 'ddsource' (OR condition)
1555            // source
1556            (
1557                "source:foo",
1558                log_event!["source" => "foo"],
1559                log_event!["tags" => vec!["source:foo"]],
1560            ),
1561            (
1562                "source:foo",
1563                log_event!["source" => "foo"],
1564                log_event!["source" => "foobar"],
1565            ),
1566            (
1567                "source:foo",
1568                log_event!["source" => "foo"],
1569                log_event!["source" => r#"{"value": "foo"}"#],
1570            ),
1571            // ddsource
1572            (
1573                "source:foo",
1574                log_event!["ddsource" => "foo"],
1575                log_event!["tags" => vec!["ddsource:foo"]],
1576            ),
1577            (
1578                "source:foo",
1579                log_event!["ddsource" => "foo"],
1580                log_event!["ddsource" => "foobar"],
1581            ),
1582            (
1583                "source:foo",
1584                log_event!["ddsource" => "foo"],
1585                log_event!["ddsource" => r#"{"value": "foo"}"#],
1586            ),
1587            // both source and ddsource
1588            (
1589                "source:foo",
1590                log_event!["source" => "foo", "ddsource" => "foo"],
1591                log_event!["source" => "foobar", "ddsource" => "foobar"],
1592            ),
1593            (
1594                "source:foo",
1595                log_event!["source" => "foo", "ddsource" => "foobar"],
1596                log_event!["source" => "foobar", "ddsource" => "foobar"],
1597            ),
1598            (
1599                "source:foo",
1600                log_event!["source" => "foobar", "ddsource" => "foo"],
1601                log_event!["source" => "foobar", "ddsource" => "foobar"],
1602            ),
1603        ]
1604    }
1605
1606    /// Test a `Matcher` by providing a `Filter<V>` and a processor that receives an
1607    /// `Event`, and returns a `V`. This allows testing against the pass/fail events that are returned
1608    /// from `get_checks()` and modifying into a type that allows for their processing.
1609    fn test_filter<V, F, P>(filter: F, processor: P)
1610    where
1611        V: std::fmt::Debug + Send + Sync + Clone + 'static,
1612        F: Filter<V> + Resolver,
1613        P: Fn(Event) -> V,
1614    {
1615        let checks = get_checks();
1616
1617        for (source, pass, fail) in checks {
1618            let node: QueryNode = source.parse().unwrap();
1619            let matcher = build_matcher(&node, &filter).unwrap();
1620
1621            assert!(matcher.run(&processor(pass)));
1622            assert!(!matcher.run(&processor(fail)));
1623        }
1624    }
1625
1626    #[test]
1627    /// Parse each Datadog Search Syntax query and check that it passes/fails.
1628    fn event_filter() {
1629        test_filter(EventFilter, |ev| ev.into_log())
1630    }
1631
1632    #[test]
1633    fn generate_config() {
1634        crate::test_util::test_generate_config::<DatadogSearchConfig>();
1635    }
1636
1637    #[test]
1638    fn check_datadog() {
1639        for (source, pass, fail) in get_checks() {
1640            let config: DatadogSearchConfig = source.parse().unwrap();
1641
1642            // Every query should build successfully.
1643            let cond = config
1644                .build(&Default::default(), &Default::default())
1645                .unwrap_or_else(|_| panic!("build failed: {source}"));
1646
1647            assert!(
1648                cond.check_with_context(pass.clone()).0.is_ok(),
1649                "should pass: {}\nevent: {}",
1650                source,
1651                serde_json::to_string(&pass.as_log()).unwrap(),
1652            );
1653
1654            assert!(
1655                cond.check_with_context(fail.clone()).0.is_err(),
1656                "should fail: {}\nevent: {}",
1657                source,
1658                serde_json::to_string(&fail.as_log()).unwrap(),
1659            );
1660        }
1661    }
1662}