@@ -195,10 +195,7 @@ pub mod tonic {
195
195
let scope_logs = scope_map
196
196
. into_iter ( )
197
197
. map ( |( key, log_data) | ScopeLogs {
198
- scope : Some ( InstrumentationScope :: from ( (
199
- log_data. first ( ) . unwrap ( ) . 1 ,
200
- Some ( key. into_owned ( ) . into ( ) ) ,
201
- ) ) ) ,
198
+ scope : Some ( new_instrumentation_scope ( log_data. first ( ) . unwrap ( ) . 1 , key) ) ,
202
199
schema_url : resource. schema_url . clone ( ) . unwrap_or_default ( ) ,
203
200
log_records : log_data
204
201
. into_iter ( )
@@ -217,6 +214,22 @@ pub mod tonic {
217
214
schema_url: resource. schema_url. clone( ) . unwrap_or_default( ) ,
218
215
} ]
219
216
}
217
+
218
+ /// Ensure that InstrumentationScope is propagated correctly.
219
+ /// These are the rules:
220
+ ///
221
+ /// 1. If the log scope name matches the key, return a new scope with all the fields from the original scope.
222
+ /// 2. Otherwise, return a new scope with the key as the name of the scope, and the rest of the fields empty.
223
+ fn new_instrumentation_scope (
224
+ log_scope : & opentelemetry:: InstrumentationScope ,
225
+ key : Cow < ' static , str > ,
226
+ ) -> InstrumentationScope {
227
+ if key == log_scope. name ( ) {
228
+ return InstrumentationScope :: from ( ( log_scope, None ) ) ;
229
+ }
230
+
231
+ InstrumentationScope :: from ( ( log_scope, Some ( key) ) )
232
+ }
220
233
}
221
234
222
235
#[ cfg( test) ]
@@ -227,6 +240,7 @@ mod tests {
227
240
use opentelemetry:: logs:: LoggerProvider ;
228
241
use opentelemetry:: time:: now;
229
242
use opentelemetry:: InstrumentationScope ;
243
+ use opentelemetry:: KeyValue ;
230
244
use opentelemetry_sdk:: error:: OTelSdkResult ;
231
245
use opentelemetry_sdk:: logs:: LogProcessor ;
232
246
use opentelemetry_sdk:: logs:: SdkLoggerProvider ;
@@ -246,6 +260,7 @@ mod tests {
246
260
fn create_test_log_data (
247
261
instrumentation_name : & str ,
248
262
_message : & str ,
263
+ attrs : Vec < KeyValue > ,
249
264
) -> ( SdkLogRecord , InstrumentationScope ) {
250
265
let processor = MockProcessor { } ;
251
266
let logger = SdkLoggerProvider :: builder ( )
@@ -255,16 +270,17 @@ mod tests {
255
270
let mut logrecord = logger. create_log_record ( ) ;
256
271
logrecord. set_timestamp ( now ( ) ) ;
257
272
logrecord. set_observed_timestamp ( now ( ) ) ;
258
- let instrumentation =
259
- InstrumentationScope :: builder ( instrumentation_name. to_string ( ) ) . build ( ) ;
273
+ let instrumentation = InstrumentationScope :: builder ( instrumentation_name. to_string ( ) )
274
+ . with_attributes ( attrs)
275
+ . build ( ) ;
260
276
( logrecord, instrumentation)
261
277
}
262
278
263
279
#[ test]
264
280
fn test_group_logs_by_resource_and_scope_single_scope ( ) {
265
281
let resource = Resource :: builder ( ) . build ( ) ;
266
- let ( log_record1, instrum_lib1) = create_test_log_data ( "test-lib" , "Log 1" ) ;
267
- let ( log_record2, instrum_lib2) = create_test_log_data ( "test-lib" , "Log 2" ) ;
282
+ let ( log_record1, instrum_lib1) = create_test_log_data ( "test-lib" , "Log 1" , vec ! [ ] ) ;
283
+ let ( log_record2, instrum_lib2) = create_test_log_data ( "test-lib" , "Log 2" , vec ! [ ] ) ;
268
284
269
285
let logs = [ ( & log_record1, & instrum_lib1) , ( & log_record2, & instrum_lib2) ] ;
270
286
let log_batch = LogBatch :: new ( & logs) ;
@@ -284,8 +300,8 @@ mod tests {
284
300
#[ test]
285
301
fn test_group_logs_by_resource_and_scope_multiple_scopes ( ) {
286
302
let resource = Resource :: builder ( ) . build ( ) ;
287
- let ( log_record1, instrum_lib1) = create_test_log_data ( "lib1" , "Log 1" ) ;
288
- let ( log_record2, instrum_lib2) = create_test_log_data ( "lib2" , "Log 2" ) ;
303
+ let ( log_record1, instrum_lib1) = create_test_log_data ( "lib1" , "Log 1" , vec ! [ ] ) ;
304
+ let ( log_record2, instrum_lib2) = create_test_log_data ( "lib2" , "Log 2" , vec ! [ ] ) ;
289
305
290
306
let logs = [ ( & log_record1, & instrum_lib1) , ( & log_record2, & instrum_lib2) ] ;
291
307
let log_batch = LogBatch :: new ( & logs) ;
@@ -311,4 +327,111 @@ mod tests {
311
327
assert_eq ! ( scope_logs_1. log_records. len( ) , 1 ) ;
312
328
assert_eq ! ( scope_logs_2. log_records. len( ) , 1 ) ;
313
329
}
330
+
331
+ #[ test]
332
+ fn test_group_logs_by_resource_preserving_scope_attributes_when_log_target_empty ( ) {
333
+ let resource = Resource :: builder ( ) . build ( ) ;
334
+ let ( log_record1, instrum_lib1) =
335
+ create_test_log_data ( "lib1" , "Log 1" , vec ! [ KeyValue :: new( "key1" , "value1" ) ] ) ;
336
+ let ( log_record2, instrum_lib2) =
337
+ create_test_log_data ( "lib2" , "Log 2" , vec ! [ KeyValue :: new( "key2" , "value2" ) ] ) ;
338
+
339
+ let logs = [ ( & log_record1, & instrum_lib1) , ( & log_record2, & instrum_lib2) ] ;
340
+ let log_batch = LogBatch :: new ( & logs) ;
341
+ let resource: ResourceAttributesWithSchema = ( & resource) . into ( ) ; // Convert Resource to ResourceAttributesWithSchema
342
+ let grouped_logs =
343
+ crate :: transform:: logs:: tonic:: group_logs_by_resource_and_scope ( log_batch, & resource) ;
344
+
345
+ assert_eq ! ( grouped_logs. len( ) , 1 ) ;
346
+ let resource_logs = & grouped_logs[ 0 ] ;
347
+ assert_eq ! ( resource_logs. scope_logs. len( ) , 2 ) ;
348
+
349
+ let scope_logs_1 = & resource_logs
350
+ . scope_logs
351
+ . iter ( )
352
+ . find ( |scope| scope. scope . as_ref ( ) . unwrap ( ) . name == "lib1" )
353
+ . unwrap ( ) ;
354
+ let scope_logs_2 = & resource_logs
355
+ . scope_logs
356
+ . iter ( )
357
+ . find ( |scope| scope. scope . as_ref ( ) . unwrap ( ) . name == "lib2" )
358
+ . unwrap ( ) ;
359
+
360
+ assert_eq ! ( 1 , scope_logs_1. scope. as_ref( ) . unwrap( ) . attributes. len( ) ) ;
361
+ assert_eq ! ( 1 , scope_logs_2. scope. as_ref( ) . unwrap( ) . attributes. len( ) ) ;
362
+ }
363
+
364
+ #[ test]
365
+ fn test_group_logs_by_resource_preserving_scope_attributes_when_log_target_matching ( ) {
366
+ let resource = Resource :: builder ( ) . build ( ) ;
367
+ let ( mut log_record1, instrum_lib1) =
368
+ create_test_log_data ( "lib1" , "Log 1" , vec ! [ KeyValue :: new( "key1" , "value1" ) ] ) ;
369
+ let ( mut log_record2, instrum_lib2) =
370
+ create_test_log_data ( "lib2" , "Log 2" , vec ! [ KeyValue :: new( "key2" , "value2" ) ] ) ;
371
+
372
+ let logs = [ ( & log_record1, & instrum_lib1) , ( & log_record2, & instrum_lib2) ] ;
373
+ let log_batch = LogBatch :: new ( & logs) ;
374
+ let resource: ResourceAttributesWithSchema = ( & resource) . into ( ) ; // Convert Resource to ResourceAttributesWithSchema
375
+ let grouped_logs =
376
+ crate :: transform:: logs:: tonic:: group_logs_by_resource_and_scope ( log_batch, & resource) ;
377
+
378
+ // This makes the grouping to match the existent InstrumentationScope, preserving the scope attributes.
379
+ log_record1. set_target ( "lib1" ) ;
380
+ log_record2. set_target ( "lib2" ) ;
381
+
382
+ assert_eq ! ( grouped_logs. len( ) , 1 ) ;
383
+ let resource_logs = & grouped_logs[ 0 ] ;
384
+ assert_eq ! ( resource_logs. scope_logs. len( ) , 2 ) ;
385
+
386
+ let scope_logs_1 = & resource_logs
387
+ . scope_logs
388
+ . iter ( )
389
+ . find ( |scope| scope. scope . as_ref ( ) . unwrap ( ) . name == "lib1" )
390
+ . unwrap ( ) ;
391
+ let scope_logs_2 = & resource_logs
392
+ . scope_logs
393
+ . iter ( )
394
+ . find ( |scope| scope. scope . as_ref ( ) . unwrap ( ) . name == "lib2" )
395
+ . unwrap ( ) ;
396
+
397
+ assert_eq ! ( 1 , scope_logs_1. scope. as_ref( ) . unwrap( ) . attributes. len( ) ) ;
398
+ assert_eq ! ( 1 , scope_logs_2. scope. as_ref( ) . unwrap( ) . attributes. len( ) ) ;
399
+ }
400
+
401
+ #[ test]
402
+ fn test_group_logs_by_resource_ignoring_scope_attributes_when_log_target_not_matching ( ) {
403
+ let resource = Resource :: builder ( ) . build ( ) ;
404
+ let ( mut log_record1, instrum_lib1) =
405
+ create_test_log_data ( "lib1" , "Log 1" , vec ! [ KeyValue :: new( "key1" , "value1" ) ] ) ;
406
+ let ( mut log_record2, instrum_lib2) =
407
+ create_test_log_data ( "lib2" , "Log 2" , vec ! [ KeyValue :: new( "key2" , "value2" ) ] ) ;
408
+
409
+ // This makes the grouping to not match the existent InstrumentationScope, ignoring the scope attributes.
410
+ log_record1. set_target ( "target1" ) ;
411
+ log_record2. set_target ( "target2" ) ;
412
+
413
+ let logs = [ ( & log_record1, & instrum_lib1) , ( & log_record2, & instrum_lib2) ] ;
414
+ let log_batch = LogBatch :: new ( & logs) ;
415
+ let resource: ResourceAttributesWithSchema = ( & resource) . into ( ) ; // Convert Resource to ResourceAttributesWithSchema
416
+ let grouped_logs =
417
+ crate :: transform:: logs:: tonic:: group_logs_by_resource_and_scope ( log_batch, & resource) ;
418
+
419
+ assert_eq ! ( grouped_logs. len( ) , 1 ) ;
420
+ let resource_logs = & grouped_logs[ 0 ] ;
421
+ assert_eq ! ( resource_logs. scope_logs. len( ) , 2 ) ;
422
+
423
+ let scope_logs_1 = & resource_logs
424
+ . scope_logs
425
+ . iter ( )
426
+ . find ( |scope| scope. scope . as_ref ( ) . unwrap ( ) . name == "target1" )
427
+ . unwrap ( ) ;
428
+ let scope_logs_2 = & resource_logs
429
+ . scope_logs
430
+ . iter ( )
431
+ . find ( |scope| scope. scope . as_ref ( ) . unwrap ( ) . name == "target2" )
432
+ . unwrap ( ) ;
433
+
434
+ assert ! ( scope_logs_1. scope. as_ref( ) . unwrap( ) . attributes. is_empty( ) ) ;
435
+ assert ! ( scope_logs_2. scope. as_ref( ) . unwrap( ) . attributes. is_empty( ) ) ;
436
+ }
314
437
}
0 commit comments