@@ -12,6 +12,7 @@ use lookup::{
12
12
use serde:: { Deserialize , Deserializer } ;
13
13
use vector_config:: configurable_component;
14
14
use vector_core:: event:: { LogEvent , MaybeAsLogMut } ;
15
+ use vector_core:: schema:: meaning;
15
16
use vrl:: value:: Value ;
16
17
17
18
use crate :: { event:: Event , serde:: skip_serializing_if_default} ;
@@ -128,20 +129,52 @@ impl Transformer {
128
129
129
130
fn apply_only_fields ( & self , log : & mut LogEvent ) {
130
131
if let Some ( only_fields) = self . only_fields . as_ref ( ) {
131
- let old_value = std:: mem:: replace ( log. value_mut ( ) , Value :: Object ( BTreeMap :: new ( ) ) ) ;
132
+ let mut old_value = std:: mem:: replace ( log. value_mut ( ) , Value :: Object ( BTreeMap :: new ( ) ) ) ;
132
133
133
134
for field in only_fields {
134
- if let Some ( value) = old_value. get ( field) {
135
- log. insert ( ( PathPrefix :: Event , field) , value. clone ( ) ) ;
135
+ if let Some ( value) = old_value. remove ( field, true ) {
136
+ log. insert ( ( PathPrefix :: Event , field) , value) ;
137
+ }
138
+ }
139
+
140
+ // We may need the service field to apply tags to emitted metrics after the log message has been pruned. If there
141
+ // is a service meaning, we move this value to `dropped_fields` in the metadata.
142
+ // If the field is still in the new log message after pruning it will have been removed from `old_value` above.
143
+ let service_path = log
144
+ . metadata ( )
145
+ . schema_definition ( )
146
+ . meaning_path ( meaning:: SERVICE ) ;
147
+ if let Some ( service_path) = service_path {
148
+ let mut new_log = LogEvent :: from ( old_value) ;
149
+ if let Some ( service) = new_log. remove ( service_path) {
150
+ log. metadata_mut ( )
151
+ . add_dropped_field ( meaning:: SERVICE . to_string ( ) , service) ;
136
152
}
137
153
}
138
154
}
139
155
}
140
156
141
157
fn apply_except_fields ( & self , log : & mut LogEvent ) {
158
+ use lookup:: path:: TargetPath ;
159
+
142
160
if let Some ( except_fields) = self . except_fields . as_ref ( ) {
161
+ let service_path = log
162
+ . metadata ( )
163
+ . schema_definition ( )
164
+ . meaning_path ( meaning:: SERVICE )
165
+ . map ( |path| path. value_path ( ) . to_string ( ) ) ;
166
+
143
167
for field in except_fields {
144
- log. remove ( field. as_str ( ) ) ;
168
+ let value = log. remove ( field. as_str ( ) ) ;
169
+
170
+ // If we are removing the service field we need to store this in a `dropped_fields` list as we may need to
171
+ // refer to this later when emitting metrics.
172
+ if let Some ( v) = value {
173
+ if matches ! ( service_path. as_ref( ) , Some ( path) if path == field) {
174
+ log. metadata_mut ( )
175
+ . add_dropped_field ( meaning:: SERVICE . to_string ( ) , v) ;
176
+ }
177
+ }
145
178
}
146
179
}
147
180
}
@@ -213,10 +246,15 @@ pub enum TimestampFormat {
213
246
#[ cfg( test) ]
214
247
mod tests {
215
248
use indoc:: indoc;
216
- use vector_core:: config:: log_schema;
249
+ use lookup:: path:: parse_target_path;
250
+ use vector_common:: btreemap;
251
+ use vector_core:: config:: { log_schema, LogNamespace } ;
252
+ use vrl:: value:: Kind ;
253
+
254
+ use crate :: config:: schema;
217
255
218
256
use super :: * ;
219
- use std:: collections:: BTreeMap ;
257
+ use std:: { collections:: BTreeMap , sync :: Arc } ;
220
258
221
259
#[ test]
222
260
fn serialize ( ) {
@@ -374,4 +412,83 @@ mod tests {
374
412
"# } ) ;
375
413
assert ! ( config. is_err( ) )
376
414
}
415
+
416
+ #[ test]
417
+ fn only_fields_with_service ( ) {
418
+ let transformer: Transformer = toml:: from_str ( r#"only_fields = ["message"]"# ) . unwrap ( ) ;
419
+ let mut log = LogEvent :: default ( ) ;
420
+ {
421
+ log. insert ( "message" , 1 ) ;
422
+ log. insert ( "thing.service" , "carrot" ) ;
423
+ }
424
+
425
+ let schema = schema:: Definition :: new_with_default_metadata (
426
+ Kind :: object ( btreemap ! {
427
+ "thing" => Kind :: object( btreemap! {
428
+ "service" => Kind :: bytes( ) ,
429
+ } )
430
+ } ) ,
431
+ [ LogNamespace :: Vector ] ,
432
+ ) ;
433
+
434
+ let schema = schema. with_meaning ( parse_target_path ( "thing.service" ) . unwrap ( ) , "service" ) ;
435
+
436
+ let mut event = Event :: from ( log) ;
437
+
438
+ event
439
+ . metadata_mut ( )
440
+ . set_schema_definition ( & Arc :: new ( schema) ) ;
441
+
442
+ transformer. transform ( & mut event) ;
443
+ assert ! ( event. as_mut_log( ) . contains( "message" ) ) ;
444
+
445
+ // Event no longer contains the service field.
446
+ assert ! ( !event. as_mut_log( ) . contains( "thing.service" ) ) ;
447
+
448
+ // But we can still get the service by meaning.
449
+ assert_eq ! (
450
+ & Value :: from( "carrot" ) ,
451
+ event. as_log( ) . get_by_meaning( "service" ) . unwrap( )
452
+ ) ;
453
+ }
454
+
455
+ #[ test]
456
+ fn except_fields_with_service ( ) {
457
+ let transformer: Transformer =
458
+ toml:: from_str ( r#"except_fields = ["thing.service"]"# ) . unwrap ( ) ;
459
+ let mut log = LogEvent :: default ( ) ;
460
+ {
461
+ log. insert ( "message" , 1 ) ;
462
+ log. insert ( "thing.service" , "carrot" ) ;
463
+ }
464
+
465
+ let schema = schema:: Definition :: new_with_default_metadata (
466
+ Kind :: object ( btreemap ! {
467
+ "thing" => Kind :: object( btreemap! {
468
+ "service" => Kind :: bytes( ) ,
469
+ } )
470
+ } ) ,
471
+ [ LogNamespace :: Vector ] ,
472
+ ) ;
473
+
474
+ let schema = schema. with_meaning ( parse_target_path ( "thing.service" ) . unwrap ( ) , "service" ) ;
475
+
476
+ let mut event = Event :: from ( log) ;
477
+
478
+ event
479
+ . metadata_mut ( )
480
+ . set_schema_definition ( & Arc :: new ( schema) ) ;
481
+
482
+ transformer. transform ( & mut event) ;
483
+ assert ! ( event. as_mut_log( ) . contains( "message" ) ) ;
484
+
485
+ // Event no longer contains the service field.
486
+ assert ! ( !event. as_mut_log( ) . contains( "thing.service" ) ) ;
487
+
488
+ // But we can still get the service by meaning.
489
+ assert_eq ! (
490
+ & Value :: from( "carrot" ) ,
491
+ event. as_log( ) . get_by_meaning( "service" ) . unwrap( )
492
+ ) ;
493
+ }
377
494
}
0 commit comments