@@ -117,7 +117,8 @@ fn category_from_body(body_str: &str) -> &'static str {
117117/// and returns a `Map` of the flattened json
118118/// this function is called recursively for each log record object in the otel logs
119119pub fn flatten_log_record ( log_record : & LogRecord ) -> Map < String , Value > {
120- let mut log_record_json: Map < String , Value > = Map :: new ( ) ;
120+ let mut log_record_json: Map < String , Value > =
121+ Map :: with_capacity ( 24 + log_record. attributes . len ( ) ) ;
121122 log_record_json. insert (
122123 "time_unix_nano" . to_string ( ) ,
123124 Value :: String ( convert_epoch_nano_to_timestamp (
@@ -144,16 +145,26 @@ pub fn flatten_log_record(log_record: &LogRecord) -> Map<String, Value> {
144145 log_record_json. insert ( key. clone ( ) , value. clone ( ) ) ;
145146
146147 // If value is a string that can be parsed as JSON object, extract its fields
147- if let Value :: String ( s) = value
148- && let Ok ( parsed) = serde_json:: from_str :: < Value > ( s)
149- && parsed. is_object ( )
150- && let Ok ( flattened_values) = generic_flattening ( & parsed)
151- {
152- for flattened_value in flattened_values {
153- if let Value :: Object ( flattened_obj) = flattened_value {
154- for ( inner_key, inner_value) in flattened_obj {
155- let prefixed_key = format ! ( "{key}_{inner_key}" ) ;
156- log_record_json. insert ( prefixed_key, inner_value) ;
148+ //
149+ if let Value :: String ( s) = value {
150+ let trimmed = s. trim ( ) ;
151+ let looks_like_json = trimmed. len ( ) >= 2
152+ && matches ! (
153+ ( trimmed. as_bytes( ) . first( ) , trimmed. as_bytes( ) . last( ) ) ,
154+ ( Some ( b'{' ) , Some ( b'}' ) ) | ( Some ( b'[' ) , Some ( b']' ) )
155+ ) ;
156+ // Skip speculative JSON parsing unless the body looks like structured JSON
157+ if looks_like_json
158+ && let Ok ( parsed) = serde_json:: from_str :: < Value > ( s)
159+ && parsed. is_object ( )
160+ && let Ok ( flattened_values) = generic_flattening ( & parsed)
161+ {
162+ for flattened_value in flattened_values {
163+ if let Value :: Object ( flattened_obj) = flattened_value {
164+ for ( inner_key, inner_value) in flattened_obj {
165+ let prefixed_key = format ! ( "{key}_{inner_key}" ) ;
166+ log_record_json. insert ( prefixed_key, inner_value) ;
167+ }
157168 }
158169 }
159170 }
@@ -185,7 +196,6 @@ pub fn flatten_log_record(log_record: &LogRecord) -> Map<String, Value> {
185196 "log_record_dropped_attributes_count" . to_string ( ) ,
186197 Value :: Number ( log_record. dropped_attributes_count . into ( ) ) ,
187198 ) ;
188-
189199 log_record_json. insert (
190200 "flags" . to_string ( ) ,
191201 Value :: Number ( ( log_record. flags ) . into ( ) ) ,
@@ -204,9 +214,14 @@ pub fn flatten_log_record(log_record: &LogRecord) -> Map<String, Value> {
204214
205215/// this function flattens the `ScopeLogs` object
206216/// and returns a `Vec` of `Map` of the flattened json
207- fn flatten_scope_log ( scope_log : & ScopeLogs , tenant_id : & str ) -> Vec < Map < String , Value > > {
208- let mut vec_scope_log_json = Vec :: new ( ) ;
209- let mut scope_log_json = Map :: new ( ) ;
217+ fn flatten_scope_log (
218+ scope_log : & ScopeLogs ,
219+ tenant_id : & str ,
220+ resource_base_json : & Map < String , Value > ,
221+ ) -> Vec < Map < String , Value > > {
222+ let mut vec_scope_log_json = Vec :: with_capacity ( scope_log. log_records . len ( ) ) ;
223+ // resources and scope merged once
224+ let mut scope_log_json = resource_base_json. clone ( ) ;
210225 if let Some ( scope) = & scope_log. scope {
211226 scope_log_json. insert ( "scope_name" . to_string ( ) , Value :: String ( scope. name . clone ( ) ) ) ;
212227 scope_log_json. insert (
@@ -219,11 +234,11 @@ fn flatten_scope_log(scope_log: &ScopeLogs, tenant_id: &str) -> Vec<Map<String,
219234 Value :: Number ( scope. dropped_attributes_count . into ( ) ) ,
220235 ) ;
221236 }
237+
222238 scope_log_json. insert (
223239 "scope_log_schema_url" . to_string ( ) ,
224240 Value :: String ( scope_log. schema_url . clone ( ) ) ,
225241 ) ;
226-
227242 for log_record in & scope_log. log_records {
228243 let log_record_json = flatten_log_record ( log_record) ;
229244 let mut combined_json = scope_log_json. clone ( ) ;
@@ -262,20 +277,22 @@ where
262277 ) ;
263278 }
264279
280+ resource_log_json. insert (
281+ "schema_url" . to_string ( ) ,
282+ Value :: String ( get_schema_url ( resource_log) . to_string ( ) ) ,
283+ ) ;
265284 let mut vec_resource_logs_json = Vec :: new ( ) ;
266285 let scope_logs = get_scope_logs ( resource_log) ;
267286
268287 for scope_log in scope_logs {
269- vec_resource_logs_json. extend ( flatten_scope_log ( scope_log, tenant_id) ) ;
288+ vec_resource_logs_json. extend ( flatten_scope_log (
289+ scope_log,
290+ tenant_id,
291+ & resource_log_json,
292+ ) ) ;
270293 }
271294
272- resource_log_json. insert (
273- "schema_url" . to_string ( ) ,
274- Value :: String ( get_schema_url ( resource_log) . to_string ( ) ) ,
275- ) ;
276-
277295 for resource_logs_json in & mut vec_resource_logs_json {
278- resource_logs_json. extend ( resource_log_json. clone ( ) ) ;
279296 vec_otel_json. push ( Value :: Object ( resource_logs_json. clone ( ) ) ) ;
280297 }
281298 }
@@ -292,7 +309,6 @@ pub fn flatten_otel_protobuf(message: &ExportLogsServiceRequest, tenant_id: &str
292309 tenant_id,
293310 )
294311}
295-
296312/// this function performs the custom flattening of the otel logs
297313/// and returns a `Vec` of `Value::Object` of the flattened json
298314pub fn flatten_otel_logs ( message : & LogsData , tenant_id : & str ) -> Vec < Value > {
0 commit comments