vector/transforms/
filter.rs1use vector_lib::{
2 config::clone_input_definitions,
3 configurable::configurable_component,
4 internal_event::{Count, InternalEventHandle as _, Registered},
5};
6
7use crate::{
8 conditions::{AnyCondition, Condition},
9 config::{
10 DataType, GenerateConfig, Input, OutputId, TransformConfig, TransformContext,
11 TransformOutput,
12 },
13 event::Event,
14 internal_events::FilterEventsDropped,
15 schema,
16 transforms::{FunctionTransform, OutputBuffer, Transform},
17};
18
19#[configurable_component(transform("filter", "Filter events based on a set of conditions."))]
21#[derive(Clone, Debug)]
22#[serde(deny_unknown_fields)]
23pub struct FilterConfig {
24 #[configurable(derived)]
25 condition: AnyCondition,
29}
30
31impl From<AnyCondition> for FilterConfig {
32 fn from(condition: AnyCondition) -> Self {
33 Self { condition }
34 }
35}
36
37impl GenerateConfig for FilterConfig {
38 fn generate_config() -> toml::Value {
39 toml::from_str(r#"condition = ".message == \"value\"""#).unwrap()
40 }
41}
42
43#[async_trait::async_trait]
44#[typetag::serde(name = "filter")]
45impl TransformConfig for FilterConfig {
46 async fn build(&self, context: &TransformContext) -> crate::Result<Transform> {
47 Ok(Transform::function(Filter::new(self.condition.build(
48 &context.enrichment_tables,
49 &context.metrics_storage,
50 )?)))
51 }
52
53 fn validate_env(&self, context: &TransformContext) -> Result<(), Vec<String>> {
54 self.condition
55 .validate(&context.enrichment_tables, &context.metrics_storage)
56 .map_err(|e| vec![e.to_string()])
57 }
58
59 fn input(&self) -> Input {
60 Input::all()
61 }
62
63 fn outputs(
64 &self,
65 _: &TransformContext,
66 input_definitions: &[(OutputId, schema::Definition)],
67 ) -> Vec<TransformOutput> {
68 vec![TransformOutput::new(
69 DataType::all_bits(),
70 clone_input_definitions(input_definitions),
71 )]
72 }
73
74 fn enable_concurrency(&self) -> bool {
75 true
76 }
77}
78
79#[derive(Clone)]
80pub struct Filter {
81 condition: Condition,
82 events_dropped: Registered<FilterEventsDropped>,
83}
84
85impl Filter {
86 pub fn new(condition: Condition) -> Self {
87 Self {
88 condition,
89 events_dropped: register!(FilterEventsDropped),
90 }
91 }
92}
93
94impl FunctionTransform for Filter {
95 fn transform(&mut self, output: &mut OutputBuffer, event: Event) {
96 let (result, event) = self.condition.check(event);
97 if result {
98 output.push(event);
99 } else {
100 self.events_dropped.emit(Count(1));
101 }
102 }
103}
104
105#[cfg(test)]
106mod test {
107 use std::sync::Arc;
108
109 use tokio::sync::mpsc;
110 use tokio_stream::wrappers::ReceiverStream;
111 use vector_lib::{
112 config::ComponentKey,
113 event::{Metric, MetricKind, MetricValue},
114 };
115
116 use super::*;
117 use crate::{
118 conditions::ConditionConfig,
119 config::schema::Definition,
120 event::{Event, LogEvent},
121 test_util::components::assert_transform_compliance,
122 transforms::test::create_topology,
123 };
124
125 const TEST_SOURCE_COMPONENT_ID: &str = "in";
126 const TEST_UPSTREAM_COMPONENT_ID: &str = "transform";
127 const TEST_SOURCE_TYPE: &str = "unit_test_stream";
128
129 fn set_expected_metadata(event: &mut Event) {
130 event.set_source_id(Arc::new(ComponentKey::from(TEST_SOURCE_COMPONENT_ID)));
131 event.set_upstream_id(Arc::new(OutputId::from(TEST_UPSTREAM_COMPONENT_ID)));
132 event.set_source_type(TEST_SOURCE_TYPE);
133 event
134 .metadata_mut()
135 .set_schema_definition(&Arc::new(Definition::default_legacy_namespace()));
136 }
137
138 #[test]
139 fn generate_config() {
140 crate::test_util::test_generate_config::<super::FilterConfig>();
141 }
142
143 #[tokio::test]
144 async fn filter_basic() {
145 assert_transform_compliance(async {
146 let transform_config = FilterConfig::from(AnyCondition::from(ConditionConfig::IsLog));
147
148 let (tx, rx) = mpsc::channel(1);
149 let (topology, mut out) =
150 create_topology(ReceiverStream::new(rx), transform_config).await;
151
152 let mut log = Event::from(LogEvent::from("message"));
153 tx.send(log.clone()).await.unwrap();
154
155 set_expected_metadata(&mut log);
156
157 assert_eq!(out.recv().await.unwrap(), log);
158
159 let metric = Event::from(Metric::new(
160 "test metric",
161 MetricKind::Incremental,
162 MetricValue::Counter { value: 1.0 },
163 ));
164 tx.send(metric).await.unwrap();
165
166 drop(tx);
167 topology.stop().await;
168 assert_eq!(out.recv().await, None);
169 })
170 .await;
171 }
172}