1use std::{borrow::Cow, str::FromStr};
2
3use bytes::Bytes;
4use vector_lib::{
5 configurable::configurable_component,
6 event::{Event, EventRef, 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<LogEvent>>> {
37 Ok(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<LogEvent>>,
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<'a>(&self, event: impl Into<EventRef<'a>>) -> bool {
72 match event.into() {
73 EventRef::Log(log) => self.matcher.run(log),
74 _ => false,
75 }
76 }
77}
78
79impl Conditional for DatadogSearchRunner {
80 fn check(&self, event: Event) -> (bool, Event) {
81 (self.matches(&event), event)
82 }
83}
84
85impl ConditionalConfig for DatadogSearchConfig {
86 fn build(
87 &self,
88 _enrichment_tables: &vector_lib::enrichment::TableRegistry,
89 ) -> crate::Result<Condition> {
90 Ok(Condition::DatadogSearch(self.try_into()?))
91 }
92}
93
94#[derive(Default, Clone)]
95struct EventFilter;
96
97impl Resolver for EventFilter {}
99
100impl Filter<LogEvent> for EventFilter {
101 fn exists(&self, field: Field) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
102 Ok(match field {
103 Field::Tag(tag) => {
104 let starts_with = format!("{tag}:");
105
106 any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
107 value == tag || value.starts_with(&starts_with)
108 })
109 }
110 Field::Reserved(field) if field == "tags" => {
112 any_string_match_multiple(vec!["ddtags", "tags"], move |value| value == field)
113 }
114 Field::Reserved(field) if field == "source" => {
116 exists_match_multiple(vec!["ddsource", "source"])
117 }
118
119 Field::Default(f) | Field::Attribute(f) | Field::Reserved(f) => exists_match(f),
120 })
121 }
122
123 fn equals(
124 &self,
125 field: Field,
126 to_match: &str,
127 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
128 Ok(match field {
129 Field::Default(field) => {
131 let re = word_regex(to_match);
132
133 string_match(&field, move |value| re.is_match(&value))
134 }
135 Field::Reserved(field) if field == "tags" => {
137 let to_match = to_match.to_owned();
138
139 array_match_multiple(vec!["ddtags", "tags"], move |values| {
140 values.contains(&Value::Bytes(Bytes::copy_from_slice(to_match.as_bytes())))
141 })
142 }
143 Field::Tag(tag) => {
145 let value_bytes = Value::Bytes(format!("{tag}:{to_match}").into());
146
147 array_match_multiple(vec!["ddtags", "tags"], move |values| {
148 values.contains(&value_bytes)
149 })
150 }
151 Field::Reserved(field) if field == "source" => {
153 let to_match = to_match.to_owned();
154
155 string_match_multiple(vec!["ddsource", "source"], move |value| value == to_match)
156 }
157 Field::Reserved(field) => {
159 let to_match = to_match.to_owned();
160
161 string_match(field, move |value| value == to_match)
162 }
163 Field::Attribute(field) => {
165 let to_match = to_match.to_owned();
166
167 simple_scalar_match(field, move |value| value == to_match)
168 }
169 })
170 }
171
172 fn prefix(
173 &self,
174 field: Field,
175 prefix: &str,
176 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
177 Ok(match field {
178 Field::Default(field) => {
180 let re = word_regex(&format!("{prefix}*"));
181
182 string_match(field, move |value| re.is_match(&value))
183 }
184 Field::Tag(tag) => {
186 let starts_with = format!("{tag}:{prefix}");
187
188 any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
189 value.starts_with(&starts_with)
190 })
191 }
192 Field::Reserved(field) if field == "source" => {
194 let prefix = prefix.to_owned();
195
196 string_match_multiple(vec!["ddsource", "source"], move |value| {
197 value.starts_with(&prefix)
198 })
199 }
200
201 Field::Reserved(field) | Field::Attribute(field) => {
203 let prefix = prefix.to_owned();
204
205 string_match(field, move |value| value.starts_with(&prefix))
206 }
207 })
208 }
209
210 fn wildcard(
211 &self,
212 field: Field,
213 wildcard: &str,
214 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
215 Ok(match field {
216 Field::Default(field) => {
217 let re = word_regex(wildcard);
218
219 string_match(field, move |value| re.is_match(&value))
220 }
221 Field::Tag(tag) => {
222 let re = wildcard_regex(&format!("{tag}:{wildcard}"));
223
224 any_string_match_multiple(vec!["ddtags", "tags"], move |value| re.is_match(&value))
225 }
226 Field::Reserved(field) if field == "source" => {
228 let re = wildcard_regex(wildcard);
229
230 string_match_multiple(vec!["ddsource", "source"], move |value| re.is_match(&value))
231 }
232 Field::Reserved(field) | Field::Attribute(field) => {
233 let re = wildcard_regex(wildcard);
234
235 string_match(field, move |value| re.is_match(&value))
236 }
237 })
238 }
239
240 fn compare(
241 &self,
242 field: Field,
243 comparator: Comparison,
244 comparison_value: ComparisonValue,
245 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
246 let rhs = Cow::from(comparison_value.to_string());
247
248 Ok(match field {
249 Field::Attribute(f) => {
251 Run::boxed(move |log: &LogEvent| {
252 match (
253 log.parse_path_and_get_value(f.as_str()).ok().flatten(),
254 &comparison_value,
255 ) {
256 (Some(Value::Integer(lhs)), ComparisonValue::Integer(rhs)) => {
258 match comparator {
259 Comparison::Lt => lhs < rhs,
260 Comparison::Lte => lhs <= rhs,
261 Comparison::Gt => lhs > rhs,
262 Comparison::Gte => lhs >= rhs,
263 }
264 }
265 (Some(Value::Integer(lhs)), ComparisonValue::Float(rhs)) => {
267 match comparator {
268 Comparison::Lt => (*lhs as f64) < *rhs,
269 Comparison::Lte => *lhs as f64 <= *rhs,
270 Comparison::Gt => *lhs as f64 > *rhs,
271 Comparison::Gte => *lhs as f64 >= *rhs,
272 }
273 }
274 (Some(Value::Float(lhs)), ComparisonValue::Float(rhs)) => {
276 match comparator {
277 Comparison::Lt => lhs.into_inner() < *rhs,
278 Comparison::Lte => lhs.into_inner() <= *rhs,
279 Comparison::Gt => lhs.into_inner() > *rhs,
280 Comparison::Gte => lhs.into_inner() >= *rhs,
281 }
282 }
283 (Some(Value::Float(lhs)), ComparisonValue::Integer(rhs)) => {
285 match comparator {
286 Comparison::Lt => lhs.into_inner() < *rhs as f64,
287 Comparison::Lte => lhs.into_inner() <= *rhs as f64,
288 Comparison::Gt => lhs.into_inner() > *rhs as f64,
289 Comparison::Gte => lhs.into_inner() >= *rhs as f64,
290 }
291 }
292 (Some(Value::Bytes(v)), ComparisonValue::String(rhs)) => {
294 let lhs = String::from_utf8_lossy(v);
295 let rhs = Cow::from(rhs);
296
297 match comparator {
298 Comparison::Lt => lhs < rhs,
299 Comparison::Lte => lhs <= rhs,
300 Comparison::Gt => lhs > rhs,
301 Comparison::Gte => lhs >= rhs,
302 }
303 }
304 (Some(Value::Bytes(v)), _) => {
306 let lhs = String::from_utf8_lossy(v);
307
308 match comparator {
309 Comparison::Lt => lhs < rhs,
310 Comparison::Lte => lhs <= rhs,
311 Comparison::Gt => lhs > rhs,
312 Comparison::Gte => lhs >= rhs,
313 }
314 }
315 _ => false,
316 }
317 })
318 }
319 Field::Tag(tag) => any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
321 match value.split_once(':') {
322 Some((t, lhs)) if t == tag => {
323 let lhs = Cow::from(lhs);
324
325 match comparator {
326 Comparison::Lt => lhs < rhs,
327 Comparison::Lte => lhs <= rhs,
328 Comparison::Gt => lhs > rhs,
329 Comparison::Gte => lhs >= rhs,
330 }
331 }
332 _ => false,
333 }
334 }),
335 Field::Reserved(field) if field == "source" => {
337 string_match_multiple(vec!["ddsource", "source"], move |lhs| match comparator {
338 Comparison::Lt => lhs < rhs,
339 Comparison::Lte => lhs <= rhs,
340 Comparison::Gt => lhs > rhs,
341 Comparison::Gte => lhs >= rhs,
342 })
343 }
344 Field::Default(field) | Field::Reserved(field) => {
346 string_match(field, move |lhs| match comparator {
347 Comparison::Lt => lhs < rhs,
348 Comparison::Lte => lhs <= rhs,
349 Comparison::Gt => lhs > rhs,
350 Comparison::Gte => lhs >= rhs,
351 })
352 }
353 })
354 }
355}
356
357fn exists_match<S>(field: S) -> Box<dyn Matcher<LogEvent>>
359where
360 S: Into<String>,
361{
362 let field = field.into();
363
364 Run::boxed(move |log: &LogEvent| {
365 log.parse_path_and_get_value(field.as_str())
366 .ok()
367 .flatten()
368 .is_some()
369 })
370}
371
372fn simple_scalar_match<S, F>(field: S, func: F) -> Box<dyn Matcher<LogEvent>>
375where
376 S: Into<String>,
377 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
378{
379 let field = field.into();
380
381 Run::boxed(move |log: &LogEvent| {
382 match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
383 Some(Value::Boolean(v)) => func(v.to_string().into()),
384 Some(Value::Bytes(v)) => func(String::from_utf8_lossy(v)),
385 Some(Value::Integer(v)) => func(v.to_string().into()),
386 Some(Value::Float(v)) => func(v.to_string().into()),
387 _ => false,
388 }
389 })
390}
391
392fn string_match<S, F>(field: S, func: F) -> Box<dyn Matcher<LogEvent>>
395where
396 S: Into<String>,
397 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
398{
399 let field = field.into();
400
401 Run::boxed(move |log: &LogEvent| {
402 match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
403 Some(Value::Bytes(v)) => func(String::from_utf8_lossy(v)),
404 _ => false,
405 }
406 })
407}
408
409fn exists_match_multiple<S>(fields: Vec<S>) -> Box<dyn Matcher<LogEvent>>
411where
412 S: Into<String> + Clone + Send + Sync + 'static,
413{
414 Run::boxed(move |log: &LogEvent| {
415 fields
416 .iter()
417 .any(|field| exists_match(field.clone()).run(log))
418 })
419}
420
421fn string_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
424where
425 S: Into<String> + Clone + Send + Sync + 'static,
426 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
427{
428 Run::boxed(move |log: &LogEvent| {
429 fields
430 .iter()
431 .any(|field| string_match(field.clone(), func.clone()).run(log))
432 })
433}
434
435fn any_string_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
436where
437 S: Into<String> + Clone + Send + Sync + 'static,
438 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
439{
440 any_match_multiple(fields, move |value| {
441 let bytes = value.coerce_to_bytes();
442 func(String::from_utf8_lossy(&bytes))
443 })
444}
445
446fn any_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
449where
450 S: Into<String> + Clone + Send + Sync + 'static,
451 F: Fn(&Value) -> bool + Send + Sync + Clone + 'static,
452{
453 array_match_multiple(fields, move |values| values.iter().any(&func))
454}
455
456fn array_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
459where
460 S: Into<String> + Clone + Send + Sync + 'static,
461 F: Fn(&Vec<Value>) -> bool + Send + Sync + Clone + 'static,
462{
463 Run::boxed(move |log: &LogEvent| {
464 fields.iter().any(|field| {
465 let field = field.clone().into();
466 match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
467 Some(Value::Array(values)) => func(values),
468 _ => false,
469 }
470 })
471 })
472}
473
474#[cfg(test)]
475mod test {
476 use super::*;
477 use crate::log_event;
478
479 fn get_checks() -> Vec<(&'static str, Event, Event)> {
484 vec![
485 (
487 "_exists_:a", log_event!["tags" => vec!["a:foo"]], log_event!["tags" => vec!["b:foo"]], ),
491 (
493 "_exists_:a-b", log_event!["tags" => vec!["a-b:foo"]], log_event!["tags" => vec!["ab:foo"]], ),
497 (
499 "NOT _exists_:a",
500 log_event!["tags" => vec!["b:foo"]],
501 log_event!("tags" => vec!["a:foo"]),
502 ),
503 (
505 "-_exists_:a",
506 log_event!["tags" => vec!["b:foo"]],
507 log_event!["tags" => vec!["a:foo"]],
508 ),
509 (
511 "_exists_:@b",
512 log_event!["b" => "foo"],
513 log_event!["a" => "foo"],
514 ),
515 (
526 "NOT _exists_:@b",
527 log_event!["a" => "foo"],
528 log_event!["b" => "foo"],
529 ),
530 (
532 "-_exists_:@b",
533 log_event!["a" => "foo"],
534 log_event!["b" => "foo"],
535 ),
536 (
538 "_missing_:a",
539 log_event![],
540 log_event!["tags" => vec!["a:foo"]],
541 ),
542 (
544 "NOT _missing_:a",
545 log_event!["tags" => vec!["a:foo"]],
546 log_event![],
547 ),
548 (
550 "-_missing_:a",
551 log_event!["tags" => vec!["a:foo"]],
552 log_event![],
553 ),
554 (
556 "_missing_:@b",
557 log_event!["a" => "foo"],
558 log_event!["b" => "foo"],
559 ),
560 (
562 "NOT _missing_:@b",
563 log_event!["b" => "foo"],
564 log_event!["a" => "foo"],
565 ),
566 (
568 "-_missing_:@b",
569 log_event!["b" => "foo"],
570 log_event!["a" => "foo"],
571 ),
572 ("bla", log_event!["message" => "bla"], log_event![]),
574 (
575 "foo",
576 log_event!["message" => r#"{"key": "foo"}"#],
577 log_event![],
578 ),
579 (
580 "bar",
581 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
582 log_event![],
583 ),
584 (
586 "NOT bla",
587 log_event!["message" => "nothing"],
588 log_event!["message" => "bla"],
589 ),
590 (
591 "NOT foo",
592 log_event![],
593 log_event!["message" => r#"{"key": "foo"}"#],
594 ),
595 (
596 "NOT bar",
597 log_event![],
598 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
599 ),
600 (
602 "-bla",
603 log_event!["message" => "nothing"],
604 log_event!["message" => "bla"],
605 ),
606 (
607 "-foo",
608 log_event![],
609 log_event!["message" => r#"{"key": "foo"}"#],
610 ),
611 (
612 "-bar",
613 log_event![],
614 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
615 ),
616 (r#""bla""#, log_event!["message" => "bla"], log_event![]),
618 (
619 r#""foo""#,
620 log_event!["message" => r#"{"key": "foo"}"#],
621 log_event![],
622 ),
623 (
624 r#""bar""#,
625 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
626 log_event![],
627 ),
628 (r#"NOT "bla""#, log_event![], log_event!["message" => "bla"]),
630 (
631 r#"NOT "foo""#,
632 log_event![],
633 log_event!["message" => r#"{"key": "foo"}"#],
634 ),
635 (
636 r#"NOT "bar""#,
637 log_event![],
638 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
639 ),
640 (r#"-"bla""#, log_event![], log_event!["message" => "bla"]),
642 (
643 r#"NOT "foo""#,
644 log_event![],
645 log_event!["message" => r#"{"key": "foo"}"#],
646 ),
647 (
648 r#"NOT "bar""#,
649 log_event![],
650 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
651 ),
652 (
654 "a:bla",
655 log_event!["tags" => vec!["a:bla"]],
656 log_event!["tags" => vec!["b:bla"]],
657 ),
658 (
660 "host:foo",
661 log_event!["host" => "foo"],
662 log_event!["tags" => vec!["host:foo"]],
663 ),
664 (
665 "host:foo",
666 log_event!["host" => "foo"],
667 log_event!["host" => "foobar"],
668 ),
669 (
670 "host:foo",
671 log_event!["host" => "foo"],
672 log_event!["host" => r#"{"value": "foo"}"#],
673 ),
674 (
676 "NOT a:bla",
677 log_event!["tags" => vec!["b:bla"]],
678 log_event!["tags" => vec!["a:bla"]],
679 ),
680 (
682 "NOT host:foo",
683 log_event!["tags" => vec!["host:fo o"]],
684 log_event!["host" => "foo"],
685 ),
686 (
688 "-a:bla",
689 log_event!["tags" => vec!["b:bla"]],
690 log_event!["tags" => vec!["a:bla"]],
691 ),
692 (
694 "-trace_id:foo",
695 log_event![],
696 log_event!["trace_id" => "foo"],
697 ),
698 (
700 r#"a:"bla""#,
701 log_event!["tags" => vec!["a:bla"]],
702 log_event!["a" => "bla"],
703 ),
704 (
706 r#"NOT a:"bla""#,
707 log_event!["a" => "bla"],
708 log_event!["tags" => vec!["a:bla"]],
709 ),
710 (
712 r#"-a:"bla""#,
713 log_event!["a" => "bla"],
714 log_event!["tags" => vec!["a:bla"]],
715 ),
716 ("@a:true", log_event!["a" => true], log_event!["a" => false]),
718 (
720 "NOT @a:false",
721 log_event!["a" => true],
722 log_event!["a" => false],
723 ),
724 (
726 "@a:bla",
727 log_event!["a" => "bla"],
728 log_event!["tags" => vec!["a:bla"]],
729 ),
730 (
732 "NOT @a:bla",
733 log_event!["tags" => vec!["a:bla"]],
734 log_event!["a" => "bla"],
735 ),
736 ("@a:b", log_event!["a" => "b"], log_event!["a" => "c"]),
738 (
740 "@a:va\\/lue",
741 log_event!["a" => "va/lue"],
742 log_event!["a" => "value"],
743 ),
744 (
746 "@a:va\\&&lue",
747 log_event!["a" => "va&&lue"],
748 log_event!["a" => "value"],
749 ),
750 (
752 "@a:va\\ lue",
753 log_event!["a" => "va lue"],
754 log_event!["a" => "value"],
755 ),
756 (
758 "@a:va\\||lue",
759 log_event!["a" => "va||lue"],
760 log_event!["a" => "value"],
761 ),
762 (
764 "@a:va\\(lue",
765 log_event!["a" => "va(lue"],
766 log_event!["a" => "value"],
767 ),
768 (
770 "@a:va\\*lue",
771 log_event!["a" => "va*lue"],
772 log_event!["a" => "value"],
773 ),
774 (
777 "@a:va\\~lue",
778 log_event!["a" => "va~lue"],
779 log_event!["a" => "value"],
780 ),
781 (
784 "@a:va\\^lue",
785 log_event!["a" => "va^lue"],
786 log_event!["a" => "value"],
787 ),
788 (
790 "@a:va/lue",
791 log_event!["a" => "va/lue"],
792 log_event!["a" => "value"],
793 ),
794 (
796 "-@a:bla",
797 log_event!["tags" => vec!["a:bla"]],
798 log_event!["a" => "bla"],
799 ),
800 (
802 r#"@a:"bla""#,
803 log_event!["a" => "bla"],
804 log_event!["tags" => vec!["a:bla"]],
805 ),
806 (
808 r#"NOT @a:"bla""#,
809 log_event!["tags" => vec!["a:bla"]],
810 log_event!["a" => "bla"],
811 ),
812 (
814 r#"-@a:"bla""#,
815 log_event!["tags" => vec!["a:bla"]],
816 log_event!["a" => "bla"],
817 ),
818 (
820 "@a:200",
821 log_event!["a" => 200],
822 log_event!["tags" => vec!["a:200"]],
823 ),
824 ("-@a:200", log_event!["a" => 199], log_event!["a" => 200]),
826 (
828 "@a:0.75",
829 log_event!["a" => 0.75],
830 log_event!["tags" => vec!["a:0.75"]],
831 ),
832 ("-@a:0.75", log_event!["a" => 0.74], log_event!["a" => 0.75]),
834 (
836 "@a.b:x",
837 log_event!["a" => serde_json::json!({"b": "x"})],
838 Event::Log(LogEvent::from(Value::from(serde_json::json!({"a.b": "x"})))),
839 ),
840 (
842 r#"@\"a.b\":x"#,
843 Event::Log(LogEvent::from(Value::from(serde_json::json!({"a.b": "x"})))),
844 log_event!["a" => serde_json::json!({"b": "x"})],
845 ),
846 (
848 "*bla",
849 log_event!["message" => "foobla"],
850 log_event!["message" => "blafoo"],
851 ),
852 (
854 "NOT *bla",
855 log_event!["message" => "blafoo"],
856 log_event!["message" => "foobla"],
857 ),
858 (
860 "-*bla",
861 log_event!["message" => "blafoo"],
862 log_event!["message" => "foobla"],
863 ),
864 (
866 "bla*",
867 log_event!["message" => "blafoo"],
868 log_event!["message" => "foobla"],
869 ),
870 (
872 "NOT bla*",
873 log_event!["message" => "foobla"],
874 log_event!["message" => "blafoo"],
875 ),
876 (
878 "-bla*",
879 log_event!["message" => "foobla"],
880 log_event!["message" => "blafoo"],
881 ),
882 ("*b*la*", log_event!["message" => "foobla"], log_event![]),
884 (
886 "NOT *b*la*",
887 log_event![],
888 log_event!["message" => "foobla"],
889 ),
890 ("-*b*la*", log_event![], log_event!["message" => "foobla"]),
892 (
894 "a:*bla",
895 log_event!["tags" => vec!["a:foobla"]],
896 log_event!["tags" => vec!["a:blafoo"]],
897 ),
898 (
900 "NOT a:*bla",
901 log_event!["tags" => vec!["a:blafoo"]],
902 log_event!["tags" => vec!["a:foobla"]],
903 ),
904 (
906 "-a:*bla",
907 log_event!["tags" => vec!["a:blafoo"]],
908 log_event!["tags" => vec!["a:foobla"]],
909 ),
910 (
912 "b:bla*",
913 log_event!["tags" => vec!["b:blabop"]],
914 log_event!["tags" => vec!["b:bopbla"]],
915 ),
916 (
918 "NOT b:bla*",
919 log_event!["tags" => vec!["b:bopbla"]],
920 log_event!["tags" => vec!["b:blabop"]],
921 ),
922 (
924 "-b:bla*",
925 log_event!["tags" => vec!["b:bopbla"]],
926 log_event!["tags" => vec!["b:blabop"]],
927 ),
928 (
930 "c:*b*la*",
931 log_event!["tags" => vec!["c:foobla"]],
932 log_event!["custom" => r#"{"title" => "foobla"}"#],
933 ),
934 (
936 "NOT c:*b*la*",
937 log_event!["custom" => r#"{"title" => "foobla"}"#],
938 log_event!["tags" => vec!["c:foobla"]],
939 ),
940 (
942 "-c:*b*la*",
943 log_event!["custom" => r#"{"title" => "foobla"}"#],
944 log_event!["tags" => vec!["c:foobla"]],
945 ),
946 (
948 "@a:*bla",
949 log_event!["a" => "foobla"],
950 log_event!["tags" => vec!["a:foobla"]],
951 ),
952 (
954 "NOT @a:*bla",
955 log_event!["tags" => vec!["a:foobla"]],
956 log_event!["a" => "foobla"],
957 ),
958 (
960 "-@a:*bla",
961 log_event!["tags" => vec!["a:foobla"]],
962 log_event!["a" => "foobla"],
963 ),
964 (
966 "@b:bla*",
967 log_event!["b" => "blabop"],
968 log_event!["tags" => vec!["b:blabop"]],
969 ),
970 (
972 "NOT @b:bla*",
973 log_event!["tags" => vec!["b:blabop"]],
974 log_event!["b" => "blabop"],
975 ),
976 (
978 "-@b:bla*",
979 log_event!["tags" => vec!["b:blabop"]],
980 log_event!["b" => "blabop"],
981 ),
982 (
984 "@c:*b*la*",
985 log_event!["c" => "foobla"],
986 log_event!["tags" => vec!["c:foobla"]],
987 ),
988 (
990 "NOT @c:*b*la*",
991 log_event!["tags" => vec!["c:foobla"]],
992 log_event!["c" => "foobla"],
993 ),
994 (
996 "-@c:*b*la*",
997 log_event!["tags" => vec!["c:foobla"]],
998 log_event!["c" => "foobla"],
999 ),
1000 (
1002 "tags:a",
1003 log_event!["tags" => vec!["a", "b", "c"]],
1004 log_event!["tags" => vec!["d", "e", "f"]],
1005 ),
1006 (
1008 "NOT tags:a",
1009 log_event!["tags" => vec!["d", "e", "f"]],
1010 log_event!["tags" => vec!["a", "b", "c"]],
1011 ),
1012 (
1014 "-tags:a",
1015 log_event!["tags" => vec!["d", "e", "f"]],
1016 log_event!["tags" => vec!["a", "b", "c"]],
1017 ),
1018 (
1020 "[1 TO 10]",
1021 log_event!["message" => "1"],
1022 log_event!["message" => "2"],
1023 ),
1024 (
1026 "NOT [1 TO 10]",
1027 log_event!["message" => "2"],
1028 log_event!["message" => "1"],
1029 ),
1030 (
1032 "-[1 TO 10]",
1033 log_event!["message" => "2"],
1034 log_event!["message" => "1"],
1035 ),
1036 (
1038 "[50 TO *]",
1039 log_event!["message" => "6"],
1040 log_event!["message" => "40"],
1041 ),
1042 (
1044 "NOT [50 TO *]",
1045 log_event!["message" => "40"],
1046 log_event!["message" => "6"],
1047 ),
1048 (
1050 "-[50 TO *]",
1051 log_event!["message" => "40"],
1052 log_event!["message" => "6"],
1053 ),
1054 (
1056 "[* TO 50]",
1057 log_event!["message" => "3"],
1058 log_event!["message" => "6"],
1059 ),
1060 (
1062 "NOT [* TO 50]",
1063 log_event!["message" => "6"],
1064 log_event!["message" => "3"],
1065 ),
1066 (
1068 "-[* TO 50]",
1069 log_event!["message" => "6"],
1070 log_event!["message" => "3"],
1071 ),
1072 ("[* TO *]", log_event!["message" => "foo"], log_event![]),
1074 ("NOT [* TO *]", log_event![], log_event!["message" => "foo"]),
1076 ("-[* TO *]", log_event![], log_event!["message" => "foo"]),
1078 (
1080 "a:[1 TO 10]",
1081 log_event!["tags" => vec!["a:1"]],
1082 log_event!["tags" => vec!["a:2"]],
1083 ),
1084 (
1086 "NOT a:[1 TO 10]",
1087 log_event!["tags" => vec!["a:2"]],
1088 log_event!["tags" => vec!["a:1"]],
1089 ),
1090 (
1092 "-a:[1 TO 10]",
1093 log_event!["tags" => vec!["a:2"]],
1094 log_event!["tags" => vec!["a:1"]],
1095 ),
1096 (
1098 "a:[50 TO *]",
1099 log_event!["tags" => vec!["a:6"]],
1100 log_event!["tags" => vec!["a:40"]],
1101 ),
1102 (
1104 "NOT a:[50 TO *]",
1105 log_event!["tags" => vec!["a:40"]],
1106 log_event!["tags" => vec!["a:6"]],
1107 ),
1108 (
1110 "-a:[50 TO *]",
1111 log_event!["tags" => vec!["a:40"]],
1112 log_event!["tags" => vec!["a:6"]],
1113 ),
1114 (
1116 "a:[* TO 50]",
1117 log_event!["tags" => vec!["a:400"]],
1118 log_event!["tags" => vec!["a:600"]],
1119 ),
1120 (
1122 "NOT a:[* TO 50]",
1123 log_event!["tags" => vec!["a:600"]],
1124 log_event!["tags" => vec!["a:400"]],
1125 ),
1126 (
1128 "-a:[* TO 50]",
1129 log_event!["tags" => vec!["a:600"]],
1130 log_event!["tags" => vec!["a:400"]],
1131 ),
1132 (
1134 "a:[* TO *]",
1135 log_event!["tags" => vec!["a:test"]],
1136 log_event!["tags" => vec!["b:test"]],
1137 ),
1138 (
1140 "NOT a:[* TO *]",
1141 log_event!["tags" => vec!["b:test"]],
1142 log_event!["tags" => vec!["a:test"]],
1143 ),
1144 (
1146 "-a:[* TO *]",
1147 log_event!["tags" => vec!["b:test"]],
1148 log_event!["tags" => vec!["a:test"]],
1149 ),
1150 ("@b:[1 TO 10]", log_event!["b" => 5], log_event!["b" => 11]),
1152 (
1153 "@b:[1 TO 100]",
1154 log_event!["b" => "10"],
1155 log_event!["b" => "2"],
1156 ),
1157 (
1159 "NOT @b:[1 TO 10]",
1160 log_event!["b" => 11],
1161 log_event!["b" => 5],
1162 ),
1163 (
1164 "NOT @b:[1 TO 100]",
1165 log_event!["b" => "2"],
1166 log_event!["b" => "10"],
1167 ),
1168 ("-@b:[1 TO 10]", log_event!["b" => 11], log_event!["b" => 5]),
1170 (
1171 "NOT @b:[1 TO 100]",
1172 log_event!["b" => "2"],
1173 log_event!["b" => "10"],
1174 ),
1175 ("@b:[a TO z]", log_event!["b" => "c"], log_event!["b" => 5]),
1177 (
1179 r#"@b:["1" TO "100"]"#,
1180 log_event!["b" => "10"],
1181 log_event!["b" => "2"],
1182 ),
1183 (
1185 r#"NOT @b:["1" TO "100"]"#,
1186 log_event!["b" => "2"],
1187 log_event!["b" => "10"],
1188 ),
1189 (
1191 r#"-@b:["1" TO "100"]"#,
1192 log_event!["b" => "2"],
1193 log_event!["b" => "10"],
1194 ),
1195 (
1197 "f:{1 TO 100}",
1198 log_event!["tags" => vec!["f:10"]],
1199 log_event!["tags" => vec!["f:1"]],
1200 ),
1201 (
1202 "f:{1 TO 100}",
1203 log_event!["tags" => vec!["f:10"]],
1204 log_event!["tags" => vec!["f:100"]],
1205 ),
1206 (
1208 "NOT f:{1 TO 100}",
1209 log_event!["tags" => vec!["f:1"]],
1210 log_event!["tags" => vec!["f:10"]],
1211 ),
1212 (
1213 "NOT f:{1 TO 100}",
1214 log_event!["tags" => vec!["f:100"]],
1215 log_event!["tags" => vec!["f:10"]],
1216 ),
1217 (
1219 "-f:{1 TO 100}",
1220 log_event!["tags" => vec!["f:1"]],
1221 log_event!["tags" => vec!["f:10"]],
1222 ),
1223 (
1224 "-f:{1 TO 100}",
1225 log_event!["tags" => vec!["f:100"]],
1226 log_event!["tags" => vec!["f:10"]],
1227 ),
1228 ("@f:{1 TO 100}", log_event!["f" => 50], log_event!["f" => 1]),
1230 (
1231 "@f:{1 TO 100}",
1232 log_event!["f" => 50],
1233 log_event!["f" => 100],
1234 ),
1235 (
1237 "NOT @f:{1 TO 100}",
1238 log_event!["f" => 1],
1239 log_event!["f" => 50],
1240 ),
1241 (
1242 "NOT @f:{1 TO 100}",
1243 log_event!["f" => 100],
1244 log_event!["f" => 50],
1245 ),
1246 (
1248 "-@f:{1 TO 100}",
1249 log_event!["f" => 1],
1250 log_event!["f" => 50],
1251 ),
1252 (
1253 "-@f:{1 TO 100}",
1254 log_event!["f" => 100],
1255 log_event!["f" => 50],
1256 ),
1257 (
1259 "@field:(value1 OR value2)",
1260 log_event!["field" => "value1"],
1261 log_event!["field" => "value"],
1262 ),
1263 (
1265 "@field:value1 OR @field:value2",
1266 log_event!["field" => "value1"],
1267 log_event!["field" => "value"],
1268 ),
1269 (
1271 "-@field1:value1 OR -@field2:value2",
1272 log_event!["field1" => "value1"],
1273 log_event!["field1" => "value1", "field2" => "value2"],
1274 ),
1275 (
1277 "@field:value @field2:value2",
1278 log_event!["field" => "value", "field2" => "value2"],
1279 log_event!["field" => "value", "field2" => "value3"],
1280 ),
1281 (
1283 "@field:(value1 OR \n value2)",
1284 log_event!["field" => "value1"],
1285 log_event!["field" => "value"],
1286 ),
1287 (
1289 "NOT (@field:true AND @field2:value2)",
1290 log_event!["field" => false, "field2" => "value2"],
1291 log_event!["field" => true, "field2" => "value2"],
1292 ),
1293 (
1297 "_exists_:a", log_event!["ddtags" => vec!["a:foo"]], log_event!["ddtags" => vec!["b:foo"]], ),
1301 (
1303 "_exists_:a-b", log_event!["ddtags" => vec!["a-b:foo"]], log_event!["ddtags" => vec!["ab:foo"]], ),
1307 (
1309 "NOT _exists_:a",
1310 log_event!["ddtags" => vec!["b:foo"]],
1311 log_event!("ddtags" => vec!["a:foo"]),
1312 ),
1313 (
1315 "-_exists_:a",
1316 log_event!["ddtags" => vec!["b:foo"]],
1317 log_event!["ddtags" => vec!["a:foo"]],
1318 ),
1319 (
1321 "_missing_:a",
1322 log_event![],
1323 log_event!["ddtags" => vec!["a:foo"]],
1324 ),
1325 (
1327 "NOT _missing_:a",
1328 log_event!["ddtags" => vec!["a:foo"]],
1329 log_event![],
1330 ),
1331 (
1333 "-_missing_:a",
1334 log_event!["ddtags" => vec!["a:foo"]],
1335 log_event![],
1336 ),
1337 (
1339 "a:bla",
1340 log_event!["ddtags" => vec!["a:bla"]],
1341 log_event!["ddtags" => vec!["b:bla"]],
1342 ),
1343 (
1345 "NOT a:bla",
1346 log_event!["ddtags" => vec!["b:bla"]],
1347 log_event!["ddtags" => vec!["a:bla"]],
1348 ),
1349 (
1351 "NOT host:foo",
1352 log_event!["ddtags" => vec!["host:fo o"]],
1353 log_event!["host" => "foo"],
1354 ),
1355 (
1357 "-a:bla",
1358 log_event!["ddtags" => vec!["b:bla"]],
1359 log_event!["ddtags" => vec!["a:bla"]],
1360 ),
1361 (
1363 r#"a:"bla""#,
1364 log_event!["ddtags" => vec!["a:bla"]],
1365 log_event!["a" => "bla"],
1366 ),
1367 (
1369 r#"NOT a:"bla""#,
1370 log_event!["a" => "bla"],
1371 log_event!["ddtags" => vec!["a:bla"]],
1372 ),
1373 (
1375 r#"-a:"bla""#,
1376 log_event!["a" => "bla"],
1377 log_event!["ddtags" => vec!["a:bla"]],
1378 ),
1379 (
1381 "@a:bla",
1382 log_event!["a" => "bla"],
1383 log_event!["ddtags" => vec!["a:bla"]],
1384 ),
1385 (
1387 "NOT @a:bla",
1388 log_event!["ddtags" => vec!["a:bla"]],
1389 log_event!["a" => "bla"],
1390 ),
1391 (
1393 "-@a:bla",
1394 log_event!["ddtags" => vec!["a:bla"]],
1395 log_event!["a" => "bla"],
1396 ),
1397 (
1399 r#"@a:"bla""#,
1400 log_event!["a" => "bla"],
1401 log_event!["ddtags" => vec!["a:bla"]],
1402 ),
1403 (
1405 r#"NOT @a:"bla""#,
1406 log_event!["ddtags" => vec!["a:bla"]],
1407 log_event!["a" => "bla"],
1408 ),
1409 (
1411 r#"-@a:"bla""#,
1412 log_event!["ddtags" => vec!["a:bla"]],
1413 log_event!["a" => "bla"],
1414 ),
1415 (
1417 "@a:200",
1418 log_event!["a" => 200],
1419 log_event!["ddtags" => vec!["a:200"]],
1420 ),
1421 (
1423 "@a:0.75",
1424 log_event!["a" => 0.75],
1425 log_event!["ddtags" => vec!["a:0.75"]],
1426 ),
1427 (
1428 "a:*bla",
1429 log_event!["ddtags" => vec!["a:foobla"]],
1430 log_event!["ddtags" => vec!["a:blafoo"]],
1431 ),
1432 (
1434 "NOT a:*bla",
1435 log_event!["ddtags" => vec!["a:blafoo"]],
1436 log_event!["ddtags" => vec!["a:foobla"]],
1437 ),
1438 (
1440 "-a:*bla",
1441 log_event!["ddtags" => vec!["a:blafoo"]],
1442 log_event!["ddtags" => vec!["a:foobla"]],
1443 ),
1444 (
1446 "b:bla*",
1447 log_event!["ddtags" => vec!["b:blabop"]],
1448 log_event!["ddtags" => vec!["b:bopbla"]],
1449 ),
1450 (
1452 "NOT b:bla*",
1453 log_event!["ddtags" => vec!["b:bopbla"]],
1454 log_event!["ddtags" => vec!["b:blabop"]],
1455 ),
1456 (
1458 "-b:bla*",
1459 log_event!["ddtags" => vec!["b:bopbla"]],
1460 log_event!["ddtags" => vec!["b:blabop"]],
1461 ),
1462 (
1464 "c:*b*la*",
1465 log_event!["ddtags" => vec!["c:foobla"]],
1466 log_event!["custom" => r#"{"title" => "foobla"}"#],
1467 ),
1468 (
1470 "NOT c:*b*la*",
1471 log_event!["custom" => r#"{"title" => "foobla"}"#],
1472 log_event!["ddtags" => vec!["c:foobla"]],
1473 ),
1474 (
1476 "-c:*b*la*",
1477 log_event!["custom" => r#"{"title" => "foobla"}"#],
1478 log_event!["ddtags" => vec!["c:foobla"]],
1479 ),
1480 (
1482 "@a:*bla",
1483 log_event!["a" => "foobla"],
1484 log_event!["ddtags" => vec!["a:foobla"]],
1485 ),
1486 (
1488 "NOT @a:*bla",
1489 log_event!["ddtags" => vec!["a:foobla"]],
1490 log_event!["a" => "foobla"],
1491 ),
1492 (
1494 "-@a:*bla",
1495 log_event!["ddtags" => vec!["a:foobla"]],
1496 log_event!["a" => "foobla"],
1497 ),
1498 (
1500 "@b:bla*",
1501 log_event!["b" => "blabop"],
1502 log_event!["ddtags" => vec!["b:blabop"]],
1503 ),
1504 (
1506 "NOT @b:bla*",
1507 log_event!["ddtags" => vec!["b:blabop"]],
1508 log_event!["b" => "blabop"],
1509 ),
1510 (
1512 "-@b:bla*",
1513 log_event!["ddtags" => vec!["b:blabop"]],
1514 log_event!["b" => "blabop"],
1515 ),
1516 (
1518 "@c:*b*la*",
1519 log_event!["c" => "foobla"],
1520 log_event!["ddtags" => vec!["c:foobla"]],
1521 ),
1522 (
1524 "NOT @c:*b*la*",
1525 log_event!["ddtags" => vec!["c:foobla"]],
1526 log_event!["c" => "foobla"],
1527 ),
1528 (
1530 "-@c:*b*la*",
1531 log_event!["ddtags" => vec!["c:foobla"]],
1532 log_event!["c" => "foobla"],
1533 ),
1534 (
1536 "tags:a",
1537 log_event!["ddtags" => vec!["a", "b", "c"]],
1538 log_event!["ddtags" => vec!["d", "e", "f"]],
1539 ),
1540 (
1542 "NOT tags:a",
1543 log_event!["ddtags" => vec!["d", "e", "f"]],
1544 log_event!["ddtags" => vec!["a", "b", "c"]],
1545 ),
1546 (
1548 "-tags:a",
1549 log_event!["ddtags" => vec!["d", "e", "f"]],
1550 log_event!["ddtags" => vec!["a", "b", "c"]],
1551 ),
1552 (
1555 "source:foo",
1556 log_event!["source" => "foo"],
1557 log_event!["tags" => vec!["source:foo"]],
1558 ),
1559 (
1560 "source:foo",
1561 log_event!["source" => "foo"],
1562 log_event!["source" => "foobar"],
1563 ),
1564 (
1565 "source:foo",
1566 log_event!["source" => "foo"],
1567 log_event!["source" => r#"{"value": "foo"}"#],
1568 ),
1569 (
1571 "source:foo",
1572 log_event!["ddsource" => "foo"],
1573 log_event!["tags" => vec!["ddsource:foo"]],
1574 ),
1575 (
1576 "source:foo",
1577 log_event!["ddsource" => "foo"],
1578 log_event!["ddsource" => "foobar"],
1579 ),
1580 (
1581 "source:foo",
1582 log_event!["ddsource" => "foo"],
1583 log_event!["ddsource" => r#"{"value": "foo"}"#],
1584 ),
1585 (
1587 "source:foo",
1588 log_event!["source" => "foo", "ddsource" => "foo"],
1589 log_event!["source" => "foobar", "ddsource" => "foobar"],
1590 ),
1591 (
1592 "source:foo",
1593 log_event!["source" => "foo", "ddsource" => "foobar"],
1594 log_event!["source" => "foobar", "ddsource" => "foobar"],
1595 ),
1596 (
1597 "source:foo",
1598 log_event!["source" => "foobar", "ddsource" => "foo"],
1599 log_event!["source" => "foobar", "ddsource" => "foobar"],
1600 ),
1601 ]
1602 }
1603
1604 fn test_filter<V, F, P>(filter: F, processor: P)
1608 where
1609 V: std::fmt::Debug + Send + Sync + Clone + 'static,
1610 F: Filter<V> + Resolver,
1611 P: Fn(Event) -> V,
1612 {
1613 let checks = get_checks();
1614
1615 for (source, pass, fail) in checks {
1616 let node: QueryNode = source.parse().unwrap();
1617 let matcher = build_matcher(&node, &filter).unwrap();
1618
1619 assert!(matcher.run(&processor(pass)));
1620 assert!(!matcher.run(&processor(fail)));
1621 }
1622 }
1623
1624 #[test]
1625 fn event_filter() {
1627 test_filter(EventFilter, |ev| ev.into_log())
1628 }
1629
1630 #[test]
1631 fn generate_config() {
1632 crate::test_util::test_generate_config::<DatadogSearchConfig>();
1633 }
1634
1635 #[test]
1636 fn check_datadog() {
1637 for (source, pass, fail) in get_checks() {
1638 let config: DatadogSearchConfig = source.parse().unwrap();
1639
1640 let cond = config
1642 .build(&Default::default())
1643 .unwrap_or_else(|_| panic!("build failed: {source}"));
1644
1645 assert!(
1646 cond.check_with_context(pass.clone()).0.is_ok(),
1647 "should pass: {}\nevent: {}",
1648 source,
1649 serde_json::to_string(&pass.as_log()).unwrap(),
1650 );
1651
1652 assert!(
1653 cond.check_with_context(fail.clone()).0.is_err(),
1654 "should fail: {}\nevent: {}",
1655 source,
1656 serde_json::to_string(&fail.as_log()).unwrap(),
1657 );
1658 }
1659 }
1660}