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            // Wildcard prefix.
840            (
841                "*bla",
842                log_event!["message" => "foobla"],
843                log_event!["message" => "blafoo"],
844            ),
845            // Wildcard prefix (negate).
846            (
847                "NOT *bla",
848                log_event!["message" => "blafoo"],
849                log_event!["message" => "foobla"],
850            ),
851            // Wildcard prefix (negate w/-).
852            (
853                "-*bla",
854                log_event!["message" => "blafoo"],
855                log_event!["message" => "foobla"],
856            ),
857            // Wildcard suffix.
858            (
859                "bla*",
860                log_event!["message" => "blafoo"],
861                log_event!["message" => "foobla"],
862            ),
863            // Wildcard suffix (negate).
864            (
865                "NOT bla*",
866                log_event!["message" => "foobla"],
867                log_event!["message" => "blafoo"],
868            ),
869            // Wildcard suffix (negate w/-).
870            (
871                "-bla*",
872                log_event!["message" => "foobla"],
873                log_event!["message" => "blafoo"],
874            ),
875            // Multiple wildcards.
876            ("*b*la*", log_event!["message" => "foobla"], log_event![]),
877            // Multiple wildcards (negate).
878            (
879                "NOT *b*la*",
880                log_event![],
881                log_event!["message" => "foobla"],
882            ),
883            // Multiple wildcards (negate w/-).
884            ("-*b*la*", log_event![], log_event!["message" => "foobla"]),
885            // Wildcard prefix - tag.
886            (
887                "a:*bla",
888                log_event!["tags" => vec!["a:foobla"]],
889                log_event!["tags" => vec!["a:blafoo"]],
890            ),
891            // Wildcard prefix - tag (negate).
892            (
893                "NOT a:*bla",
894                log_event!["tags" => vec!["a:blafoo"]],
895                log_event!["tags" => vec!["a:foobla"]],
896            ),
897            // Wildcard prefix - tag (negate w/-).
898            (
899                "-a:*bla",
900                log_event!["tags" => vec!["a:blafoo"]],
901                log_event!["tags" => vec!["a:foobla"]],
902            ),
903            // Wildcard suffix - tag.
904            (
905                "b:bla*",
906                log_event!["tags" => vec!["b:blabop"]],
907                log_event!["tags" => vec!["b:bopbla"]],
908            ),
909            // Wildcard suffix - tag (negate).
910            (
911                "NOT b:bla*",
912                log_event!["tags" => vec!["b:bopbla"]],
913                log_event!["tags" => vec!["b:blabop"]],
914            ),
915            // Wildcard suffix - tag (negate w/-).
916            (
917                "-b:bla*",
918                log_event!["tags" => vec!["b:bopbla"]],
919                log_event!["tags" => vec!["b:blabop"]],
920            ),
921            // Multiple wildcards - tag.
922            (
923                "c:*b*la*",
924                log_event!["tags" => vec!["c:foobla"]],
925                log_event!["custom" => r#"{"title" => "foobla"}"#],
926            ),
927            // Multiple wildcards - tag (negate).
928            (
929                "NOT c:*b*la*",
930                log_event!["custom" => r#"{"title" => "foobla"}"#],
931                log_event!["tags" => vec!["c:foobla"]],
932            ),
933            // Multiple wildcards - tag (negate w/-).
934            (
935                "-c:*b*la*",
936                log_event!["custom" => r#"{"title" => "foobla"}"#],
937                log_event!["tags" => vec!["c:foobla"]],
938            ),
939            // Wildcard prefix - attribute.
940            (
941                "@a:*bla",
942                log_event!["a" => "foobla"],
943                log_event!["tags" => vec!["a:foobla"]],
944            ),
945            // Wildcard prefix - attribute (negate).
946            (
947                "NOT @a:*bla",
948                log_event!["tags" => vec!["a:foobla"]],
949                log_event!["a" => "foobla"],
950            ),
951            // Wildcard prefix - attribute (negate w/-).
952            (
953                "-@a:*bla",
954                log_event!["tags" => vec!["a:foobla"]],
955                log_event!["a" => "foobla"],
956            ),
957            // Wildcard suffix - attribute.
958            (
959                "@b:bla*",
960                log_event!["b" => "blabop"],
961                log_event!["tags" => vec!["b:blabop"]],
962            ),
963            // Wildcard suffix - attribute (negate).
964            (
965                "NOT @b:bla*",
966                log_event!["tags" => vec!["b:blabop"]],
967                log_event!["b" => "blabop"],
968            ),
969            // Wildcard suffix - attribute (negate w/-).
970            (
971                "-@b:bla*",
972                log_event!["tags" => vec!["b:blabop"]],
973                log_event!["b" => "blabop"],
974            ),
975            // Multiple wildcards - attribute.
976            (
977                "@c:*b*la*",
978                log_event!["c" => "foobla"],
979                log_event!["tags" => vec!["c:foobla"]],
980            ),
981            // Multiple wildcards - attribute (negate).
982            (
983                "NOT @c:*b*la*",
984                log_event!["tags" => vec!["c:foobla"]],
985                log_event!["c" => "foobla"],
986            ),
987            // Multiple wildcards - attribute (negate w/-).
988            (
989                "-@c:*b*la*",
990                log_event!["tags" => vec!["c:foobla"]],
991                log_event!["c" => "foobla"],
992            ),
993            // Special case for tags.
994            (
995                "tags:a",
996                log_event!["tags" => vec!["a", "b", "c"]],
997                log_event!["tags" => vec!["d", "e", "f"]],
998            ),
999            // Special case for tags (negate).
1000            (
1001                "NOT tags:a",
1002                log_event!["tags" => vec!["d", "e", "f"]],
1003                log_event!["tags" => vec!["a", "b", "c"]],
1004            ),
1005            // Special case for tags (negate w/-).
1006            (
1007                "-tags:a",
1008                log_event!["tags" => vec!["d", "e", "f"]],
1009                log_event!["tags" => vec!["a", "b", "c"]],
1010            ),
1011            // Range - numeric, inclusive.
1012            (
1013                "[1 TO 10]",
1014                log_event!["message" => "1"],
1015                log_event!["message" => "2"],
1016            ),
1017            // Range - numeric, inclusive (negate).
1018            (
1019                "NOT [1 TO 10]",
1020                log_event!["message" => "2"],
1021                log_event!["message" => "1"],
1022            ),
1023            // Range - numeric, inclusive (negate w/-).
1024            (
1025                "-[1 TO 10]",
1026                log_event!["message" => "2"],
1027                log_event!["message" => "1"],
1028            ),
1029            // Range - numeric, inclusive, unbounded (upper).
1030            (
1031                "[50 TO *]",
1032                log_event!["message" => "6"],
1033                log_event!["message" => "40"],
1034            ),
1035            // Range - numeric, inclusive, unbounded (upper) (negate).
1036            (
1037                "NOT [50 TO *]",
1038                log_event!["message" => "40"],
1039                log_event!["message" => "6"],
1040            ),
1041            // Range - numeric, inclusive, unbounded (upper) (negate w/-).
1042            (
1043                "-[50 TO *]",
1044                log_event!["message" => "40"],
1045                log_event!["message" => "6"],
1046            ),
1047            // Range - numeric, inclusive, unbounded (lower).
1048            (
1049                "[* TO 50]",
1050                log_event!["message" => "3"],
1051                log_event!["message" => "6"],
1052            ),
1053            // Range - numeric, inclusive, unbounded (lower) (negate).
1054            (
1055                "NOT [* TO 50]",
1056                log_event!["message" => "6"],
1057                log_event!["message" => "3"],
1058            ),
1059            // Range - numeric, inclusive, unbounded (lower) (negate w/-).
1060            (
1061                "-[* TO 50]",
1062                log_event!["message" => "6"],
1063                log_event!["message" => "3"],
1064            ),
1065            // Range - numeric, inclusive, unbounded (both).
1066            ("[* TO *]", log_event!["message" => "foo"], log_event![]),
1067            // Range - numeric, inclusive, unbounded (both) (negate).
1068            ("NOT [* TO *]", log_event![], log_event!["message" => "foo"]),
1069            // Range - numeric, inclusive, unbounded (both) (negate w/-i).
1070            ("-[* TO *]", log_event![], log_event!["message" => "foo"]),
1071            // Range - numeric, inclusive, tag.
1072            (
1073                "a:[1 TO 10]",
1074                log_event!["tags" => vec!["a:1"]],
1075                log_event!["tags" => vec!["a:2"]],
1076            ),
1077            // Range - numeric, inclusive, tag (negate).
1078            (
1079                "NOT a:[1 TO 10]",
1080                log_event!["tags" => vec!["a:2"]],
1081                log_event!["tags" => vec!["a:1"]],
1082            ),
1083            // Range - numeric, inclusive, tag (negate w/-).
1084            (
1085                "-a:[1 TO 10]",
1086                log_event!["tags" => vec!["a:2"]],
1087                log_event!["tags" => vec!["a:1"]],
1088            ),
1089            // Range - numeric, inclusive, unbounded (upper), tag.
1090            (
1091                "a:[50 TO *]",
1092                log_event!["tags" => vec!["a:6"]],
1093                log_event!["tags" => vec!["a:40"]],
1094            ),
1095            // Range - numeric, inclusive, unbounded (upper), tag (negate).
1096            (
1097                "NOT a:[50 TO *]",
1098                log_event!["tags" => vec!["a:40"]],
1099                log_event!["tags" => vec!["a:6"]],
1100            ),
1101            // Range - numeric, inclusive, unbounded (upper), tag (negate w/-).
1102            (
1103                "-a:[50 TO *]",
1104                log_event!["tags" => vec!["a:40"]],
1105                log_event!["tags" => vec!["a:6"]],
1106            ),
1107            // Range - numeric, inclusive, unbounded (lower), tag.
1108            (
1109                "a:[* TO 50]",
1110                log_event!["tags" => vec!["a:400"]],
1111                log_event!["tags" => vec!["a:600"]],
1112            ),
1113            // Range - numeric, inclusive, unbounded (lower), tag (negate).
1114            (
1115                "NOT a:[* TO 50]",
1116                log_event!["tags" => vec!["a:600"]],
1117                log_event!["tags" => vec!["a:400"]],
1118            ),
1119            // Range - numeric, inclusive, unbounded (lower), tag (negate w/-).
1120            (
1121                "-a:[* TO 50]",
1122                log_event!["tags" => vec!["a:600"]],
1123                log_event!["tags" => vec!["a:400"]],
1124            ),
1125            // Range - numeric, inclusive, unbounded (both), tag.
1126            (
1127                "a:[* TO *]",
1128                log_event!["tags" => vec!["a:test"]],
1129                log_event!["tags" => vec!["b:test"]],
1130            ),
1131            // Range - numeric, inclusive, unbounded (both), tag (negate).
1132            (
1133                "NOT a:[* TO *]",
1134                log_event!["tags" => vec!["b:test"]],
1135                log_event!["tags" => vec!["a:test"]],
1136            ),
1137            // Range - numeric, inclusive, unbounded (both), tag (negate w/-).
1138            (
1139                "-a:[* TO *]",
1140                log_event!["tags" => vec!["b:test"]],
1141                log_event!["tags" => vec!["a:test"]],
1142            ),
1143            // Range - numeric, inclusive, attribute.
1144            ("@b:[1 TO 10]", log_event!["b" => 5], log_event!["b" => 11]),
1145            (
1146                "@b:[1 TO 100]",
1147                log_event!["b" => "10"],
1148                log_event!["b" => "2"],
1149            ),
1150            // Range - numeric, inclusive, attribute (negate).
1151            (
1152                "NOT @b:[1 TO 10]",
1153                log_event!["b" => 11],
1154                log_event!["b" => 5],
1155            ),
1156            (
1157                "NOT @b:[1 TO 100]",
1158                log_event!["b" => "2"],
1159                log_event!["b" => "10"],
1160            ),
1161            // Range - numeric, inclusive, attribute (negate w/-).
1162            ("-@b:[1 TO 10]", log_event!["b" => 11], log_event!["b" => 5]),
1163            (
1164                "NOT @b:[1 TO 100]",
1165                log_event!["b" => "2"],
1166                log_event!["b" => "10"],
1167            ),
1168            // Range - alpha, inclusive, attribute.
1169            ("@b:[a TO z]", log_event!["b" => "c"], log_event!["b" => 5]),
1170            // Range - alphanumeric, inclusive, attribute.
1171            (
1172                r#"@b:["1" TO "100"]"#,
1173                log_event!["b" => "10"],
1174                log_event!["b" => "2"],
1175            ),
1176            // Range - alphanumeric, inclusive, attribute (negate).
1177            (
1178                r#"NOT @b:["1" TO "100"]"#,
1179                log_event!["b" => "2"],
1180                log_event!["b" => "10"],
1181            ),
1182            // Range - alphanumeric, inclusive, attribute (negate).
1183            (
1184                r#"-@b:["1" TO "100"]"#,
1185                log_event!["b" => "2"],
1186                log_event!["b" => "10"],
1187            ),
1188            // Range - tag, exclusive.
1189            (
1190                "f:{1 TO 100}",
1191                log_event!["tags" => vec!["f:10"]],
1192                log_event!["tags" => vec!["f:1"]],
1193            ),
1194            (
1195                "f:{1 TO 100}",
1196                log_event!["tags" => vec!["f:10"]],
1197                log_event!["tags" => vec!["f:100"]],
1198            ),
1199            // Range - tag, exclusive (negate).
1200            (
1201                "NOT f:{1 TO 100}",
1202                log_event!["tags" => vec!["f:1"]],
1203                log_event!["tags" => vec!["f:10"]],
1204            ),
1205            (
1206                "NOT f:{1 TO 100}",
1207                log_event!["tags" => vec!["f:100"]],
1208                log_event!["tags" => vec!["f:10"]],
1209            ),
1210            // Range - tag, exclusive (negate w/-).
1211            (
1212                "-f:{1 TO 100}",
1213                log_event!["tags" => vec!["f:1"]],
1214                log_event!["tags" => vec!["f:10"]],
1215            ),
1216            (
1217                "-f:{1 TO 100}",
1218                log_event!["tags" => vec!["f:100"]],
1219                log_event!["tags" => vec!["f:10"]],
1220            ),
1221            // Range - attribute, exclusive.
1222            ("@f:{1 TO 100}", log_event!["f" => 50], log_event!["f" => 1]),
1223            (
1224                "@f:{1 TO 100}",
1225                log_event!["f" => 50],
1226                log_event!["f" => 100],
1227            ),
1228            // Range - attribute, exclusive (negate).
1229            (
1230                "NOT @f:{1 TO 100}",
1231                log_event!["f" => 1],
1232                log_event!["f" => 50],
1233            ),
1234            (
1235                "NOT @f:{1 TO 100}",
1236                log_event!["f" => 100],
1237                log_event!["f" => 50],
1238            ),
1239            // Range - attribute, exclusive (negate w/-).
1240            (
1241                "-@f:{1 TO 100}",
1242                log_event!["f" => 1],
1243                log_event!["f" => 50],
1244            ),
1245            (
1246                "-@f:{1 TO 100}",
1247                log_event!["f" => 100],
1248                log_event!["f" => 50],
1249            ),
1250            // OR of two values
1251            (
1252                "@field:(value1 OR value2)",
1253                log_event!["field" => "value1"],
1254                log_event!["field" => "value"],
1255            ),
1256            // OR of two values
1257            (
1258                "@field:value1 OR @field:value2",
1259                log_event!["field" => "value1"],
1260                log_event!["field" => "value"],
1261            ),
1262            // negate OR of two values
1263            (
1264                "-@field:value1 OR -@field:value2",
1265                log_event!["field" => "value"],
1266                log_event!["field" => "value2"],
1267            ),
1268            // default AND of two values
1269            (
1270                "@field:value @field2:value2",
1271                log_event!["field" => "value", "field2" => "value2"],
1272                log_event!["field" => "value", "field2" => "value3"],
1273            ),
1274            // handles newline
1275            (
1276                "@field:(value1 OR \n value2)",
1277                log_event!["field" => "value1"],
1278                log_event!["field" => "value"],
1279            ),
1280            // negate AND of bool and string
1281            (
1282                "NOT (@field:true AND @field2:value2)",
1283                log_event!["field" => false, "field2" => "value2"],
1284                log_event!["field" => true, "field2" => "value2"],
1285            ),
1286            // tags checks with 'ddtags' (DD Agent Source naming)
1287
1288            // Tag exists.
1289            (
1290                "_exists_:a",                          // Source
1291                log_event!["ddtags" => vec!["a:foo"]], // Pass
1292                log_event!["ddtags" => vec!["b:foo"]], // Fail
1293            ),
1294            // Tag exists with - in name.
1295            (
1296                "_exists_:a-b",                          // Source
1297                log_event!["ddtags" => vec!["a-b:foo"]], // Pass
1298                log_event!["ddtags" => vec!["ab:foo"]],  // Fail
1299            ),
1300            // Tag exists (negate).
1301            (
1302                "NOT _exists_:a",
1303                log_event!["ddtags" => vec!["b:foo"]],
1304                log_event!("ddtags" => vec!["a:foo"]),
1305            ),
1306            // Tag exists (negate w/-).
1307            (
1308                "-_exists_:a",
1309                log_event!["ddtags" => vec!["b:foo"]],
1310                log_event!["ddtags" => vec!["a:foo"]],
1311            ),
1312            // Tag doesn't exist.
1313            (
1314                "_missing_:a",
1315                log_event![],
1316                log_event!["ddtags" => vec!["a:foo"]],
1317            ),
1318            // Tag doesn't exist (negate).
1319            (
1320                "NOT _missing_:a",
1321                log_event!["ddtags" => vec!["a:foo"]],
1322                log_event![],
1323            ),
1324            // Tag doesn't exist (negate w/-).
1325            (
1326                "-_missing_:a",
1327                log_event!["ddtags" => vec!["a:foo"]],
1328                log_event![],
1329            ),
1330            // Tag match.
1331            (
1332                "a:bla",
1333                log_event!["ddtags" => vec!["a:bla"]],
1334                log_event!["ddtags" => vec!["b:bla"]],
1335            ),
1336            // Tag match (negate).
1337            (
1338                "NOT a:bla",
1339                log_event!["ddtags" => vec!["b:bla"]],
1340                log_event!["ddtags" => vec!["a:bla"]],
1341            ),
1342            // Reserved tag match (negate).
1343            (
1344                "NOT host:foo",
1345                log_event!["ddtags" => vec!["host:fo  o"]],
1346                log_event!["host" => "foo"],
1347            ),
1348            // Tag match (negate w/-).
1349            (
1350                "-a:bla",
1351                log_event!["ddtags" => vec!["b:bla"]],
1352                log_event!["ddtags" => vec!["a:bla"]],
1353            ),
1354            // Quoted tag match.
1355            (
1356                r#"a:"bla""#,
1357                log_event!["ddtags" => vec!["a:bla"]],
1358                log_event!["a" => "bla"],
1359            ),
1360            // Quoted tag match (negate).
1361            (
1362                r#"NOT a:"bla""#,
1363                log_event!["a" => "bla"],
1364                log_event!["ddtags" => vec!["a:bla"]],
1365            ),
1366            // Quoted tag match (negate w/-).
1367            (
1368                r#"-a:"bla""#,
1369                log_event!["a" => "bla"],
1370                log_event!["ddtags" => vec!["a:bla"]],
1371            ),
1372            // String attribute match.
1373            (
1374                "@a:bla",
1375                log_event!["a" => "bla"],
1376                log_event!["ddtags" => vec!["a:bla"]],
1377            ),
1378            // String attribute match (negate).
1379            (
1380                "NOT @a:bla",
1381                log_event!["ddtags" => vec!["a:bla"]],
1382                log_event!["a" => "bla"],
1383            ),
1384            // String attribute match (negate w/-).
1385            (
1386                "-@a:bla",
1387                log_event!["ddtags" => vec!["a:bla"]],
1388                log_event!["a" => "bla"],
1389            ),
1390            // Quoted attribute match.
1391            (
1392                r#"@a:"bla""#,
1393                log_event!["a" => "bla"],
1394                log_event!["ddtags" => vec!["a:bla"]],
1395            ),
1396            // Quoted attribute match (negate).
1397            (
1398                r#"NOT @a:"bla""#,
1399                log_event!["ddtags" => vec!["a:bla"]],
1400                log_event!["a" => "bla"],
1401            ),
1402            // Quoted attribute match (negate w/-).
1403            (
1404                r#"-@a:"bla""#,
1405                log_event!["ddtags" => vec!["a:bla"]],
1406                log_event!["a" => "bla"],
1407            ),
1408            // Integer attribute match.
1409            (
1410                "@a:200",
1411                log_event!["a" => 200],
1412                log_event!["ddtags" => vec!["a:200"]],
1413            ),
1414            // Float attribute match.
1415            (
1416                "@a:0.75",
1417                log_event!["a" => 0.75],
1418                log_event!["ddtags" => vec!["a:0.75"]],
1419            ),
1420            (
1421                "a:*bla",
1422                log_event!["ddtags" => vec!["a:foobla"]],
1423                log_event!["ddtags" => vec!["a:blafoo"]],
1424            ),
1425            // Wildcard prefix - tag (negate).
1426            (
1427                "NOT a:*bla",
1428                log_event!["ddtags" => vec!["a:blafoo"]],
1429                log_event!["ddtags" => vec!["a:foobla"]],
1430            ),
1431            // Wildcard prefix - tag (negate w/-).
1432            (
1433                "-a:*bla",
1434                log_event!["ddtags" => vec!["a:blafoo"]],
1435                log_event!["ddtags" => vec!["a:foobla"]],
1436            ),
1437            // Wildcard suffix - tag.
1438            (
1439                "b:bla*",
1440                log_event!["ddtags" => vec!["b:blabop"]],
1441                log_event!["ddtags" => vec!["b:bopbla"]],
1442            ),
1443            // Wildcard suffix - tag (negate).
1444            (
1445                "NOT b:bla*",
1446                log_event!["ddtags" => vec!["b:bopbla"]],
1447                log_event!["ddtags" => vec!["b:blabop"]],
1448            ),
1449            // Wildcard suffix - tag (negate w/-).
1450            (
1451                "-b:bla*",
1452                log_event!["ddtags" => vec!["b:bopbla"]],
1453                log_event!["ddtags" => vec!["b:blabop"]],
1454            ),
1455            // Multiple wildcards - tag.
1456            (
1457                "c:*b*la*",
1458                log_event!["ddtags" => vec!["c:foobla"]],
1459                log_event!["custom" => r#"{"title" => "foobla"}"#],
1460            ),
1461            // Multiple wildcards - tag (negate).
1462            (
1463                "NOT c:*b*la*",
1464                log_event!["custom" => r#"{"title" => "foobla"}"#],
1465                log_event!["ddtags" => vec!["c:foobla"]],
1466            ),
1467            // Multiple wildcards - tag (negate w/-).
1468            (
1469                "-c:*b*la*",
1470                log_event!["custom" => r#"{"title" => "foobla"}"#],
1471                log_event!["ddtags" => vec!["c:foobla"]],
1472            ),
1473            // Wildcard prefix - attribute.
1474            (
1475                "@a:*bla",
1476                log_event!["a" => "foobla"],
1477                log_event!["ddtags" => vec!["a:foobla"]],
1478            ),
1479            // Wildcard prefix - attribute (negate).
1480            (
1481                "NOT @a:*bla",
1482                log_event!["ddtags" => vec!["a:foobla"]],
1483                log_event!["a" => "foobla"],
1484            ),
1485            // Wildcard prefix - attribute (negate w/-).
1486            (
1487                "-@a:*bla",
1488                log_event!["ddtags" => vec!["a:foobla"]],
1489                log_event!["a" => "foobla"],
1490            ),
1491            // Wildcard suffix - attribute.
1492            (
1493                "@b:bla*",
1494                log_event!["b" => "blabop"],
1495                log_event!["ddtags" => vec!["b:blabop"]],
1496            ),
1497            // Wildcard suffix - attribute (negate).
1498            (
1499                "NOT @b:bla*",
1500                log_event!["ddtags" => vec!["b:blabop"]],
1501                log_event!["b" => "blabop"],
1502            ),
1503            // Wildcard suffix - attribute (negate w/-).
1504            (
1505                "-@b:bla*",
1506                log_event!["ddtags" => vec!["b:blabop"]],
1507                log_event!["b" => "blabop"],
1508            ),
1509            // Multiple wildcards - attribute.
1510            (
1511                "@c:*b*la*",
1512                log_event!["c" => "foobla"],
1513                log_event!["ddtags" => vec!["c:foobla"]],
1514            ),
1515            // Multiple wildcards - attribute (negate).
1516            (
1517                "NOT @c:*b*la*",
1518                log_event!["ddtags" => vec!["c:foobla"]],
1519                log_event!["c" => "foobla"],
1520            ),
1521            // Multiple wildcards - attribute (negate w/-).
1522            (
1523                "-@c:*b*la*",
1524                log_event!["ddtags" => vec!["c:foobla"]],
1525                log_event!["c" => "foobla"],
1526            ),
1527            // Special case for tags.
1528            (
1529                "tags:a",
1530                log_event!["ddtags" => vec!["a", "b", "c"]],
1531                log_event!["ddtags" => vec!["d", "e", "f"]],
1532            ),
1533            // Special case for tags (negate).
1534            (
1535                "NOT tags:a",
1536                log_event!["ddtags" => vec!["d", "e", "f"]],
1537                log_event!["ddtags" => vec!["a", "b", "c"]],
1538            ),
1539            // Special case for tags (negate w/-).
1540            (
1541                "-tags:a",
1542                log_event!["ddtags" => vec!["d", "e", "f"]],
1543                log_event!["ddtags" => vec!["a", "b", "c"]],
1544            ),
1545            // Special case: 'source' looks up on 'source' and 'ddsource' (OR condition)
1546            // source
1547            (
1548                "source:foo",
1549                log_event!["source" => "foo"],
1550                log_event!["tags" => vec!["source:foo"]],
1551            ),
1552            (
1553                "source:foo",
1554                log_event!["source" => "foo"],
1555                log_event!["source" => "foobar"],
1556            ),
1557            (
1558                "source:foo",
1559                log_event!["source" => "foo"],
1560                log_event!["source" => r#"{"value": "foo"}"#],
1561            ),
1562            // ddsource
1563            (
1564                "source:foo",
1565                log_event!["ddsource" => "foo"],
1566                log_event!["tags" => vec!["ddsource:foo"]],
1567            ),
1568            (
1569                "source:foo",
1570                log_event!["ddsource" => "foo"],
1571                log_event!["ddsource" => "foobar"],
1572            ),
1573            (
1574                "source:foo",
1575                log_event!["ddsource" => "foo"],
1576                log_event!["ddsource" => r#"{"value": "foo"}"#],
1577            ),
1578            // both source and ddsource
1579            (
1580                "source:foo",
1581                log_event!["source" => "foo", "ddsource" => "foo"],
1582                log_event!["source" => "foobar", "ddsource" => "foobar"],
1583            ),
1584            (
1585                "source:foo",
1586                log_event!["source" => "foo", "ddsource" => "foobar"],
1587                log_event!["source" => "foobar", "ddsource" => "foobar"],
1588            ),
1589            (
1590                "source:foo",
1591                log_event!["source" => "foobar", "ddsource" => "foo"],
1592                log_event!["source" => "foobar", "ddsource" => "foobar"],
1593            ),
1594        ]
1595    }
1596
1597    /// Test a `Matcher` by providing a `Filter<V>` and a processor that receives an
1598    /// `Event`, and returns a `V`. This allows testing against the pass/fail events that are returned
1599    /// from `get_checks()` and modifying into a type that allows for their processing.
1600    fn test_filter<V, F, P>(filter: F, processor: P)
1601    where
1602        V: std::fmt::Debug + Send + Sync + Clone + 'static,
1603        F: Filter<V> + Resolver,
1604        P: Fn(Event) -> V,
1605    {
1606        let checks = get_checks();
1607
1608        for (source, pass, fail) in checks {
1609            let node: QueryNode = source.parse().unwrap();
1610            let matcher = build_matcher(&node, &filter).unwrap();
1611
1612            assert!(matcher.run(&processor(pass)));
1613            assert!(!matcher.run(&processor(fail)));
1614        }
1615    }
1616
1617    #[test]
1618    /// Parse each Datadog Search Syntax query and check that it passes/fails.
1619    fn event_filter() {
1620        test_filter(EventFilter, |ev| ev.into_log())
1621    }
1622
1623    #[test]
1624    fn generate_config() {
1625        crate::test_util::test_generate_config::<DatadogSearchConfig>();
1626    }
1627
1628    #[test]
1629    fn check_datadog() {
1630        for (source, pass, fail) in get_checks() {
1631            let config: DatadogSearchConfig = source.parse().unwrap();
1632
1633            // Every query should build successfully.
1634            let cond = config
1635                .build(&Default::default())
1636                .unwrap_or_else(|_| panic!("build failed: {source}"));
1637
1638            assert!(
1639                cond.check_with_context(pass.clone()).0.is_ok(),
1640                "should pass: {}\nevent: {}",
1641                source,
1642                serde_json::to_string(&pass.as_log()).unwrap(),
1643            );
1644
1645            assert!(
1646                cond.check_with_context(fail.clone()).0.is_err(),
1647                "should fail: {}\nevent: {}",
1648                source,
1649                serde_json::to_string(&fail.as_log()).unwrap(),
1650            );
1651        }
1652    }
1653}