@@ -24,7 +24,7 @@ use vector_common::{
2424} ;
2525use vrl:: value:: Value ;
2626
27- use super :: { CHUNK_SIZE , SendError , SourceSenderItem } ;
27+ use super :: { CHUNK_SIZE , PostProcessor , SendError , SourceSenderItem } ;
2828use crate :: {
2929 EstimatedJsonEncodedSizeOf ,
3030 config:: { OutputId , log_schema} ,
@@ -93,6 +93,8 @@ pub(super) struct Output {
9393 /// `EventMetadata` for all event sent through here.
9494 id : Arc < OutputId > ,
9595 timeout : Option < Duration > ,
96+ /// Optional post-processing step applied to every event before it is placed on the channel.
97+ post_processor : Option < PostProcessor > ,
9698}
9799
98100#[ derive( Clone , Default ) ]
@@ -129,6 +131,7 @@ impl fmt::Debug for Output {
129131}
130132
131133impl Output {
134+ #[ expect( clippy:: too_many_arguments) ]
132135 pub ( super ) fn new_with_buffer (
133136 n : usize ,
134137 output : String ,
@@ -137,6 +140,7 @@ impl Output {
137140 output_id : OutputId ,
138141 timeout : Option < Duration > ,
139142 ewma_half_life_seconds : Option < f64 > ,
143+ post_processor : Option < PostProcessor > ,
140144 ) -> ( Self , LimitedReceiver < SourceSenderItem > ) {
141145 let limit = MemoryBufferSize :: MaxEvents ( NonZeroUsize :: new ( n) . unwrap ( ) ) ;
142146 let channel_metrics =
@@ -152,6 +156,7 @@ impl Output {
152156 log_definition,
153157 id : Arc :: new ( output_id) ,
154158 timeout,
159+ post_processor,
155160 } ,
156161 rx,
157162 )
@@ -168,7 +173,7 @@ impl Output {
168173
169174 async fn send_inner (
170175 & mut self ,
171- mut events : EventArray ,
176+ events : EventArray ,
172177 unsent_event_count : & mut UnsentEventCount ,
173178 reference : i64 ,
174179 ) -> Result < ( ) , SendError > {
@@ -177,29 +182,49 @@ impl Output {
177182 . iter_events ( )
178183 . for_each ( |event| self . emit_lag_time ( event, reference) ) ;
179184
180- events. iter_events_mut ( ) . for_each ( |mut event| {
181- // attach runtime schema definitions from the source
182- if let Some ( log_definition) = & self . log_definition {
183- event. metadata_mut ( ) . set_schema_definition ( log_definition) ;
184- }
185- event. metadata_mut ( ) . set_upstream_id ( Arc :: clone ( & self . id ) ) ;
186- } ) ;
185+ let post_processor = self . post_processor . clone ( ) ;
186+ let log_definition = self . log_definition . clone ( ) ;
187+ let id = Arc :: clone ( & self . id ) ;
188+
189+ // Iterate over individual events to apply metadata and the optional post-processor.
190+ // We collect into a Vec<Event> first so that we can then re-group them into
191+ // EventArrays (which are homogeneous by type). We send each sub-array separately to
192+ // handle mixed-type batches correctly — no events are silently dropped.
193+ let processed: Vec < Event > = events
194+ . into_events ( )
195+ . map ( |mut event| {
196+ if let Some ( ref def) = log_definition {
197+ event. metadata_mut ( ) . set_schema_definition ( def) ;
198+ }
199+ event. metadata_mut ( ) . set_upstream_id ( Arc :: clone ( & id) ) ;
200+ if let Some ( PostProcessor :: HardCoded ( ref f) ) = post_processor {
201+ f ( & mut event) ;
202+ }
203+ event
204+ } )
205+ . collect ( ) ;
187206
188- let byte_size = events. estimated_json_encoded_size_of ( ) ;
189- let count = events. len ( ) ;
207+ if processed. is_empty ( ) {
208+ return Ok ( ( ) ) ;
209+ }
190210
211+ // Re-group individual events back into typed EventArrays and send each one.
212+ // Using `events_into_arrays(...).collect::<Vec<_>>()` ensures ALL sub-arrays are
213+ // captured — no type group is silently discarded (avoids the `.next()`-only bug).
191214 let send_start = Instant :: now ( ) ;
215+ for sub_array in array:: events_into_arrays ( processed. into_iter ( ) , None ) {
216+ let byte_size = sub_array. estimated_json_encoded_size_of ( ) ;
217+ let count = sub_array. len ( ) ;
192218
193- let send_result = self . send_with_timeout ( events , send_reference) . await ;
219+ self . send_with_timeout ( sub_array , send_reference) . await ? ;
194220
221+ self . events_sent . emit ( CountByteSize ( count, byte_size) ) ;
222+ unsent_event_count. decr ( count) ;
223+ }
195224 if let Some ( send_latency) = & self . metrics . send_latency {
196225 send_latency. record ( send_start. elapsed ( ) . as_secs_f64 ( ) ) ;
197226 }
198227
199- send_result?;
200-
201- self . events_sent . emit ( CountByteSize ( count, byte_size) ) ;
202- unsent_event_count. decr ( count) ;
203228 Ok ( ( ) )
204229 }
205230
@@ -295,6 +320,11 @@ impl Output {
295320 Ok ( ( ) )
296321 }
297322
323+ /// Attach a post-processing step to this output, replacing any previously set one.
324+ pub ( super ) fn set_post_processor ( & mut self , pp : & PostProcessor ) {
325+ self . post_processor = Some ( pp. clone ( ) ) ;
326+ }
327+
298328 /// Calculate the difference between the reference time and the
299329 /// timestamp stored in the given event reference, and emit the
300330 /// different, as expressed in milliseconds, as a histogram.
@@ -336,3 +366,4 @@ const fn get_timestamp_millis(value: &Value) -> Option<i64> {
336366 _ => None ,
337367 }
338368}
369+
0 commit comments