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