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 vector_vrl_metrics::MetricsStorage;
9use vrl::{
10 datadog_filter::{
11 Filter, Matcher, Resolver, Run, build_matcher,
12 regex::{wildcard_regex, word_regex},
13 },
14 datadog_search_syntax::{Comparison, ComparisonValue, Field, QueryNode},
15 path::PathParseError,
16};
17
18use super::{Condition, Conditional, ConditionalConfig};
19
20#[configurable_component]
22#[derive(Clone, Debug, PartialEq)]
23pub struct DatadogSearchConfig {
24 source: QueryNode,
26}
27
28impl Default for DatadogSearchConfig {
29 fn default() -> Self {
30 Self {
31 source: QueryNode::MatchAllDocs,
32 }
33 }
34}
35
36impl DatadogSearchConfig {
37 pub fn build_matcher(&self) -> crate::Result<Box<dyn Matcher<LogEvent>>> {
38 Ok(build_matcher(&self.source, &EventFilter)?)
39 }
40}
41
42impl FromStr for DatadogSearchConfig {
43 type Err = <QueryNode as FromStr>::Err;
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 s.parse().map(|source| Self { source })
46 }
47}
48
49impl From<QueryNode> for DatadogSearchConfig {
50 fn from(source: QueryNode) -> Self {
51 Self { source }
52 }
53}
54
55impl_generate_config_from_default!(DatadogSearchConfig);
56
57#[derive(Debug, Clone)]
60pub struct DatadogSearchRunner {
61 matcher: Box<dyn Matcher<LogEvent>>,
62}
63
64impl TryFrom<&DatadogSearchConfig> for DatadogSearchRunner {
65 type Error = crate::Error;
66 fn try_from(config: &DatadogSearchConfig) -> Result<Self, Self::Error> {
67 config.build_matcher().map(|matcher| Self { matcher })
68 }
69}
70
71impl DatadogSearchRunner {
72 pub fn matches<'a>(&self, event: impl Into<EventRef<'a>>) -> bool {
73 match event.into() {
74 EventRef::Log(log) => self.matcher.run(log),
75 _ => false,
76 }
77 }
78}
79
80impl Conditional for DatadogSearchRunner {
81 fn check(&self, event: Event) -> (bool, Event) {
82 (self.matches(&event), event)
83 }
84}
85
86impl ConditionalConfig for DatadogSearchConfig {
87 fn build(
88 &self,
89 _enrichment_tables: &vector_lib::enrichment::TableRegistry,
90 _: &MetricsStorage,
91 ) -> crate::Result<Condition> {
92 Ok(Condition::DatadogSearch(self.try_into()?))
93 }
94}
95
96#[derive(Default, Clone)]
97struct EventFilter;
98
99impl Resolver for EventFilter {}
101
102impl Filter<LogEvent> for EventFilter {
103 fn exists(&self, field: Field) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
104 Ok(match field {
105 Field::Tag(tag) => {
106 let starts_with = format!("{tag}:");
107
108 any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
109 value == tag || value.starts_with(&starts_with)
110 })
111 }
112 Field::Reserved(field) if field == "tags" => {
114 any_string_match_multiple(vec!["ddtags", "tags"], move |value| value == field)
115 }
116 Field::Reserved(field) if field == "source" => {
118 exists_match_multiple(vec!["ddsource", "source"])
119 }
120
121 Field::Default(f) | Field::Attribute(f) | Field::Reserved(f) => exists_match(f),
122 })
123 }
124
125 fn equals(
126 &self,
127 field: Field,
128 to_match: &str,
129 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
130 Ok(match field {
131 Field::Default(field) => {
133 let re = word_regex(to_match);
134
135 string_match(&field, move |value| re.is_match(&value))
136 }
137 Field::Reserved(field) if field == "tags" => {
139 let to_match = to_match.to_owned();
140
141 array_match_multiple(vec!["ddtags", "tags"], move |values| {
142 values.contains(&Value::Bytes(Bytes::copy_from_slice(to_match.as_bytes())))
143 })
144 }
145 Field::Tag(tag) => {
147 let value_bytes = Value::Bytes(format!("{tag}:{to_match}").into());
148
149 array_match_multiple(vec!["ddtags", "tags"], move |values| {
150 values.contains(&value_bytes)
151 })
152 }
153 Field::Reserved(field) if field == "source" => {
155 let to_match = to_match.to_owned();
156
157 string_match_multiple(vec!["ddsource", "source"], move |value| value == to_match)
158 }
159 Field::Reserved(field) => {
161 let to_match = to_match.to_owned();
162
163 string_match(field, move |value| value == to_match)
164 }
165 Field::Attribute(field) => {
167 let to_match = to_match.to_owned();
168
169 simple_scalar_match(field, move |value| value == to_match)
170 }
171 })
172 }
173
174 fn prefix(
175 &self,
176 field: Field,
177 prefix: &str,
178 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
179 Ok(match field {
180 Field::Default(field) => {
182 let re = word_regex(&format!("{prefix}*"));
183
184 string_match(field, move |value| re.is_match(&value))
185 }
186 Field::Tag(tag) => {
188 let starts_with = format!("{tag}:{prefix}");
189
190 any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
191 value.starts_with(&starts_with)
192 })
193 }
194 Field::Reserved(field) if field == "source" => {
196 let prefix = prefix.to_owned();
197
198 string_match_multiple(vec!["ddsource", "source"], move |value| {
199 value.starts_with(&prefix)
200 })
201 }
202
203 Field::Reserved(field) | Field::Attribute(field) => {
205 let prefix = prefix.to_owned();
206
207 string_match(field, move |value| value.starts_with(&prefix))
208 }
209 })
210 }
211
212 fn wildcard(
213 &self,
214 field: Field,
215 wildcard: &str,
216 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
217 Ok(match field {
218 Field::Default(field) => {
219 let re = word_regex(wildcard);
220
221 string_match(field, move |value| re.is_match(&value))
222 }
223 Field::Tag(tag) => {
224 let re = wildcard_regex(&format!("{tag}:{wildcard}"));
225
226 any_string_match_multiple(vec!["ddtags", "tags"], move |value| re.is_match(&value))
227 }
228 Field::Reserved(field) if field == "source" => {
230 let re = wildcard_regex(wildcard);
231
232 string_match_multiple(vec!["ddsource", "source"], move |value| re.is_match(&value))
233 }
234 Field::Reserved(field) | Field::Attribute(field) => {
235 let re = wildcard_regex(wildcard);
236
237 string_match(field, move |value| re.is_match(&value))
238 }
239 })
240 }
241
242 fn compare(
243 &self,
244 field: Field,
245 comparator: Comparison,
246 comparison_value: ComparisonValue,
247 ) -> Result<Box<dyn Matcher<LogEvent>>, PathParseError> {
248 let rhs = Cow::from(comparison_value.to_string());
249
250 Ok(match field {
251 Field::Attribute(f) => {
253 Run::boxed(move |log: &LogEvent| {
254 match (
255 log.parse_path_and_get_value(f.as_str()).ok().flatten(),
256 &comparison_value,
257 ) {
258 (Some(Value::Integer(lhs)), ComparisonValue::Integer(rhs)) => {
260 match comparator {
261 Comparison::Lt => lhs < rhs,
262 Comparison::Lte => lhs <= rhs,
263 Comparison::Gt => lhs > rhs,
264 Comparison::Gte => lhs >= rhs,
265 }
266 }
267 (Some(Value::Integer(lhs)), ComparisonValue::Float(rhs)) => {
269 match comparator {
270 Comparison::Lt => (*lhs as f64) < *rhs,
271 Comparison::Lte => *lhs as f64 <= *rhs,
272 Comparison::Gt => *lhs as f64 > *rhs,
273 Comparison::Gte => *lhs as f64 >= *rhs,
274 }
275 }
276 (Some(Value::Float(lhs)), ComparisonValue::Float(rhs)) => {
278 match comparator {
279 Comparison::Lt => lhs.into_inner() < *rhs,
280 Comparison::Lte => lhs.into_inner() <= *rhs,
281 Comparison::Gt => lhs.into_inner() > *rhs,
282 Comparison::Gte => lhs.into_inner() >= *rhs,
283 }
284 }
285 (Some(Value::Float(lhs)), ComparisonValue::Integer(rhs)) => {
287 match comparator {
288 Comparison::Lt => lhs.into_inner() < *rhs as f64,
289 Comparison::Lte => lhs.into_inner() <= *rhs as f64,
290 Comparison::Gt => lhs.into_inner() > *rhs as f64,
291 Comparison::Gte => lhs.into_inner() >= *rhs as f64,
292 }
293 }
294 (Some(Value::Bytes(v)), ComparisonValue::String(rhs)) => {
296 let lhs = String::from_utf8_lossy(v);
297 let rhs = Cow::from(rhs);
298
299 match comparator {
300 Comparison::Lt => lhs < rhs,
301 Comparison::Lte => lhs <= rhs,
302 Comparison::Gt => lhs > rhs,
303 Comparison::Gte => lhs >= rhs,
304 }
305 }
306 (Some(Value::Bytes(v)), _) => {
308 let lhs = String::from_utf8_lossy(v);
309
310 match comparator {
311 Comparison::Lt => lhs < rhs,
312 Comparison::Lte => lhs <= rhs,
313 Comparison::Gt => lhs > rhs,
314 Comparison::Gte => lhs >= rhs,
315 }
316 }
317 _ => false,
318 }
319 })
320 }
321 Field::Tag(tag) => any_string_match_multiple(vec!["ddtags", "tags"], move |value| {
323 match value.split_once(':') {
324 Some((t, lhs)) if t == tag => {
325 let lhs = Cow::from(lhs);
326
327 match comparator {
328 Comparison::Lt => lhs < rhs,
329 Comparison::Lte => lhs <= rhs,
330 Comparison::Gt => lhs > rhs,
331 Comparison::Gte => lhs >= rhs,
332 }
333 }
334 _ => false,
335 }
336 }),
337 Field::Reserved(field) if field == "source" => {
339 string_match_multiple(vec!["ddsource", "source"], move |lhs| match comparator {
340 Comparison::Lt => lhs < rhs,
341 Comparison::Lte => lhs <= rhs,
342 Comparison::Gt => lhs > rhs,
343 Comparison::Gte => lhs >= rhs,
344 })
345 }
346 Field::Default(field) | Field::Reserved(field) => {
348 string_match(field, move |lhs| match comparator {
349 Comparison::Lt => lhs < rhs,
350 Comparison::Lte => lhs <= rhs,
351 Comparison::Gt => lhs > rhs,
352 Comparison::Gte => lhs >= rhs,
353 })
354 }
355 })
356 }
357}
358
359fn exists_match<S>(field: S) -> Box<dyn Matcher<LogEvent>>
361where
362 S: Into<String>,
363{
364 let field = field.into();
365
366 Run::boxed(move |log: &LogEvent| {
367 log.parse_path_and_get_value(field.as_str())
368 .ok()
369 .flatten()
370 .is_some()
371 })
372}
373
374fn simple_scalar_match<S, F>(field: S, func: F) -> Box<dyn Matcher<LogEvent>>
377where
378 S: Into<String>,
379 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
380{
381 let field = field.into();
382
383 Run::boxed(move |log: &LogEvent| {
384 match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
385 Some(Value::Boolean(v)) => func(v.to_string().into()),
386 Some(Value::Bytes(v)) => func(String::from_utf8_lossy(v)),
387 Some(Value::Integer(v)) => func(v.to_string().into()),
388 Some(Value::Float(v)) => func(v.to_string().into()),
389 _ => false,
390 }
391 })
392}
393
394fn string_match<S, F>(field: S, func: F) -> Box<dyn Matcher<LogEvent>>
397where
398 S: Into<String>,
399 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
400{
401 let field = field.into();
402
403 Run::boxed(move |log: &LogEvent| {
404 match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
405 Some(Value::Bytes(v)) => func(String::from_utf8_lossy(v)),
406 _ => false,
407 }
408 })
409}
410
411fn exists_match_multiple<S>(fields: Vec<S>) -> Box<dyn Matcher<LogEvent>>
413where
414 S: Into<String> + Clone + Send + Sync + 'static,
415{
416 Run::boxed(move |log: &LogEvent| {
417 fields
418 .iter()
419 .any(|field| exists_match(field.clone()).run(log))
420 })
421}
422
423fn string_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
426where
427 S: Into<String> + Clone + Send + Sync + 'static,
428 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
429{
430 Run::boxed(move |log: &LogEvent| {
431 fields
432 .iter()
433 .any(|field| string_match(field.clone(), func.clone()).run(log))
434 })
435}
436
437fn any_string_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
438where
439 S: Into<String> + Clone + Send + Sync + 'static,
440 F: Fn(Cow<str>) -> bool + Send + Sync + Clone + 'static,
441{
442 any_match_multiple(fields, move |value| {
443 let bytes = value.coerce_to_bytes();
444 func(String::from_utf8_lossy(&bytes))
445 })
446}
447
448fn any_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
451where
452 S: Into<String> + Clone + Send + Sync + 'static,
453 F: Fn(&Value) -> bool + Send + Sync + Clone + 'static,
454{
455 array_match_multiple(fields, move |values| values.iter().any(&func))
456}
457
458fn array_match_multiple<S, F>(fields: Vec<S>, func: F) -> Box<dyn Matcher<LogEvent>>
461where
462 S: Into<String> + Clone + Send + Sync + 'static,
463 F: Fn(&Vec<Value>) -> bool + Send + Sync + Clone + 'static,
464{
465 Run::boxed(move |log: &LogEvent| {
466 fields.iter().any(|field| {
467 let field = field.clone().into();
468 match log.parse_path_and_get_value(field.as_str()).ok().flatten() {
469 Some(Value::Array(values)) => func(values),
470 _ => false,
471 }
472 })
473 })
474}
475
476#[cfg(test)]
477mod test {
478 use super::*;
479 use crate::log_event;
480
481 fn get_checks() -> Vec<(&'static str, Event, Event)> {
486 vec![
487 (
489 "_exists_:a", log_event!["tags" => vec!["a:foo"]], log_event!["tags" => vec!["b:foo"]], ),
493 (
495 "_exists_:a-b", log_event!["tags" => vec!["a-b:foo"]], log_event!["tags" => vec!["ab:foo"]], ),
499 (
501 "NOT _exists_:a",
502 log_event!["tags" => vec!["b:foo"]],
503 log_event!("tags" => vec!["a:foo"]),
504 ),
505 (
507 "-_exists_:a",
508 log_event!["tags" => vec!["b:foo"]],
509 log_event!["tags" => vec!["a:foo"]],
510 ),
511 (
513 "_exists_:@b",
514 log_event!["b" => "foo"],
515 log_event!["a" => "foo"],
516 ),
517 (
528 "NOT _exists_:@b",
529 log_event!["a" => "foo"],
530 log_event!["b" => "foo"],
531 ),
532 (
534 "-_exists_:@b",
535 log_event!["a" => "foo"],
536 log_event!["b" => "foo"],
537 ),
538 (
540 "_missing_:a",
541 log_event![],
542 log_event!["tags" => vec!["a:foo"]],
543 ),
544 (
546 "NOT _missing_:a",
547 log_event!["tags" => vec!["a:foo"]],
548 log_event![],
549 ),
550 (
552 "-_missing_:a",
553 log_event!["tags" => vec!["a:foo"]],
554 log_event![],
555 ),
556 (
558 "_missing_:@b",
559 log_event!["a" => "foo"],
560 log_event!["b" => "foo"],
561 ),
562 (
564 "NOT _missing_:@b",
565 log_event!["b" => "foo"],
566 log_event!["a" => "foo"],
567 ),
568 (
570 "-_missing_:@b",
571 log_event!["b" => "foo"],
572 log_event!["a" => "foo"],
573 ),
574 ("bla", log_event!["message" => "bla"], log_event![]),
576 (
577 "foo",
578 log_event!["message" => r#"{"key": "foo"}"#],
579 log_event![],
580 ),
581 (
582 "bar",
583 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
584 log_event![],
585 ),
586 (
588 "NOT bla",
589 log_event!["message" => "nothing"],
590 log_event!["message" => "bla"],
591 ),
592 (
593 "NOT foo",
594 log_event![],
595 log_event!["message" => r#"{"key": "foo"}"#],
596 ),
597 (
598 "NOT bar",
599 log_event![],
600 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
601 ),
602 (
604 "-bla",
605 log_event!["message" => "nothing"],
606 log_event!["message" => "bla"],
607 ),
608 (
609 "-foo",
610 log_event![],
611 log_event!["message" => r#"{"key": "foo"}"#],
612 ),
613 (
614 "-bar",
615 log_event![],
616 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
617 ),
618 (r#""bla""#, log_event!["message" => "bla"], log_event![]),
620 (
621 r#""foo""#,
622 log_event!["message" => r#"{"key": "foo"}"#],
623 log_event![],
624 ),
625 (
626 r#""bar""#,
627 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
628 log_event![],
629 ),
630 (r#"NOT "bla""#, log_event![], log_event!["message" => "bla"]),
632 (
633 r#"NOT "foo""#,
634 log_event![],
635 log_event!["message" => r#"{"key": "foo"}"#],
636 ),
637 (
638 r#"NOT "bar""#,
639 log_event![],
640 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
641 ),
642 (r#"-"bla""#, log_event![], log_event!["message" => "bla"]),
644 (
645 r#"NOT "foo""#,
646 log_event![],
647 log_event!["message" => r#"{"key": "foo"}"#],
648 ),
649 (
650 r#"NOT "bar""#,
651 log_event![],
652 log_event!["message" => r#"{"nested": {"value": ["foo", "bar"]}}"#],
653 ),
654 (
656 "a:bla",
657 log_event!["tags" => vec!["a:bla"]],
658 log_event!["tags" => vec!["b:bla"]],
659 ),
660 (
662 "host:foo",
663 log_event!["host" => "foo"],
664 log_event!["tags" => vec!["host:foo"]],
665 ),
666 (
667 "host:foo",
668 log_event!["host" => "foo"],
669 log_event!["host" => "foobar"],
670 ),
671 (
672 "host:foo",
673 log_event!["host" => "foo"],
674 log_event!["host" => r#"{"value": "foo"}"#],
675 ),
676 (
678 "NOT a:bla",
679 log_event!["tags" => vec!["b:bla"]],
680 log_event!["tags" => vec!["a:bla"]],
681 ),
682 (
684 "NOT host:foo",
685 log_event!["tags" => vec!["host:fo o"]],
686 log_event!["host" => "foo"],
687 ),
688 (
690 "-a:bla",
691 log_event!["tags" => vec!["b:bla"]],
692 log_event!["tags" => vec!["a:bla"]],
693 ),
694 (
696 "-trace_id:foo",
697 log_event![],
698 log_event!["trace_id" => "foo"],
699 ),
700 (
702 r#"a:"bla""#,
703 log_event!["tags" => vec!["a:bla"]],
704 log_event!["a" => "bla"],
705 ),
706 (
708 r#"NOT a:"bla""#,
709 log_event!["a" => "bla"],
710 log_event!["tags" => vec!["a:bla"]],
711 ),
712 (
714 r#"-a:"bla""#,
715 log_event!["a" => "bla"],
716 log_event!["tags" => vec!["a:bla"]],
717 ),
718 ("@a:true", log_event!["a" => true], log_event!["a" => false]),
720 (
722 "NOT @a:false",
723 log_event!["a" => true],
724 log_event!["a" => false],
725 ),
726 (
728 "@a:bla",
729 log_event!["a" => "bla"],
730 log_event!["tags" => vec!["a:bla"]],
731 ),
732 (
734 "NOT @a:bla",
735 log_event!["tags" => vec!["a:bla"]],
736 log_event!["a" => "bla"],
737 ),
738 ("@a:b", log_event!["a" => "b"], log_event!["a" => "c"]),
740 (
742 "@a:va\\/lue",
743 log_event!["a" => "va/lue"],
744 log_event!["a" => "value"],
745 ),
746 (
748 "@a:va\\&&lue",
749 log_event!["a" => "va&&lue"],
750 log_event!["a" => "value"],
751 ),
752 (
754 "@a:va\\ lue",
755 log_event!["a" => "va lue"],
756 log_event!["a" => "value"],
757 ),
758 (
760 "@a:va\\||lue",
761 log_event!["a" => "va||lue"],
762 log_event!["a" => "value"],
763 ),
764 (
766 "@a:va\\(lue",
767 log_event!["a" => "va(lue"],
768 log_event!["a" => "value"],
769 ),
770 (
772 "@a:va\\*lue",
773 log_event!["a" => "va*lue"],
774 log_event!["a" => "value"],
775 ),
776 (
779 "@a:va\\~lue",
780 log_event!["a" => "va~lue"],
781 log_event!["a" => "value"],
782 ),
783 (
786 "@a:va\\^lue",
787 log_event!["a" => "va^lue"],
788 log_event!["a" => "value"],
789 ),
790 (
792 "@a:va/lue",
793 log_event!["a" => "va/lue"],
794 log_event!["a" => "value"],
795 ),
796 (
798 "-@a:bla",
799 log_event!["tags" => vec!["a:bla"]],
800 log_event!["a" => "bla"],
801 ),
802 (
804 r#"@a:"bla""#,
805 log_event!["a" => "bla"],
806 log_event!["tags" => vec!["a:bla"]],
807 ),
808 (
810 r#"NOT @a:"bla""#,
811 log_event!["tags" => vec!["a:bla"]],
812 log_event!["a" => "bla"],
813 ),
814 (
816 r#"-@a:"bla""#,
817 log_event!["tags" => vec!["a:bla"]],
818 log_event!["a" => "bla"],
819 ),
820 (
822 "@a:200",
823 log_event!["a" => 200],
824 log_event!["tags" => vec!["a:200"]],
825 ),
826 ("-@a:200", log_event!["a" => 199], log_event!["a" => 200]),
828 (
830 "@a:0.75",
831 log_event!["a" => 0.75],
832 log_event!["tags" => vec!["a:0.75"]],
833 ),
834 ("-@a:0.75", log_event!["a" => 0.74], log_event!["a" => 0.75]),
836 (
838 "@a.b:x",
839 log_event!["a" => serde_json::json!({"b": "x"})],
840 Event::Log(LogEvent::from(Value::from(serde_json::json!({"a.b": "x"})))),
841 ),
842 (
844 r#"@\"a.b\":x"#,
845 Event::Log(LogEvent::from(Value::from(serde_json::json!({"a.b": "x"})))),
846 log_event!["a" => serde_json::json!({"b": "x"})],
847 ),
848 (
850 "*bla",
851 log_event!["message" => "foobla"],
852 log_event!["message" => "blafoo"],
853 ),
854 (
856 "NOT *bla",
857 log_event!["message" => "blafoo"],
858 log_event!["message" => "foobla"],
859 ),
860 (
862 "-*bla",
863 log_event!["message" => "blafoo"],
864 log_event!["message" => "foobla"],
865 ),
866 (
868 "bla*",
869 log_event!["message" => "blafoo"],
870 log_event!["message" => "foobla"],
871 ),
872 (
874 "NOT bla*",
875 log_event!["message" => "foobla"],
876 log_event!["message" => "blafoo"],
877 ),
878 (
880 "-bla*",
881 log_event!["message" => "foobla"],
882 log_event!["message" => "blafoo"],
883 ),
884 ("*b*la*", log_event!["message" => "foobla"], log_event![]),
886 (
888 "NOT *b*la*",
889 log_event![],
890 log_event!["message" => "foobla"],
891 ),
892 ("-*b*la*", log_event![], log_event!["message" => "foobla"]),
894 (
896 "a:*bla",
897 log_event!["tags" => vec!["a:foobla"]],
898 log_event!["tags" => vec!["a:blafoo"]],
899 ),
900 (
902 "NOT a:*bla",
903 log_event!["tags" => vec!["a:blafoo"]],
904 log_event!["tags" => vec!["a:foobla"]],
905 ),
906 (
908 "-a:*bla",
909 log_event!["tags" => vec!["a:blafoo"]],
910 log_event!["tags" => vec!["a:foobla"]],
911 ),
912 (
914 "b:bla*",
915 log_event!["tags" => vec!["b:blabop"]],
916 log_event!["tags" => vec!["b:bopbla"]],
917 ),
918 (
920 "NOT b:bla*",
921 log_event!["tags" => vec!["b:bopbla"]],
922 log_event!["tags" => vec!["b:blabop"]],
923 ),
924 (
926 "-b:bla*",
927 log_event!["tags" => vec!["b:bopbla"]],
928 log_event!["tags" => vec!["b:blabop"]],
929 ),
930 (
932 "c:*b*la*",
933 log_event!["tags" => vec!["c:foobla"]],
934 log_event!["custom" => r#"{"title" => "foobla"}"#],
935 ),
936 (
938 "NOT c:*b*la*",
939 log_event!["custom" => r#"{"title" => "foobla"}"#],
940 log_event!["tags" => vec!["c:foobla"]],
941 ),
942 (
944 "-c:*b*la*",
945 log_event!["custom" => r#"{"title" => "foobla"}"#],
946 log_event!["tags" => vec!["c:foobla"]],
947 ),
948 (
950 "@a:*bla",
951 log_event!["a" => "foobla"],
952 log_event!["tags" => vec!["a:foobla"]],
953 ),
954 (
956 "NOT @a:*bla",
957 log_event!["tags" => vec!["a:foobla"]],
958 log_event!["a" => "foobla"],
959 ),
960 (
962 "-@a:*bla",
963 log_event!["tags" => vec!["a:foobla"]],
964 log_event!["a" => "foobla"],
965 ),
966 (
968 "@b:bla*",
969 log_event!["b" => "blabop"],
970 log_event!["tags" => vec!["b:blabop"]],
971 ),
972 (
974 "NOT @b:bla*",
975 log_event!["tags" => vec!["b:blabop"]],
976 log_event!["b" => "blabop"],
977 ),
978 (
980 "-@b:bla*",
981 log_event!["tags" => vec!["b:blabop"]],
982 log_event!["b" => "blabop"],
983 ),
984 (
986 "@c:*b*la*",
987 log_event!["c" => "foobla"],
988 log_event!["tags" => vec!["c:foobla"]],
989 ),
990 (
992 "NOT @c:*b*la*",
993 log_event!["tags" => vec!["c:foobla"]],
994 log_event!["c" => "foobla"],
995 ),
996 (
998 "-@c:*b*la*",
999 log_event!["tags" => vec!["c:foobla"]],
1000 log_event!["c" => "foobla"],
1001 ),
1002 (
1004 "tags:a",
1005 log_event!["tags" => vec!["a", "b", "c"]],
1006 log_event!["tags" => vec!["d", "e", "f"]],
1007 ),
1008 (
1010 "NOT tags:a",
1011 log_event!["tags" => vec!["d", "e", "f"]],
1012 log_event!["tags" => vec!["a", "b", "c"]],
1013 ),
1014 (
1016 "-tags:a",
1017 log_event!["tags" => vec!["d", "e", "f"]],
1018 log_event!["tags" => vec!["a", "b", "c"]],
1019 ),
1020 (
1022 "[1 TO 10]",
1023 log_event!["message" => "1"],
1024 log_event!["message" => "2"],
1025 ),
1026 (
1028 "NOT [1 TO 10]",
1029 log_event!["message" => "2"],
1030 log_event!["message" => "1"],
1031 ),
1032 (
1034 "-[1 TO 10]",
1035 log_event!["message" => "2"],
1036 log_event!["message" => "1"],
1037 ),
1038 (
1040 "[50 TO *]",
1041 log_event!["message" => "6"],
1042 log_event!["message" => "40"],
1043 ),
1044 (
1046 "NOT [50 TO *]",
1047 log_event!["message" => "40"],
1048 log_event!["message" => "6"],
1049 ),
1050 (
1052 "-[50 TO *]",
1053 log_event!["message" => "40"],
1054 log_event!["message" => "6"],
1055 ),
1056 (
1058 "[* TO 50]",
1059 log_event!["message" => "3"],
1060 log_event!["message" => "6"],
1061 ),
1062 (
1064 "NOT [* TO 50]",
1065 log_event!["message" => "6"],
1066 log_event!["message" => "3"],
1067 ),
1068 (
1070 "-[* TO 50]",
1071 log_event!["message" => "6"],
1072 log_event!["message" => "3"],
1073 ),
1074 ("[* TO *]", log_event!["message" => "foo"], log_event![]),
1076 ("NOT [* TO *]", log_event![], log_event!["message" => "foo"]),
1078 ("-[* TO *]", log_event![], log_event!["message" => "foo"]),
1080 (
1082 "a:[1 TO 10]",
1083 log_event!["tags" => vec!["a:1"]],
1084 log_event!["tags" => vec!["a:2"]],
1085 ),
1086 (
1088 "NOT a:[1 TO 10]",
1089 log_event!["tags" => vec!["a:2"]],
1090 log_event!["tags" => vec!["a:1"]],
1091 ),
1092 (
1094 "-a:[1 TO 10]",
1095 log_event!["tags" => vec!["a:2"]],
1096 log_event!["tags" => vec!["a:1"]],
1097 ),
1098 (
1100 "a:[50 TO *]",
1101 log_event!["tags" => vec!["a:6"]],
1102 log_event!["tags" => vec!["a:40"]],
1103 ),
1104 (
1106 "NOT a:[50 TO *]",
1107 log_event!["tags" => vec!["a:40"]],
1108 log_event!["tags" => vec!["a:6"]],
1109 ),
1110 (
1112 "-a:[50 TO *]",
1113 log_event!["tags" => vec!["a:40"]],
1114 log_event!["tags" => vec!["a:6"]],
1115 ),
1116 (
1118 "a:[* TO 50]",
1119 log_event!["tags" => vec!["a:400"]],
1120 log_event!["tags" => vec!["a:600"]],
1121 ),
1122 (
1124 "NOT a:[* TO 50]",
1125 log_event!["tags" => vec!["a:600"]],
1126 log_event!["tags" => vec!["a:400"]],
1127 ),
1128 (
1130 "-a:[* TO 50]",
1131 log_event!["tags" => vec!["a:600"]],
1132 log_event!["tags" => vec!["a:400"]],
1133 ),
1134 (
1136 "a:[* TO *]",
1137 log_event!["tags" => vec!["a:test"]],
1138 log_event!["tags" => vec!["b:test"]],
1139 ),
1140 (
1142 "NOT a:[* TO *]",
1143 log_event!["tags" => vec!["b:test"]],
1144 log_event!["tags" => vec!["a:test"]],
1145 ),
1146 (
1148 "-a:[* TO *]",
1149 log_event!["tags" => vec!["b:test"]],
1150 log_event!["tags" => vec!["a:test"]],
1151 ),
1152 ("@b:[1 TO 10]", log_event!["b" => 5], log_event!["b" => 11]),
1154 (
1155 "@b:[1 TO 100]",
1156 log_event!["b" => "10"],
1157 log_event!["b" => "2"],
1158 ),
1159 (
1161 "NOT @b:[1 TO 10]",
1162 log_event!["b" => 11],
1163 log_event!["b" => 5],
1164 ),
1165 (
1166 "NOT @b:[1 TO 100]",
1167 log_event!["b" => "2"],
1168 log_event!["b" => "10"],
1169 ),
1170 ("-@b:[1 TO 10]", log_event!["b" => 11], log_event!["b" => 5]),
1172 (
1173 "NOT @b:[1 TO 100]",
1174 log_event!["b" => "2"],
1175 log_event!["b" => "10"],
1176 ),
1177 ("@b:[a TO z]", log_event!["b" => "c"], log_event!["b" => 5]),
1179 (
1181 r#"@b:["1" TO "100"]"#,
1182 log_event!["b" => "10"],
1183 log_event!["b" => "2"],
1184 ),
1185 (
1187 r#"NOT @b:["1" TO "100"]"#,
1188 log_event!["b" => "2"],
1189 log_event!["b" => "10"],
1190 ),
1191 (
1193 r#"-@b:["1" TO "100"]"#,
1194 log_event!["b" => "2"],
1195 log_event!["b" => "10"],
1196 ),
1197 (
1199 "f:{1 TO 100}",
1200 log_event!["tags" => vec!["f:10"]],
1201 log_event!["tags" => vec!["f:1"]],
1202 ),
1203 (
1204 "f:{1 TO 100}",
1205 log_event!["tags" => vec!["f:10"]],
1206 log_event!["tags" => vec!["f:100"]],
1207 ),
1208 (
1210 "NOT f:{1 TO 100}",
1211 log_event!["tags" => vec!["f:1"]],
1212 log_event!["tags" => vec!["f:10"]],
1213 ),
1214 (
1215 "NOT f:{1 TO 100}",
1216 log_event!["tags" => vec!["f:100"]],
1217 log_event!["tags" => vec!["f:10"]],
1218 ),
1219 (
1221 "-f:{1 TO 100}",
1222 log_event!["tags" => vec!["f:1"]],
1223 log_event!["tags" => vec!["f:10"]],
1224 ),
1225 (
1226 "-f:{1 TO 100}",
1227 log_event!["tags" => vec!["f:100"]],
1228 log_event!["tags" => vec!["f:10"]],
1229 ),
1230 ("@f:{1 TO 100}", log_event!["f" => 50], log_event!["f" => 1]),
1232 (
1233 "@f:{1 TO 100}",
1234 log_event!["f" => 50],
1235 log_event!["f" => 100],
1236 ),
1237 (
1239 "NOT @f:{1 TO 100}",
1240 log_event!["f" => 1],
1241 log_event!["f" => 50],
1242 ),
1243 (
1244 "NOT @f:{1 TO 100}",
1245 log_event!["f" => 100],
1246 log_event!["f" => 50],
1247 ),
1248 (
1250 "-@f:{1 TO 100}",
1251 log_event!["f" => 1],
1252 log_event!["f" => 50],
1253 ),
1254 (
1255 "-@f:{1 TO 100}",
1256 log_event!["f" => 100],
1257 log_event!["f" => 50],
1258 ),
1259 (
1261 "@field:(value1 OR value2)",
1262 log_event!["field" => "value1"],
1263 log_event!["field" => "value"],
1264 ),
1265 (
1267 "@field:value1 OR @field:value2",
1268 log_event!["field" => "value1"],
1269 log_event!["field" => "value"],
1270 ),
1271 (
1273 "-@field1:value1 OR -@field2:value2",
1274 log_event!["field1" => "value1"],
1275 log_event!["field1" => "value1", "field2" => "value2"],
1276 ),
1277 (
1279 "@field:value @field2:value2",
1280 log_event!["field" => "value", "field2" => "value2"],
1281 log_event!["field" => "value", "field2" => "value3"],
1282 ),
1283 (
1285 "@field:(value1 OR \n value2)",
1286 log_event!["field" => "value1"],
1287 log_event!["field" => "value"],
1288 ),
1289 (
1291 "NOT (@field:true AND @field2:value2)",
1292 log_event!["field" => false, "field2" => "value2"],
1293 log_event!["field" => true, "field2" => "value2"],
1294 ),
1295 (
1299 "_exists_:a", log_event!["ddtags" => vec!["a:foo"]], log_event!["ddtags" => vec!["b:foo"]], ),
1303 (
1305 "_exists_:a-b", log_event!["ddtags" => vec!["a-b:foo"]], log_event!["ddtags" => vec!["ab:foo"]], ),
1309 (
1311 "NOT _exists_:a",
1312 log_event!["ddtags" => vec!["b:foo"]],
1313 log_event!("ddtags" => vec!["a:foo"]),
1314 ),
1315 (
1317 "-_exists_:a",
1318 log_event!["ddtags" => vec!["b:foo"]],
1319 log_event!["ddtags" => vec!["a:foo"]],
1320 ),
1321 (
1323 "_missing_:a",
1324 log_event![],
1325 log_event!["ddtags" => vec!["a:foo"]],
1326 ),
1327 (
1329 "NOT _missing_:a",
1330 log_event!["ddtags" => vec!["a:foo"]],
1331 log_event![],
1332 ),
1333 (
1335 "-_missing_:a",
1336 log_event!["ddtags" => vec!["a:foo"]],
1337 log_event![],
1338 ),
1339 (
1341 "a:bla",
1342 log_event!["ddtags" => vec!["a:bla"]],
1343 log_event!["ddtags" => vec!["b:bla"]],
1344 ),
1345 (
1347 "NOT a:bla",
1348 log_event!["ddtags" => vec!["b:bla"]],
1349 log_event!["ddtags" => vec!["a:bla"]],
1350 ),
1351 (
1353 "NOT host:foo",
1354 log_event!["ddtags" => vec!["host:fo o"]],
1355 log_event!["host" => "foo"],
1356 ),
1357 (
1359 "-a:bla",
1360 log_event!["ddtags" => vec!["b:bla"]],
1361 log_event!["ddtags" => vec!["a:bla"]],
1362 ),
1363 (
1365 r#"a:"bla""#,
1366 log_event!["ddtags" => vec!["a:bla"]],
1367 log_event!["a" => "bla"],
1368 ),
1369 (
1371 r#"NOT a:"bla""#,
1372 log_event!["a" => "bla"],
1373 log_event!["ddtags" => vec!["a:bla"]],
1374 ),
1375 (
1377 r#"-a:"bla""#,
1378 log_event!["a" => "bla"],
1379 log_event!["ddtags" => vec!["a:bla"]],
1380 ),
1381 (
1383 "@a:bla",
1384 log_event!["a" => "bla"],
1385 log_event!["ddtags" => vec!["a:bla"]],
1386 ),
1387 (
1389 "NOT @a:bla",
1390 log_event!["ddtags" => vec!["a:bla"]],
1391 log_event!["a" => "bla"],
1392 ),
1393 (
1395 "-@a:bla",
1396 log_event!["ddtags" => vec!["a:bla"]],
1397 log_event!["a" => "bla"],
1398 ),
1399 (
1401 r#"@a:"bla""#,
1402 log_event!["a" => "bla"],
1403 log_event!["ddtags" => vec!["a:bla"]],
1404 ),
1405 (
1407 r#"NOT @a:"bla""#,
1408 log_event!["ddtags" => vec!["a:bla"]],
1409 log_event!["a" => "bla"],
1410 ),
1411 (
1413 r#"-@a:"bla""#,
1414 log_event!["ddtags" => vec!["a:bla"]],
1415 log_event!["a" => "bla"],
1416 ),
1417 (
1419 "@a:200",
1420 log_event!["a" => 200],
1421 log_event!["ddtags" => vec!["a:200"]],
1422 ),
1423 (
1425 "@a:0.75",
1426 log_event!["a" => 0.75],
1427 log_event!["ddtags" => vec!["a:0.75"]],
1428 ),
1429 (
1430 "a:*bla",
1431 log_event!["ddtags" => vec!["a:foobla"]],
1432 log_event!["ddtags" => vec!["a:blafoo"]],
1433 ),
1434 (
1436 "NOT a:*bla",
1437 log_event!["ddtags" => vec!["a:blafoo"]],
1438 log_event!["ddtags" => vec!["a:foobla"]],
1439 ),
1440 (
1442 "-a:*bla",
1443 log_event!["ddtags" => vec!["a:blafoo"]],
1444 log_event!["ddtags" => vec!["a:foobla"]],
1445 ),
1446 (
1448 "b:bla*",
1449 log_event!["ddtags" => vec!["b:blabop"]],
1450 log_event!["ddtags" => vec!["b:bopbla"]],
1451 ),
1452 (
1454 "NOT b:bla*",
1455 log_event!["ddtags" => vec!["b:bopbla"]],
1456 log_event!["ddtags" => vec!["b:blabop"]],
1457 ),
1458 (
1460 "-b:bla*",
1461 log_event!["ddtags" => vec!["b:bopbla"]],
1462 log_event!["ddtags" => vec!["b:blabop"]],
1463 ),
1464 (
1466 "c:*b*la*",
1467 log_event!["ddtags" => vec!["c:foobla"]],
1468 log_event!["custom" => r#"{"title" => "foobla"}"#],
1469 ),
1470 (
1472 "NOT c:*b*la*",
1473 log_event!["custom" => r#"{"title" => "foobla"}"#],
1474 log_event!["ddtags" => vec!["c:foobla"]],
1475 ),
1476 (
1478 "-c:*b*la*",
1479 log_event!["custom" => r#"{"title" => "foobla"}"#],
1480 log_event!["ddtags" => vec!["c:foobla"]],
1481 ),
1482 (
1484 "@a:*bla",
1485 log_event!["a" => "foobla"],
1486 log_event!["ddtags" => vec!["a:foobla"]],
1487 ),
1488 (
1490 "NOT @a:*bla",
1491 log_event!["ddtags" => vec!["a:foobla"]],
1492 log_event!["a" => "foobla"],
1493 ),
1494 (
1496 "-@a:*bla",
1497 log_event!["ddtags" => vec!["a:foobla"]],
1498 log_event!["a" => "foobla"],
1499 ),
1500 (
1502 "@b:bla*",
1503 log_event!["b" => "blabop"],
1504 log_event!["ddtags" => vec!["b:blabop"]],
1505 ),
1506 (
1508 "NOT @b:bla*",
1509 log_event!["ddtags" => vec!["b:blabop"]],
1510 log_event!["b" => "blabop"],
1511 ),
1512 (
1514 "-@b:bla*",
1515 log_event!["ddtags" => vec!["b:blabop"]],
1516 log_event!["b" => "blabop"],
1517 ),
1518 (
1520 "@c:*b*la*",
1521 log_event!["c" => "foobla"],
1522 log_event!["ddtags" => vec!["c:foobla"]],
1523 ),
1524 (
1526 "NOT @c:*b*la*",
1527 log_event!["ddtags" => vec!["c:foobla"]],
1528 log_event!["c" => "foobla"],
1529 ),
1530 (
1532 "-@c:*b*la*",
1533 log_event!["ddtags" => vec!["c:foobla"]],
1534 log_event!["c" => "foobla"],
1535 ),
1536 (
1538 "tags:a",
1539 log_event!["ddtags" => vec!["a", "b", "c"]],
1540 log_event!["ddtags" => vec!["d", "e", "f"]],
1541 ),
1542 (
1544 "NOT tags:a",
1545 log_event!["ddtags" => vec!["d", "e", "f"]],
1546 log_event!["ddtags" => vec!["a", "b", "c"]],
1547 ),
1548 (
1550 "-tags:a",
1551 log_event!["ddtags" => vec!["d", "e", "f"]],
1552 log_event!["ddtags" => vec!["a", "b", "c"]],
1553 ),
1554 (
1557 "source:foo",
1558 log_event!["source" => "foo"],
1559 log_event!["tags" => vec!["source:foo"]],
1560 ),
1561 (
1562 "source:foo",
1563 log_event!["source" => "foo"],
1564 log_event!["source" => "foobar"],
1565 ),
1566 (
1567 "source:foo",
1568 log_event!["source" => "foo"],
1569 log_event!["source" => r#"{"value": "foo"}"#],
1570 ),
1571 (
1573 "source:foo",
1574 log_event!["ddsource" => "foo"],
1575 log_event!["tags" => vec!["ddsource:foo"]],
1576 ),
1577 (
1578 "source:foo",
1579 log_event!["ddsource" => "foo"],
1580 log_event!["ddsource" => "foobar"],
1581 ),
1582 (
1583 "source:foo",
1584 log_event!["ddsource" => "foo"],
1585 log_event!["ddsource" => r#"{"value": "foo"}"#],
1586 ),
1587 (
1589 "source:foo",
1590 log_event!["source" => "foo", "ddsource" => "foo"],
1591 log_event!["source" => "foobar", "ddsource" => "foobar"],
1592 ),
1593 (
1594 "source:foo",
1595 log_event!["source" => "foo", "ddsource" => "foobar"],
1596 log_event!["source" => "foobar", "ddsource" => "foobar"],
1597 ),
1598 (
1599 "source:foo",
1600 log_event!["source" => "foobar", "ddsource" => "foo"],
1601 log_event!["source" => "foobar", "ddsource" => "foobar"],
1602 ),
1603 ]
1604 }
1605
1606 fn test_filter<V, F, P>(filter: F, processor: P)
1610 where
1611 V: std::fmt::Debug + Send + Sync + Clone + 'static,
1612 F: Filter<V> + Resolver,
1613 P: Fn(Event) -> V,
1614 {
1615 let checks = get_checks();
1616
1617 for (source, pass, fail) in checks {
1618 let node: QueryNode = source.parse().unwrap();
1619 let matcher = build_matcher(&node, &filter).unwrap();
1620
1621 assert!(matcher.run(&processor(pass)));
1622 assert!(!matcher.run(&processor(fail)));
1623 }
1624 }
1625
1626 #[test]
1627 fn event_filter() {
1629 test_filter(EventFilter, |ev| ev.into_log())
1630 }
1631
1632 #[test]
1633 fn generate_config() {
1634 crate::test_util::test_generate_config::<DatadogSearchConfig>();
1635 }
1636
1637 #[test]
1638 fn check_datadog() {
1639 for (source, pass, fail) in get_checks() {
1640 let config: DatadogSearchConfig = source.parse().unwrap();
1641
1642 let cond = config
1644 .build(&Default::default(), &Default::default())
1645 .unwrap_or_else(|_| panic!("build failed: {source}"));
1646
1647 assert!(
1648 cond.check_with_context(pass.clone()).0.is_ok(),
1649 "should pass: {}\nevent: {}",
1650 source,
1651 serde_json::to_string(&pass.as_log()).unwrap(),
1652 );
1653
1654 assert!(
1655 cond.check_with_context(fail.clone()).0.is_err(),
1656 "should fail: {}\nevent: {}",
1657 source,
1658 serde_json::to_string(&fail.as_log()).unwrap(),
1659 );
1660 }
1661 }
1662}