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