@@ -6,11 +6,13 @@ use lookup::{OwnedTargetPath, OwnedValuePath, PathPrefix};
6
6
use snafu:: Snafu ;
7
7
use vrl:: compiler:: value:: VrlValueConvert ;
8
8
use vrl:: compiler:: { ProgramInfo , SecretTarget , Target } ;
9
- use vrl:: value:: Value ;
9
+ use vrl:: prelude:: Collection ;
10
+ use vrl:: value:: { Kind , Value } ;
10
11
11
12
use super :: { Event , EventMetadata , LogEvent , Metric , MetricKind , TraceEvent } ;
12
- use crate :: config:: log_schema;
13
+ use crate :: config:: { log_schema, LogNamespace } ;
13
14
use crate :: event:: metric:: TagValue ;
15
+ use crate :: schema:: Definition ;
14
16
15
17
const VALID_METRIC_PATHS_SET : & str = ".name, .namespace, .timestamp, .kind, .tags" ;
16
18
@@ -114,11 +116,24 @@ impl VrlTarget {
114
116
}
115
117
}
116
118
119
+ /// Modifies a schema in the same way that the `into_events` function modifies the event
120
+ pub fn modify_schema_definition_for_into_events ( input : Definition ) -> Definition {
121
+ let log_namespaces = input. log_namespaces ( ) . clone ( ) ;
122
+
123
+ // both namespaces merge arrays, but only `Legacy` moves field definitions into a "message" field.
124
+ let merged_arrays = merge_array_definitions ( input) ;
125
+ Definition :: combine_log_namespaces (
126
+ & log_namespaces,
127
+ move_field_definitions_into_message ( merged_arrays. clone ( ) ) ,
128
+ merged_arrays,
129
+ )
130
+ }
131
+
117
132
/// Turn the target back into events.
118
133
///
119
134
/// This returns an iterator of events as one event can be turned into multiple by assigning an
120
135
/// array to `.` in VRL.
121
- pub fn into_events ( self ) -> TargetEvents {
136
+ pub fn into_events ( self , log_namespace : LogNamespace ) -> TargetEvents {
122
137
match self {
123
138
VrlTarget :: LogEvent ( value, metadata) => match value {
124
139
value @ Value :: Object ( _) => {
@@ -131,11 +146,16 @@ impl VrlTarget {
131
146
_marker : PhantomData ,
132
147
} ) ,
133
148
134
- v => {
135
- let mut log = LogEvent :: new_with_metadata ( metadata) ;
136
- log. insert ( log_schema ( ) . message_key ( ) , v) ;
137
- TargetEvents :: One ( log. into ( ) )
138
- }
149
+ v => match log_namespace {
150
+ LogNamespace :: Vector => {
151
+ TargetEvents :: One ( LogEvent :: from_parts ( v, metadata) . into ( ) )
152
+ }
153
+ LogNamespace :: Legacy => {
154
+ let mut log = LogEvent :: new_with_metadata ( metadata) ;
155
+ log. insert ( log_schema ( ) . message_key ( ) , v) ;
156
+ TargetEvents :: One ( log. into ( ) )
157
+ }
158
+ } ,
139
159
} ,
140
160
VrlTarget :: Trace ( value, metadata) => match value {
141
161
value @ Value :: Object ( _) => {
@@ -174,6 +194,53 @@ impl VrlTarget {
174
194
}
175
195
}
176
196
197
+ /// If the VRL returns a value that is not an array (see [`merge_array_definitions`]),
198
+ /// or an object, that data is moved into the `message` field.
199
+ fn move_field_definitions_into_message ( mut definition : Definition ) -> Definition {
200
+ let mut message = definition. event_kind ( ) . clone ( ) ;
201
+ message. remove_object ( ) ;
202
+ message. remove_array ( ) ;
203
+
204
+ if !message. is_never ( ) {
205
+ // We need to add the given message type to a field called `message`
206
+ // in the event.
207
+ let message = Kind :: object ( Collection :: from ( BTreeMap :: from ( [ (
208
+ log_schema ( ) . message_key ( ) . into ( ) ,
209
+ message,
210
+ ) ] ) ) ) ;
211
+
212
+ definition. event_kind_mut ( ) . remove_bytes ( ) ;
213
+ definition. event_kind_mut ( ) . remove_integer ( ) ;
214
+ definition. event_kind_mut ( ) . remove_float ( ) ;
215
+ definition. event_kind_mut ( ) . remove_boolean ( ) ;
216
+ definition. event_kind_mut ( ) . remove_timestamp ( ) ;
217
+ definition. event_kind_mut ( ) . remove_regex ( ) ;
218
+ definition. event_kind_mut ( ) . remove_null ( ) ;
219
+
220
+ * definition. event_kind_mut ( ) = definition. event_kind ( ) . union ( message) ;
221
+ }
222
+
223
+ definition
224
+ }
225
+
226
+ /// If the transform returns an array, the elements of this array will be separated
227
+ /// out into it's individual elements and passed downstream.
228
+ ///
229
+ /// The potential types that the transform can output are any of the arrays
230
+ /// elements or any non-array elements that are within the definition. All these
231
+ /// definitions need to be merged together.
232
+ fn merge_array_definitions ( mut definition : Definition ) -> Definition {
233
+ if let Some ( array) = definition. event_kind ( ) . as_array ( ) {
234
+ let array_kinds = array. reduced_kind ( ) ;
235
+
236
+ let kind = definition. event_kind_mut ( ) ;
237
+ kind. remove_array ( ) ;
238
+ * kind = kind. union ( array_kinds) ;
239
+ }
240
+
241
+ definition
242
+ }
243
+
177
244
fn set_metric_tag_values ( name : String , value : & Value , metric : & mut Metric , multi_value_tags : bool ) {
178
245
if multi_value_tags {
179
246
let tag_values = value
@@ -589,11 +656,107 @@ mod test {
589
656
use lookup:: owned_value_path;
590
657
use similar_asserts:: assert_eq;
591
658
use vrl:: btreemap;
659
+ use vrl:: value:: kind:: Index ;
592
660
593
661
use super :: super :: MetricValue ;
594
662
use super :: * ;
595
663
use crate :: metric_tags;
596
664
665
+ #[ test]
666
+ fn test_field_definitions_in_message ( ) {
667
+ let definition =
668
+ Definition :: new_with_default_metadata ( Kind :: bytes ( ) , [ LogNamespace :: Legacy ] ) ;
669
+ assert_eq ! (
670
+ Definition :: new_with_default_metadata(
671
+ Kind :: object( BTreeMap :: from( [ ( "message" . into( ) , Kind :: bytes( ) ) ] ) ) ,
672
+ [ LogNamespace :: Legacy ]
673
+ ) ,
674
+ move_field_definitions_into_message( definition)
675
+ ) ;
676
+
677
+ // Test when a message field already exists.
678
+ let definition = Definition :: new_with_default_metadata (
679
+ Kind :: object ( BTreeMap :: from ( [ ( "message" . into ( ) , Kind :: integer ( ) ) ] ) ) . or_bytes ( ) ,
680
+ [ LogNamespace :: Legacy ] ,
681
+ ) ;
682
+ assert_eq ! (
683
+ Definition :: new_with_default_metadata(
684
+ Kind :: object( BTreeMap :: from( [ (
685
+ "message" . into( ) ,
686
+ Kind :: bytes( ) . or_integer( )
687
+ ) ] ) ) ,
688
+ [ LogNamespace :: Legacy ]
689
+ ) ,
690
+ move_field_definitions_into_message( definition)
691
+ ) ;
692
+ }
693
+
694
+ #[ test]
695
+ fn test_merged_array_definitions_simple ( ) {
696
+ // Test merging the array definitions where the schema definition
697
+ // is simple, containing only one possible type in the array.
698
+ let object: BTreeMap < vrl:: value:: kind:: Field , Kind > = [
699
+ ( "carrot" . into ( ) , Kind :: bytes ( ) ) ,
700
+ ( "potato" . into ( ) , Kind :: integer ( ) ) ,
701
+ ]
702
+ . into ( ) ;
703
+
704
+ let kind = Kind :: array ( Collection :: from_unknown ( Kind :: object ( object) ) ) ;
705
+
706
+ let definition = Definition :: new_with_default_metadata ( kind, [ LogNamespace :: Legacy ] ) ;
707
+
708
+ let kind = Kind :: object ( BTreeMap :: from ( [
709
+ ( "carrot" . into ( ) , Kind :: bytes ( ) ) ,
710
+ ( "potato" . into ( ) , Kind :: integer ( ) ) ,
711
+ ] ) ) ;
712
+
713
+ let wanted = Definition :: new_with_default_metadata ( kind, [ LogNamespace :: Legacy ] ) ;
714
+ let merged = merge_array_definitions ( definition) ;
715
+
716
+ assert_eq ! ( wanted, merged) ;
717
+ }
718
+
719
+ #[ test]
720
+ fn test_merged_array_definitions_complex ( ) {
721
+ // Test merging the array definitions where the schema definition
722
+ // is fairly complex containing multiple different possible types.
723
+ let object: BTreeMap < vrl:: value:: kind:: Field , Kind > = [
724
+ ( "carrot" . into ( ) , Kind :: bytes ( ) ) ,
725
+ ( "potato" . into ( ) , Kind :: integer ( ) ) ,
726
+ ]
727
+ . into ( ) ;
728
+
729
+ let array: BTreeMap < Index , Kind > = [
730
+ ( Index :: from ( 0 ) , Kind :: integer ( ) ) ,
731
+ ( Index :: from ( 1 ) , Kind :: boolean ( ) ) ,
732
+ (
733
+ Index :: from ( 2 ) ,
734
+ Kind :: object ( BTreeMap :: from ( [ ( "peas" . into ( ) , Kind :: bytes ( ) ) ] ) ) ,
735
+ ) ,
736
+ ]
737
+ . into ( ) ;
738
+
739
+ let mut kind = Kind :: bytes ( ) ;
740
+ kind. add_object ( object) ;
741
+ kind. add_array ( array) ;
742
+
743
+ let definition = Definition :: new_with_default_metadata ( kind, [ LogNamespace :: Legacy ] ) ;
744
+
745
+ let mut kind = Kind :: bytes ( ) ;
746
+ kind. add_integer ( ) ;
747
+ kind. add_boolean ( ) ;
748
+ kind. add_object ( BTreeMap :: from ( [
749
+ ( "carrot" . into ( ) , Kind :: bytes ( ) . or_undefined ( ) ) ,
750
+ ( "potato" . into ( ) , Kind :: integer ( ) . or_undefined ( ) ) ,
751
+ ( "peas" . into ( ) , Kind :: bytes ( ) . or_undefined ( ) ) ,
752
+ ] ) ) ;
753
+
754
+ let wanted = Definition :: new_with_default_metadata ( kind, [ LogNamespace :: Legacy ] ) ;
755
+ let merged = merge_array_definitions ( definition) ;
756
+
757
+ assert_eq ! ( wanted, merged) ;
758
+ }
759
+
597
760
#[ test]
598
761
fn log_get ( ) {
599
762
let cases = vec ! [
@@ -755,7 +918,7 @@ mod test {
755
918
Ok ( Some ( value) )
756
919
) ;
757
920
assert_eq ! (
758
- match target. into_events( ) {
921
+ match target. into_events( LogNamespace :: Legacy ) {
759
922
TargetEvents :: One ( event) => vec![ event] ,
760
923
TargetEvents :: Logs ( events) => events. collect:: <Vec <_>>( ) ,
761
924
TargetEvents :: Traces ( events) => events. collect:: <Vec <_>>( ) ,
@@ -901,7 +1064,7 @@ mod test {
901
1064
Target :: target_insert ( & mut target, & OwnedTargetPath :: event_root ( ) , value) . unwrap ( ) ;
902
1065
903
1066
assert_eq ! (
904
- match target. into_events( ) {
1067
+ match target. into_events( LogNamespace :: Legacy ) {
905
1068
TargetEvents :: One ( event) => vec![ event] ,
906
1069
TargetEvents :: Logs ( events) => events. collect:: <Vec <_>>( ) ,
907
1070
TargetEvents :: Traces ( events) => events. collect:: <Vec <_>>( ) ,
0 commit comments