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#[configurable_component]
21#[derive(Clone, Debug, PartialEq)]
22pub struct DatadogSearchConfig {
23 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#[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
91fn 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
102impl 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 Field::Reserved(field) if field == "tags" => {
117 any_string_match_multiple(vec!["ddtags", "tags"], move |value| value == field)
118 }
119 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 Field::Default(field) => {
136 let re = word_regex(to_match);
137
138 string_match(&field, move |value| re.is_match(&value))
139 }
140 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 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 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 Field::Reserved(field) => {
164 let to_match = to_match.to_owned();
165
166 string_match(field, move |value| value == to_match)
167 }
168 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 Field::Default(field) => {
185 let re = word_regex(&format!("{prefix}*"));
186
187 string_match(field, move |value| re.is_match(&value))
188 }
189 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 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 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 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 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 (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 (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 (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 (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 (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 (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 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 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 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
362fn 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
377fn 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
397fn 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
414fn 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
426fn 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
451fn 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
461fn 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 fn get_checks() -> Vec<(&'static str, Event, Event)> {
489 vec![
490 (
492 "_exists_:a", log_event!["tags" => vec!["a:foo"]], log_event!["tags" => vec!["b:foo"]], ),
496 (
498 "_exists_:a-b", log_event!["tags" => vec!["a-b:foo"]], log_event!["tags" => vec!["ab:foo"]], ),
502 (
504 "NOT _exists_:a",
505 log_event!["tags" => vec!["b:foo"]],
506 log_event!("tags" => vec!["a:foo"]),
507 ),
508 (
510 "-_exists_:a",
511 log_event!["tags" => vec!["b:foo"]],
512 log_event!["tags" => vec!["a:foo"]],
513 ),
514 (
516 "_exists_:@b",
517 log_event!["b" => "foo"],
518 log_event!["a" => "foo"],
519 ),
520 (
531 "NOT _exists_:@b",
532 log_event!["a" => "foo"],
533 log_event!["b" => "foo"],
534 ),
535 (
537 "-_exists_:@b",
538 log_event!["a" => "foo"],
539 log_event!["b" => "foo"],
540 ),
541 (
543 "_missing_:a",
544 log_event![],
545 log_event!["tags" => vec!["a:foo"]],
546 ),
547 (
549 "NOT _missing_:a",
550 log_event!["tags" => vec!["a:foo"]],
551 log_event![],
552 ),
553 (
555 "-_missing_:a",
556 log_event!["tags" => vec!["a:foo"]],
557 log_event![],
558 ),
559 (
561 "_missing_:@b",
562 log_event!["a" => "foo"],
563 log_event!["b" => "foo"],
564 ),
565 (
567 "NOT _missing_:@b",
568 log_event!["b" => "foo"],
569 log_event!["a" => "foo"],
570 ),
571 (
573 "-_missing_:@b",
574 log_event!["b" => "foo"],
575 log_event!["a" => "foo"],
576 ),
577 ("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 (
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 (
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 (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 (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 (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 (
659 "a:bla",
660 log_event!["tags" => vec!["a:bla"]],
661 log_event!["tags" => vec!["b:bla"]],
662 ),
663 (
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 (
681 "NOT a:bla",
682 log_event!["tags" => vec!["b:bla"]],
683 log_event!["tags" => vec!["a:bla"]],
684 ),
685 (
687 "NOT host:foo",
688 log_event!["tags" => vec!["host:fo o"]],
689 log_event!["host" => "foo"],
690 ),
691 (
693 "-a:bla",
694 log_event!["tags" => vec!["b:bla"]],
695 log_event!["tags" => vec!["a:bla"]],
696 ),
697 (
699 "-trace_id:foo",
700 log_event![],
701 log_event!["trace_id" => "foo"],
702 ),
703 (
705 r#"a:"bla""#,
706 log_event!["tags" => vec!["a:bla"]],
707 log_event!["a" => "bla"],
708 ),
709 (
711 r#"NOT a:"bla""#,
712 log_event!["a" => "bla"],
713 log_event!["tags" => vec!["a:bla"]],
714 ),
715 (
717 r#"-a:"bla""#,
718 log_event!["a" => "bla"],
719 log_event!["tags" => vec!["a:bla"]],
720 ),
721 ("@a:true", log_event!["a" => true], log_event!["a" => false]),
723 (
725 "NOT @a:false",
726 log_event!["a" => true],
727 log_event!["a" => false],
728 ),
729 (
731 "@a:bla",
732 log_event!["a" => "bla"],
733 log_event!["tags" => vec!["a:bla"]],
734 ),
735 (
737 "NOT @a:bla",
738 log_event!["tags" => vec!["a:bla"]],
739 log_event!["a" => "bla"],
740 ),
741 ("@a:b", log_event!["a" => "b"], log_event!["a" => "c"]),
743 (
745 "@a:va\\/lue",
746 log_event!["a" => "va/lue"],
747 log_event!["a" => "value"],
748 ),
749 (
751 "@a:va\\&&lue",
752 log_event!["a" => "va&&lue"],
753 log_event!["a" => "value"],
754 ),
755 (
757 "@a:va\\ lue",
758 log_event!["a" => "va lue"],
759 log_event!["a" => "value"],
760 ),
761 (
763 "@a:va\\||lue",
764 log_event!["a" => "va||lue"],
765 log_event!["a" => "value"],
766 ),
767 (
769 "@a:va\\(lue",
770 log_event!["a" => "va(lue"],
771 log_event!["a" => "value"],
772 ),
773 (
775 "@a:va\\*lue",
776 log_event!["a" => "va*lue"],
777 log_event!["a" => "value"],
778 ),
779 (
782 "@a:va\\~lue",
783 log_event!["a" => "va~lue"],
784 log_event!["a" => "value"],
785 ),
786 (
789 "@a:va\\^lue",
790 log_event!["a" => "va^lue"],
791 log_event!["a" => "value"],
792 ),
793 (
795 "@a:va/lue",
796 log_event!["a" => "va/lue"],
797 log_event!["a" => "value"],
798 ),
799 (
801 "-@a:bla",
802 log_event!["tags" => vec!["a:bla"]],
803 log_event!["a" => "bla"],
804 ),
805 (
807 r#"@a:"bla""#,
808 log_event!["a" => "bla"],
809 log_event!["tags" => vec!["a:bla"]],
810 ),
811 (
813 r#"NOT @a:"bla""#,
814 log_event!["tags" => vec!["a:bla"]],
815 log_event!["a" => "bla"],
816 ),
817 (
819 r#"-@a:"bla""#,
820 log_event!["tags" => vec!["a:bla"]],
821 log_event!["a" => "bla"],
822 ),
823 (
825 "@a:200",
826 log_event!["a" => 200],
827 log_event!["tags" => vec!["a:200"]],
828 ),
829 ("-@a:200", log_event!["a" => 199], log_event!["a" => 200]),
831 (
833 "@a:0.75",
834 log_event!["a" => 0.75],
835 log_event!["tags" => vec!["a:0.75"]],
836 ),
837 ("-@a:0.75", log_event!["a" => 0.74], log_event!["a" => 0.75]),
839 (
841 "*bla",
842 log_event!["message" => "foobla"],
843 log_event!["message" => "blafoo"],
844 ),
845 (
847 "NOT *bla",
848 log_event!["message" => "blafoo"],
849 log_event!["message" => "foobla"],
850 ),
851 (
853 "-*bla",
854 log_event!["message" => "blafoo"],
855 log_event!["message" => "foobla"],
856 ),
857 (
859 "bla*",
860 log_event!["message" => "blafoo"],
861 log_event!["message" => "foobla"],
862 ),
863 (
865 "NOT bla*",
866 log_event!["message" => "foobla"],
867 log_event!["message" => "blafoo"],
868 ),
869 (
871 "-bla*",
872 log_event!["message" => "foobla"],
873 log_event!["message" => "blafoo"],
874 ),
875 ("*b*la*", log_event!["message" => "foobla"], log_event![]),
877 (
879 "NOT *b*la*",
880 log_event![],
881 log_event!["message" => "foobla"],
882 ),
883 ("-*b*la*", log_event![], log_event!["message" => "foobla"]),
885 (
887 "a:*bla",
888 log_event!["tags" => vec!["a:foobla"]],
889 log_event!["tags" => vec!["a:blafoo"]],
890 ),
891 (
893 "NOT a:*bla",
894 log_event!["tags" => vec!["a:blafoo"]],
895 log_event!["tags" => vec!["a:foobla"]],
896 ),
897 (
899 "-a:*bla",
900 log_event!["tags" => vec!["a:blafoo"]],
901 log_event!["tags" => vec!["a:foobla"]],
902 ),
903 (
905 "b:bla*",
906 log_event!["tags" => vec!["b:blabop"]],
907 log_event!["tags" => vec!["b:bopbla"]],
908 ),
909 (
911 "NOT b:bla*",
912 log_event!["tags" => vec!["b:bopbla"]],
913 log_event!["tags" => vec!["b:blabop"]],
914 ),
915 (
917 "-b:bla*",
918 log_event!["tags" => vec!["b:bopbla"]],
919 log_event!["tags" => vec!["b:blabop"]],
920 ),
921 (
923 "c:*b*la*",
924 log_event!["tags" => vec!["c:foobla"]],
925 log_event!["custom" => r#"{"title" => "foobla"}"#],
926 ),
927 (
929 "NOT c:*b*la*",
930 log_event!["custom" => r#"{"title" => "foobla"}"#],
931 log_event!["tags" => vec!["c:foobla"]],
932 ),
933 (
935 "-c:*b*la*",
936 log_event!["custom" => r#"{"title" => "foobla"}"#],
937 log_event!["tags" => vec!["c:foobla"]],
938 ),
939 (
941 "@a:*bla",
942 log_event!["a" => "foobla"],
943 log_event!["tags" => vec!["a:foobla"]],
944 ),
945 (
947 "NOT @a:*bla",
948 log_event!["tags" => vec!["a:foobla"]],
949 log_event!["a" => "foobla"],
950 ),
951 (
953 "-@a:*bla",
954 log_event!["tags" => vec!["a:foobla"]],
955 log_event!["a" => "foobla"],
956 ),
957 (
959 "@b:bla*",
960 log_event!["b" => "blabop"],
961 log_event!["tags" => vec!["b:blabop"]],
962 ),
963 (
965 "NOT @b:bla*",
966 log_event!["tags" => vec!["b:blabop"]],
967 log_event!["b" => "blabop"],
968 ),
969 (
971 "-@b:bla*",
972 log_event!["tags" => vec!["b:blabop"]],
973 log_event!["b" => "blabop"],
974 ),
975 (
977 "@c:*b*la*",
978 log_event!["c" => "foobla"],
979 log_event!["tags" => vec!["c:foobla"]],
980 ),
981 (
983 "NOT @c:*b*la*",
984 log_event!["tags" => vec!["c:foobla"]],
985 log_event!["c" => "foobla"],
986 ),
987 (
989 "-@c:*b*la*",
990 log_event!["tags" => vec!["c:foobla"]],
991 log_event!["c" => "foobla"],
992 ),
993 (
995 "tags:a",
996 log_event!["tags" => vec!["a", "b", "c"]],
997 log_event!["tags" => vec!["d", "e", "f"]],
998 ),
999 (
1001 "NOT tags:a",
1002 log_event!["tags" => vec!["d", "e", "f"]],
1003 log_event!["tags" => vec!["a", "b", "c"]],
1004 ),
1005 (
1007 "-tags:a",
1008 log_event!["tags" => vec!["d", "e", "f"]],
1009 log_event!["tags" => vec!["a", "b", "c"]],
1010 ),
1011 (
1013 "[1 TO 10]",
1014 log_event!["message" => "1"],
1015 log_event!["message" => "2"],
1016 ),
1017 (
1019 "NOT [1 TO 10]",
1020 log_event!["message" => "2"],
1021 log_event!["message" => "1"],
1022 ),
1023 (
1025 "-[1 TO 10]",
1026 log_event!["message" => "2"],
1027 log_event!["message" => "1"],
1028 ),
1029 (
1031 "[50 TO *]",
1032 log_event!["message" => "6"],
1033 log_event!["message" => "40"],
1034 ),
1035 (
1037 "NOT [50 TO *]",
1038 log_event!["message" => "40"],
1039 log_event!["message" => "6"],
1040 ),
1041 (
1043 "-[50 TO *]",
1044 log_event!["message" => "40"],
1045 log_event!["message" => "6"],
1046 ),
1047 (
1049 "[* TO 50]",
1050 log_event!["message" => "3"],
1051 log_event!["message" => "6"],
1052 ),
1053 (
1055 "NOT [* TO 50]",
1056 log_event!["message" => "6"],
1057 log_event!["message" => "3"],
1058 ),
1059 (
1061 "-[* TO 50]",
1062 log_event!["message" => "6"],
1063 log_event!["message" => "3"],
1064 ),
1065 ("[* TO *]", log_event!["message" => "foo"], log_event![]),
1067 ("NOT [* TO *]", log_event![], log_event!["message" => "foo"]),
1069 ("-[* TO *]", log_event![], log_event!["message" => "foo"]),
1071 (
1073 "a:[1 TO 10]",
1074 log_event!["tags" => vec!["a:1"]],
1075 log_event!["tags" => vec!["a:2"]],
1076 ),
1077 (
1079 "NOT a:[1 TO 10]",
1080 log_event!["tags" => vec!["a:2"]],
1081 log_event!["tags" => vec!["a:1"]],
1082 ),
1083 (
1085 "-a:[1 TO 10]",
1086 log_event!["tags" => vec!["a:2"]],
1087 log_event!["tags" => vec!["a:1"]],
1088 ),
1089 (
1091 "a:[50 TO *]",
1092 log_event!["tags" => vec!["a:6"]],
1093 log_event!["tags" => vec!["a:40"]],
1094 ),
1095 (
1097 "NOT a:[50 TO *]",
1098 log_event!["tags" => vec!["a:40"]],
1099 log_event!["tags" => vec!["a:6"]],
1100 ),
1101 (
1103 "-a:[50 TO *]",
1104 log_event!["tags" => vec!["a:40"]],
1105 log_event!["tags" => vec!["a:6"]],
1106 ),
1107 (
1109 "a:[* TO 50]",
1110 log_event!["tags" => vec!["a:400"]],
1111 log_event!["tags" => vec!["a:600"]],
1112 ),
1113 (
1115 "NOT a:[* TO 50]",
1116 log_event!["tags" => vec!["a:600"]],
1117 log_event!["tags" => vec!["a:400"]],
1118 ),
1119 (
1121 "-a:[* TO 50]",
1122 log_event!["tags" => vec!["a:600"]],
1123 log_event!["tags" => vec!["a:400"]],
1124 ),
1125 (
1127 "a:[* TO *]",
1128 log_event!["tags" => vec!["a:test"]],
1129 log_event!["tags" => vec!["b:test"]],
1130 ),
1131 (
1133 "NOT a:[* TO *]",
1134 log_event!["tags" => vec!["b:test"]],
1135 log_event!["tags" => vec!["a:test"]],
1136 ),
1137 (
1139 "-a:[* TO *]",
1140 log_event!["tags" => vec!["b:test"]],
1141 log_event!["tags" => vec!["a:test"]],
1142 ),
1143 ("@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 (
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 ("-@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 ("@b:[a TO z]", log_event!["b" => "c"], log_event!["b" => 5]),
1170 (
1172 r#"@b:["1" TO "100"]"#,
1173 log_event!["b" => "10"],
1174 log_event!["b" => "2"],
1175 ),
1176 (
1178 r#"NOT @b:["1" TO "100"]"#,
1179 log_event!["b" => "2"],
1180 log_event!["b" => "10"],
1181 ),
1182 (
1184 r#"-@b:["1" TO "100"]"#,
1185 log_event!["b" => "2"],
1186 log_event!["b" => "10"],
1187 ),
1188 (
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 (
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 (
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 ("@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 (
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 (
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 (
1252 "@field:(value1 OR value2)",
1253 log_event!["field" => "value1"],
1254 log_event!["field" => "value"],
1255 ),
1256 (
1258 "@field:value1 OR @field:value2",
1259 log_event!["field" => "value1"],
1260 log_event!["field" => "value"],
1261 ),
1262 (
1264 "-@field:value1 OR -@field:value2",
1265 log_event!["field" => "value"],
1266 log_event!["field" => "value2"],
1267 ),
1268 (
1270 "@field:value @field2:value2",
1271 log_event!["field" => "value", "field2" => "value2"],
1272 log_event!["field" => "value", "field2" => "value3"],
1273 ),
1274 (
1276 "@field:(value1 OR \n value2)",
1277 log_event!["field" => "value1"],
1278 log_event!["field" => "value"],
1279 ),
1280 (
1282 "NOT (@field:true AND @field2:value2)",
1283 log_event!["field" => false, "field2" => "value2"],
1284 log_event!["field" => true, "field2" => "value2"],
1285 ),
1286 (
1290 "_exists_:a", log_event!["ddtags" => vec!["a:foo"]], log_event!["ddtags" => vec!["b:foo"]], ),
1294 (
1296 "_exists_:a-b", log_event!["ddtags" => vec!["a-b:foo"]], log_event!["ddtags" => vec!["ab:foo"]], ),
1300 (
1302 "NOT _exists_:a",
1303 log_event!["ddtags" => vec!["b:foo"]],
1304 log_event!("ddtags" => vec!["a:foo"]),
1305 ),
1306 (
1308 "-_exists_:a",
1309 log_event!["ddtags" => vec!["b:foo"]],
1310 log_event!["ddtags" => vec!["a:foo"]],
1311 ),
1312 (
1314 "_missing_:a",
1315 log_event![],
1316 log_event!["ddtags" => vec!["a:foo"]],
1317 ),
1318 (
1320 "NOT _missing_:a",
1321 log_event!["ddtags" => vec!["a:foo"]],
1322 log_event![],
1323 ),
1324 (
1326 "-_missing_:a",
1327 log_event!["ddtags" => vec!["a:foo"]],
1328 log_event![],
1329 ),
1330 (
1332 "a:bla",
1333 log_event!["ddtags" => vec!["a:bla"]],
1334 log_event!["ddtags" => vec!["b:bla"]],
1335 ),
1336 (
1338 "NOT a:bla",
1339 log_event!["ddtags" => vec!["b:bla"]],
1340 log_event!["ddtags" => vec!["a:bla"]],
1341 ),
1342 (
1344 "NOT host:foo",
1345 log_event!["ddtags" => vec!["host:fo o"]],
1346 log_event!["host" => "foo"],
1347 ),
1348 (
1350 "-a:bla",
1351 log_event!["ddtags" => vec!["b:bla"]],
1352 log_event!["ddtags" => vec!["a:bla"]],
1353 ),
1354 (
1356 r#"a:"bla""#,
1357 log_event!["ddtags" => vec!["a:bla"]],
1358 log_event!["a" => "bla"],
1359 ),
1360 (
1362 r#"NOT a:"bla""#,
1363 log_event!["a" => "bla"],
1364 log_event!["ddtags" => vec!["a:bla"]],
1365 ),
1366 (
1368 r#"-a:"bla""#,
1369 log_event!["a" => "bla"],
1370 log_event!["ddtags" => vec!["a:bla"]],
1371 ),
1372 (
1374 "@a:bla",
1375 log_event!["a" => "bla"],
1376 log_event!["ddtags" => vec!["a:bla"]],
1377 ),
1378 (
1380 "NOT @a:bla",
1381 log_event!["ddtags" => vec!["a:bla"]],
1382 log_event!["a" => "bla"],
1383 ),
1384 (
1386 "-@a:bla",
1387 log_event!["ddtags" => vec!["a:bla"]],
1388 log_event!["a" => "bla"],
1389 ),
1390 (
1392 r#"@a:"bla""#,
1393 log_event!["a" => "bla"],
1394 log_event!["ddtags" => vec!["a:bla"]],
1395 ),
1396 (
1398 r#"NOT @a:"bla""#,
1399 log_event!["ddtags" => vec!["a:bla"]],
1400 log_event!["a" => "bla"],
1401 ),
1402 (
1404 r#"-@a:"bla""#,
1405 log_event!["ddtags" => vec!["a:bla"]],
1406 log_event!["a" => "bla"],
1407 ),
1408 (
1410 "@a:200",
1411 log_event!["a" => 200],
1412 log_event!["ddtags" => vec!["a:200"]],
1413 ),
1414 (
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 (
1427 "NOT a:*bla",
1428 log_event!["ddtags" => vec!["a:blafoo"]],
1429 log_event!["ddtags" => vec!["a:foobla"]],
1430 ),
1431 (
1433 "-a:*bla",
1434 log_event!["ddtags" => vec!["a:blafoo"]],
1435 log_event!["ddtags" => vec!["a:foobla"]],
1436 ),
1437 (
1439 "b:bla*",
1440 log_event!["ddtags" => vec!["b:blabop"]],
1441 log_event!["ddtags" => vec!["b:bopbla"]],
1442 ),
1443 (
1445 "NOT b:bla*",
1446 log_event!["ddtags" => vec!["b:bopbla"]],
1447 log_event!["ddtags" => vec!["b:blabop"]],
1448 ),
1449 (
1451 "-b:bla*",
1452 log_event!["ddtags" => vec!["b:bopbla"]],
1453 log_event!["ddtags" => vec!["b:blabop"]],
1454 ),
1455 (
1457 "c:*b*la*",
1458 log_event!["ddtags" => vec!["c:foobla"]],
1459 log_event!["custom" => r#"{"title" => "foobla"}"#],
1460 ),
1461 (
1463 "NOT c:*b*la*",
1464 log_event!["custom" => r#"{"title" => "foobla"}"#],
1465 log_event!["ddtags" => vec!["c:foobla"]],
1466 ),
1467 (
1469 "-c:*b*la*",
1470 log_event!["custom" => r#"{"title" => "foobla"}"#],
1471 log_event!["ddtags" => vec!["c:foobla"]],
1472 ),
1473 (
1475 "@a:*bla",
1476 log_event!["a" => "foobla"],
1477 log_event!["ddtags" => vec!["a:foobla"]],
1478 ),
1479 (
1481 "NOT @a:*bla",
1482 log_event!["ddtags" => vec!["a:foobla"]],
1483 log_event!["a" => "foobla"],
1484 ),
1485 (
1487 "-@a:*bla",
1488 log_event!["ddtags" => vec!["a:foobla"]],
1489 log_event!["a" => "foobla"],
1490 ),
1491 (
1493 "@b:bla*",
1494 log_event!["b" => "blabop"],
1495 log_event!["ddtags" => vec!["b:blabop"]],
1496 ),
1497 (
1499 "NOT @b:bla*",
1500 log_event!["ddtags" => vec!["b:blabop"]],
1501 log_event!["b" => "blabop"],
1502 ),
1503 (
1505 "-@b:bla*",
1506 log_event!["ddtags" => vec!["b:blabop"]],
1507 log_event!["b" => "blabop"],
1508 ),
1509 (
1511 "@c:*b*la*",
1512 log_event!["c" => "foobla"],
1513 log_event!["ddtags" => vec!["c:foobla"]],
1514 ),
1515 (
1517 "NOT @c:*b*la*",
1518 log_event!["ddtags" => vec!["c:foobla"]],
1519 log_event!["c" => "foobla"],
1520 ),
1521 (
1523 "-@c:*b*la*",
1524 log_event!["ddtags" => vec!["c:foobla"]],
1525 log_event!["c" => "foobla"],
1526 ),
1527 (
1529 "tags:a",
1530 log_event!["ddtags" => vec!["a", "b", "c"]],
1531 log_event!["ddtags" => vec!["d", "e", "f"]],
1532 ),
1533 (
1535 "NOT tags:a",
1536 log_event!["ddtags" => vec!["d", "e", "f"]],
1537 log_event!["ddtags" => vec!["a", "b", "c"]],
1538 ),
1539 (
1541 "-tags:a",
1542 log_event!["ddtags" => vec!["d", "e", "f"]],
1543 log_event!["ddtags" => vec!["a", "b", "c"]],
1544 ),
1545 (
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 (
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 (
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 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 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 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}