vector/conditions/
datadog_search.rs

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