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#[configurable_component]
15#[derive(Clone, Debug, PartialEq)]
16pub struct DatadogSearchConfig {
17 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#[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
85fn 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
96impl 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 Field::Reserved(field) if field == "tags" => {
111 any_string_match_multiple(vec!["ddtags", "tags"], move |value| value == field)
112 }
113 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 Field::Default(field) => {
130 let re = word_regex(to_match);
131
132 string_match(&field, move |value| re.is_match(&value))
133 }
134 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 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 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 Field::Reserved(field) => {
158 let to_match = to_match.to_owned();
159
160 string_match(field, move |value| value == to_match)
161 }
162 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 Field::Default(field) => {
179 let re = word_regex(&format!("{prefix}*"));
180
181 string_match(field, move |value| re.is_match(&value))
182 }
183 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 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 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 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 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 (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 (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 (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 (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 (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 (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 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 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 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
356fn 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
371fn 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
391fn 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
408fn 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
420fn 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
445fn 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
455fn 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 fn get_checks() -> Vec<(&'static str, Event, Event)> {
483 vec![
484 (
486 "_exists_:a", log_event!["tags" => vec!["a:foo"]], log_event!["tags" => vec!["b:foo"]], ),
490 (
492 "_exists_:a-b", log_event!["tags" => vec!["a-b:foo"]], log_event!["tags" => vec!["ab:foo"]], ),
496 (
498 "NOT _exists_:a",
499 log_event!["tags" => vec!["b:foo"]],
500 log_event!("tags" => vec!["a:foo"]),
501 ),
502 (
504 "-_exists_:a",
505 log_event!["tags" => vec!["b:foo"]],
506 log_event!["tags" => vec!["a:foo"]],
507 ),
508 (
510 "_exists_:@b",
511 log_event!["b" => "foo"],
512 log_event!["a" => "foo"],
513 ),
514 (
525 "NOT _exists_:@b",
526 log_event!["a" => "foo"],
527 log_event!["b" => "foo"],
528 ),
529 (
531 "-_exists_:@b",
532 log_event!["a" => "foo"],
533 log_event!["b" => "foo"],
534 ),
535 (
537 "_missing_:a",
538 log_event![],
539 log_event!["tags" => vec!["a:foo"]],
540 ),
541 (
543 "NOT _missing_:a",
544 log_event!["tags" => vec!["a:foo"]],
545 log_event![],
546 ),
547 (
549 "-_missing_:a",
550 log_event!["tags" => vec!["a:foo"]],
551 log_event![],
552 ),
553 (
555 "_missing_:@b",
556 log_event!["a" => "foo"],
557 log_event!["b" => "foo"],
558 ),
559 (
561 "NOT _missing_:@b",
562 log_event!["b" => "foo"],
563 log_event!["a" => "foo"],
564 ),
565 (
567 "-_missing_:@b",
568 log_event!["b" => "foo"],
569 log_event!["a" => "foo"],
570 ),
571 ("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 (
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 (
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 (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 (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 (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 (
653 "a:bla",
654 log_event!["tags" => vec!["a:bla"]],
655 log_event!["tags" => vec!["b:bla"]],
656 ),
657 (
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 (
675 "NOT a:bla",
676 log_event!["tags" => vec!["b:bla"]],
677 log_event!["tags" => vec!["a:bla"]],
678 ),
679 (
681 "NOT host:foo",
682 log_event!["tags" => vec!["host:fo o"]],
683 log_event!["host" => "foo"],
684 ),
685 (
687 "-a:bla",
688 log_event!["tags" => vec!["b:bla"]],
689 log_event!["tags" => vec!["a:bla"]],
690 ),
691 (
693 "-trace_id:foo",
694 log_event![],
695 log_event!["trace_id" => "foo"],
696 ),
697 (
699 r#"a:"bla""#,
700 log_event!["tags" => vec!["a:bla"]],
701 log_event!["a" => "bla"],
702 ),
703 (
705 r#"NOT a:"bla""#,
706 log_event!["a" => "bla"],
707 log_event!["tags" => vec!["a:bla"]],
708 ),
709 (
711 r#"-a:"bla""#,
712 log_event!["a" => "bla"],
713 log_event!["tags" => vec!["a:bla"]],
714 ),
715 ("@a:true", log_event!["a" => true], log_event!["a" => false]),
717 (
719 "NOT @a:false",
720 log_event!["a" => true],
721 log_event!["a" => false],
722 ),
723 (
725 "@a:bla",
726 log_event!["a" => "bla"],
727 log_event!["tags" => vec!["a:bla"]],
728 ),
729 (
731 "NOT @a:bla",
732 log_event!["tags" => vec!["a:bla"]],
733 log_event!["a" => "bla"],
734 ),
735 ("@a:b", log_event!["a" => "b"], log_event!["a" => "c"]),
737 (
739 "@a:va\\/lue",
740 log_event!["a" => "va/lue"],
741 log_event!["a" => "value"],
742 ),
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 (
776 "@a:va\\~lue",
777 log_event!["a" => "va~lue"],
778 log_event!["a" => "value"],
779 ),
780 (
783 "@a:va\\^lue",
784 log_event!["a" => "va^lue"],
785 log_event!["a" => "value"],
786 ),
787 (
789 "@a:va/lue",
790 log_event!["a" => "va/lue"],
791 log_event!["a" => "value"],
792 ),
793 (
795 "-@a:bla",
796 log_event!["tags" => vec!["a:bla"]],
797 log_event!["a" => "bla"],
798 ),
799 (
801 r#"@a:"bla""#,
802 log_event!["a" => "bla"],
803 log_event!["tags" => vec!["a:bla"]],
804 ),
805 (
807 r#"NOT @a:"bla""#,
808 log_event!["tags" => vec!["a:bla"]],
809 log_event!["a" => "bla"],
810 ),
811 (
813 r#"-@a:"bla""#,
814 log_event!["tags" => vec!["a:bla"]],
815 log_event!["a" => "bla"],
816 ),
817 (
819 "@a:200",
820 log_event!["a" => 200],
821 log_event!["tags" => vec!["a:200"]],
822 ),
823 ("-@a:200", log_event!["a" => 199], log_event!["a" => 200]),
825 (
827 "@a:0.75",
828 log_event!["a" => 0.75],
829 log_event!["tags" => vec!["a:0.75"]],
830 ),
831 ("-@a:0.75", log_event!["a" => 0.74], log_event!["a" => 0.75]),
833 (
835 "*bla",
836 log_event!["message" => "foobla"],
837 log_event!["message" => "blafoo"],
838 ),
839 (
841 "NOT *bla",
842 log_event!["message" => "blafoo"],
843 log_event!["message" => "foobla"],
844 ),
845 (
847 "-*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 "NOT bla*",
860 log_event!["message" => "foobla"],
861 log_event!["message" => "blafoo"],
862 ),
863 (
865 "-bla*",
866 log_event!["message" => "foobla"],
867 log_event!["message" => "blafoo"],
868 ),
869 ("*b*la*", log_event!["message" => "foobla"], log_event![]),
871 (
873 "NOT *b*la*",
874 log_event![],
875 log_event!["message" => "foobla"],
876 ),
877 ("-*b*la*", log_event![], log_event!["message" => "foobla"]),
879 (
881 "a:*bla",
882 log_event!["tags" => vec!["a:foobla"]],
883 log_event!["tags" => vec!["a:blafoo"]],
884 ),
885 (
887 "NOT a:*bla",
888 log_event!["tags" => vec!["a:blafoo"]],
889 log_event!["tags" => vec!["a:foobla"]],
890 ),
891 (
893 "-a:*bla",
894 log_event!["tags" => vec!["a:blafoo"]],
895 log_event!["tags" => vec!["a:foobla"]],
896 ),
897 (
899 "b:bla*",
900 log_event!["tags" => vec!["b:blabop"]],
901 log_event!["tags" => vec!["b:bopbla"]],
902 ),
903 (
905 "NOT b:bla*",
906 log_event!["tags" => vec!["b:bopbla"]],
907 log_event!["tags" => vec!["b:blabop"]],
908 ),
909 (
911 "-b:bla*",
912 log_event!["tags" => vec!["b:bopbla"]],
913 log_event!["tags" => vec!["b:blabop"]],
914 ),
915 (
917 "c:*b*la*",
918 log_event!["tags" => vec!["c:foobla"]],
919 log_event!["custom" => r#"{"title" => "foobla"}"#],
920 ),
921 (
923 "NOT c:*b*la*",
924 log_event!["custom" => r#"{"title" => "foobla"}"#],
925 log_event!["tags" => vec!["c:foobla"]],
926 ),
927 (
929 "-c:*b*la*",
930 log_event!["custom" => r#"{"title" => "foobla"}"#],
931 log_event!["tags" => vec!["c:foobla"]],
932 ),
933 (
935 "@a:*bla",
936 log_event!["a" => "foobla"],
937 log_event!["tags" => vec!["a:foobla"]],
938 ),
939 (
941 "NOT @a:*bla",
942 log_event!["tags" => vec!["a:foobla"]],
943 log_event!["a" => "foobla"],
944 ),
945 (
947 "-@a:*bla",
948 log_event!["tags" => vec!["a:foobla"]],
949 log_event!["a" => "foobla"],
950 ),
951 (
953 "@b:bla*",
954 log_event!["b" => "blabop"],
955 log_event!["tags" => vec!["b:blabop"]],
956 ),
957 (
959 "NOT @b:bla*",
960 log_event!["tags" => vec!["b:blabop"]],
961 log_event!["b" => "blabop"],
962 ),
963 (
965 "-@b:bla*",
966 log_event!["tags" => vec!["b:blabop"]],
967 log_event!["b" => "blabop"],
968 ),
969 (
971 "@c:*b*la*",
972 log_event!["c" => "foobla"],
973 log_event!["tags" => vec!["c:foobla"]],
974 ),
975 (
977 "NOT @c:*b*la*",
978 log_event!["tags" => vec!["c:foobla"]],
979 log_event!["c" => "foobla"],
980 ),
981 (
983 "-@c:*b*la*",
984 log_event!["tags" => vec!["c:foobla"]],
985 log_event!["c" => "foobla"],
986 ),
987 (
989 "tags:a",
990 log_event!["tags" => vec!["a", "b", "c"]],
991 log_event!["tags" => vec!["d", "e", "f"]],
992 ),
993 (
995 "NOT tags:a",
996 log_event!["tags" => vec!["d", "e", "f"]],
997 log_event!["tags" => vec!["a", "b", "c"]],
998 ),
999 (
1001 "-tags:a",
1002 log_event!["tags" => vec!["d", "e", "f"]],
1003 log_event!["tags" => vec!["a", "b", "c"]],
1004 ),
1005 (
1007 "[1 TO 10]",
1008 log_event!["message" => "1"],
1009 log_event!["message" => "2"],
1010 ),
1011 (
1013 "NOT [1 TO 10]",
1014 log_event!["message" => "2"],
1015 log_event!["message" => "1"],
1016 ),
1017 (
1019 "-[1 TO 10]",
1020 log_event!["message" => "2"],
1021 log_event!["message" => "1"],
1022 ),
1023 (
1025 "[50 TO *]",
1026 log_event!["message" => "6"],
1027 log_event!["message" => "40"],
1028 ),
1029 (
1031 "NOT [50 TO *]",
1032 log_event!["message" => "40"],
1033 log_event!["message" => "6"],
1034 ),
1035 (
1037 "-[50 TO *]",
1038 log_event!["message" => "40"],
1039 log_event!["message" => "6"],
1040 ),
1041 (
1043 "[* TO 50]",
1044 log_event!["message" => "3"],
1045 log_event!["message" => "6"],
1046 ),
1047 (
1049 "NOT [* TO 50]",
1050 log_event!["message" => "6"],
1051 log_event!["message" => "3"],
1052 ),
1053 (
1055 "-[* TO 50]",
1056 log_event!["message" => "6"],
1057 log_event!["message" => "3"],
1058 ),
1059 ("[* TO *]", log_event!["message" => "foo"], log_event![]),
1061 ("NOT [* TO *]", log_event![], log_event!["message" => "foo"]),
1063 ("-[* TO *]", log_event![], log_event!["message" => "foo"]),
1065 (
1067 "a:[1 TO 10]",
1068 log_event!["tags" => vec!["a:1"]],
1069 log_event!["tags" => vec!["a:2"]],
1070 ),
1071 (
1073 "NOT a:[1 TO 10]",
1074 log_event!["tags" => vec!["a:2"]],
1075 log_event!["tags" => vec!["a:1"]],
1076 ),
1077 (
1079 "-a:[1 TO 10]",
1080 log_event!["tags" => vec!["a:2"]],
1081 log_event!["tags" => vec!["a:1"]],
1082 ),
1083 (
1085 "a:[50 TO *]",
1086 log_event!["tags" => vec!["a:6"]],
1087 log_event!["tags" => vec!["a:40"]],
1088 ),
1089 (
1091 "NOT a:[50 TO *]",
1092 log_event!["tags" => vec!["a:40"]],
1093 log_event!["tags" => vec!["a:6"]],
1094 ),
1095 (
1097 "-a:[50 TO *]",
1098 log_event!["tags" => vec!["a:40"]],
1099 log_event!["tags" => vec!["a:6"]],
1100 ),
1101 (
1103 "a:[* TO 50]",
1104 log_event!["tags" => vec!["a:400"]],
1105 log_event!["tags" => vec!["a:600"]],
1106 ),
1107 (
1109 "NOT a:[* TO 50]",
1110 log_event!["tags" => vec!["a:600"]],
1111 log_event!["tags" => vec!["a:400"]],
1112 ),
1113 (
1115 "-a:[* TO 50]",
1116 log_event!["tags" => vec!["a:600"]],
1117 log_event!["tags" => vec!["a:400"]],
1118 ),
1119 (
1121 "a:[* TO *]",
1122 log_event!["tags" => vec!["a:test"]],
1123 log_event!["tags" => vec!["b:test"]],
1124 ),
1125 (
1127 "NOT a:[* TO *]",
1128 log_event!["tags" => vec!["b:test"]],
1129 log_event!["tags" => vec!["a:test"]],
1130 ),
1131 (
1133 "-a:[* TO *]",
1134 log_event!["tags" => vec!["b:test"]],
1135 log_event!["tags" => vec!["a:test"]],
1136 ),
1137 ("@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 (
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 ("-@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 ("@b:[a TO z]", log_event!["b" => "c"], log_event!["b" => 5]),
1164 (
1166 r#"@b:["1" TO "100"]"#,
1167 log_event!["b" => "10"],
1168 log_event!["b" => "2"],
1169 ),
1170 (
1172 r#"NOT @b:["1" TO "100"]"#,
1173 log_event!["b" => "2"],
1174 log_event!["b" => "10"],
1175 ),
1176 (
1178 r#"-@b:["1" TO "100"]"#,
1179 log_event!["b" => "2"],
1180 log_event!["b" => "10"],
1181 ),
1182 (
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 (
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 (
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 ("@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 (
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 (
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 (
1246 "@field:(value1 OR value2)",
1247 log_event!["field" => "value1"],
1248 log_event!["field" => "value"],
1249 ),
1250 (
1252 "@field:value1 OR @field:value2",
1253 log_event!["field" => "value1"],
1254 log_event!["field" => "value"],
1255 ),
1256 (
1258 "-@field:value1 OR -@field:value2",
1259 log_event!["field" => "value"],
1260 log_event!["field" => "value2"],
1261 ),
1262 (
1264 "@field:value @field2:value2",
1265 log_event!["field" => "value", "field2" => "value2"],
1266 log_event!["field" => "value", "field2" => "value3"],
1267 ),
1268 (
1270 "@field:(value1 OR \n value2)",
1271 log_event!["field" => "value1"],
1272 log_event!["field" => "value"],
1273 ),
1274 (
1276 "NOT (@field:true AND @field2:value2)",
1277 log_event!["field" => false, "field2" => "value2"],
1278 log_event!["field" => true, "field2" => "value2"],
1279 ),
1280 (
1284 "_exists_:a", log_event!["ddtags" => vec!["a:foo"]], log_event!["ddtags" => vec!["b:foo"]], ),
1288 (
1290 "_exists_:a-b", log_event!["ddtags" => vec!["a-b:foo"]], log_event!["ddtags" => vec!["ab:foo"]], ),
1294 (
1296 "NOT _exists_:a",
1297 log_event!["ddtags" => vec!["b:foo"]],
1298 log_event!("ddtags" => vec!["a:foo"]),
1299 ),
1300 (
1302 "-_exists_:a",
1303 log_event!["ddtags" => vec!["b:foo"]],
1304 log_event!["ddtags" => vec!["a:foo"]],
1305 ),
1306 (
1308 "_missing_:a",
1309 log_event![],
1310 log_event!["ddtags" => vec!["a:foo"]],
1311 ),
1312 (
1314 "NOT _missing_:a",
1315 log_event!["ddtags" => vec!["a:foo"]],
1316 log_event![],
1317 ),
1318 (
1320 "-_missing_:a",
1321 log_event!["ddtags" => vec!["a:foo"]],
1322 log_event![],
1323 ),
1324 (
1326 "a:bla",
1327 log_event!["ddtags" => vec!["a:bla"]],
1328 log_event!["ddtags" => vec!["b:bla"]],
1329 ),
1330 (
1332 "NOT a:bla",
1333 log_event!["ddtags" => vec!["b:bla"]],
1334 log_event!["ddtags" => vec!["a:bla"]],
1335 ),
1336 (
1338 "NOT host:foo",
1339 log_event!["ddtags" => vec!["host:fo o"]],
1340 log_event!["host" => "foo"],
1341 ),
1342 (
1344 "-a:bla",
1345 log_event!["ddtags" => vec!["b:bla"]],
1346 log_event!["ddtags" => vec!["a:bla"]],
1347 ),
1348 (
1350 r#"a:"bla""#,
1351 log_event!["ddtags" => vec!["a:bla"]],
1352 log_event!["a" => "bla"],
1353 ),
1354 (
1356 r#"NOT a:"bla""#,
1357 log_event!["a" => "bla"],
1358 log_event!["ddtags" => vec!["a:bla"]],
1359 ),
1360 (
1362 r#"-a:"bla""#,
1363 log_event!["a" => "bla"],
1364 log_event!["ddtags" => vec!["a:bla"]],
1365 ),
1366 (
1368 "@a:bla",
1369 log_event!["a" => "bla"],
1370 log_event!["ddtags" => vec!["a:bla"]],
1371 ),
1372 (
1374 "NOT @a:bla",
1375 log_event!["ddtags" => vec!["a:bla"]],
1376 log_event!["a" => "bla"],
1377 ),
1378 (
1380 "-@a:bla",
1381 log_event!["ddtags" => vec!["a:bla"]],
1382 log_event!["a" => "bla"],
1383 ),
1384 (
1386 r#"@a:"bla""#,
1387 log_event!["a" => "bla"],
1388 log_event!["ddtags" => vec!["a:bla"]],
1389 ),
1390 (
1392 r#"NOT @a:"bla""#,
1393 log_event!["ddtags" => vec!["a:bla"]],
1394 log_event!["a" => "bla"],
1395 ),
1396 (
1398 r#"-@a:"bla""#,
1399 log_event!["ddtags" => vec!["a:bla"]],
1400 log_event!["a" => "bla"],
1401 ),
1402 (
1404 "@a:200",
1405 log_event!["a" => 200],
1406 log_event!["ddtags" => vec!["a:200"]],
1407 ),
1408 (
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 (
1421 "NOT a:*bla",
1422 log_event!["ddtags" => vec!["a:blafoo"]],
1423 log_event!["ddtags" => vec!["a:foobla"]],
1424 ),
1425 (
1427 "-a:*bla",
1428 log_event!["ddtags" => vec!["a:blafoo"]],
1429 log_event!["ddtags" => vec!["a:foobla"]],
1430 ),
1431 (
1433 "b:bla*",
1434 log_event!["ddtags" => vec!["b:blabop"]],
1435 log_event!["ddtags" => vec!["b:bopbla"]],
1436 ),
1437 (
1439 "NOT b:bla*",
1440 log_event!["ddtags" => vec!["b:bopbla"]],
1441 log_event!["ddtags" => vec!["b:blabop"]],
1442 ),
1443 (
1445 "-b:bla*",
1446 log_event!["ddtags" => vec!["b:bopbla"]],
1447 log_event!["ddtags" => vec!["b:blabop"]],
1448 ),
1449 (
1451 "c:*b*la*",
1452 log_event!["ddtags" => vec!["c:foobla"]],
1453 log_event!["custom" => r#"{"title" => "foobla"}"#],
1454 ),
1455 (
1457 "NOT c:*b*la*",
1458 log_event!["custom" => r#"{"title" => "foobla"}"#],
1459 log_event!["ddtags" => vec!["c:foobla"]],
1460 ),
1461 (
1463 "-c:*b*la*",
1464 log_event!["custom" => r#"{"title" => "foobla"}"#],
1465 log_event!["ddtags" => vec!["c:foobla"]],
1466 ),
1467 (
1469 "@a:*bla",
1470 log_event!["a" => "foobla"],
1471 log_event!["ddtags" => vec!["a:foobla"]],
1472 ),
1473 (
1475 "NOT @a:*bla",
1476 log_event!["ddtags" => vec!["a:foobla"]],
1477 log_event!["a" => "foobla"],
1478 ),
1479 (
1481 "-@a:*bla",
1482 log_event!["ddtags" => vec!["a:foobla"]],
1483 log_event!["a" => "foobla"],
1484 ),
1485 (
1487 "@b:bla*",
1488 log_event!["b" => "blabop"],
1489 log_event!["ddtags" => vec!["b:blabop"]],
1490 ),
1491 (
1493 "NOT @b:bla*",
1494 log_event!["ddtags" => vec!["b:blabop"]],
1495 log_event!["b" => "blabop"],
1496 ),
1497 (
1499 "-@b:bla*",
1500 log_event!["ddtags" => vec!["b:blabop"]],
1501 log_event!["b" => "blabop"],
1502 ),
1503 (
1505 "@c:*b*la*",
1506 log_event!["c" => "foobla"],
1507 log_event!["ddtags" => vec!["c:foobla"]],
1508 ),
1509 (
1511 "NOT @c:*b*la*",
1512 log_event!["ddtags" => vec!["c:foobla"]],
1513 log_event!["c" => "foobla"],
1514 ),
1515 (
1517 "-@c:*b*la*",
1518 log_event!["ddtags" => vec!["c:foobla"]],
1519 log_event!["c" => "foobla"],
1520 ),
1521 (
1523 "tags:a",
1524 log_event!["ddtags" => vec!["a", "b", "c"]],
1525 log_event!["ddtags" => vec!["d", "e", "f"]],
1526 ),
1527 (
1529 "NOT tags:a",
1530 log_event!["ddtags" => vec!["d", "e", "f"]],
1531 log_event!["ddtags" => vec!["a", "b", "c"]],
1532 ),
1533 (
1535 "-tags:a",
1536 log_event!["ddtags" => vec!["d", "e", "f"]],
1537 log_event!["ddtags" => vec!["a", "b", "c"]],
1538 ),
1539 (
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 (
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 (
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 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 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 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}