@@ -17,7 +17,7 @@ public class SerilogLoggerTest
17
17
const string Name = "test" ;
18
18
const string TestMessage = "This is a test" ;
19
19
20
- static Tuple < SerilogLogger , SerilogSink > SetUp ( LogLevel logLevel )
20
+ static Tuple < SerilogLogger , SerilogSink > SetUp ( LogLevel logLevel , IExternalScopeProvider ? externalScopeProvider = null )
21
21
{
22
22
var sink = new SerilogSink ( ) ;
23
23
@@ -29,6 +29,11 @@ static Tuple<SerilogLogger, SerilogSink> SetUp(LogLevel logLevel)
29
29
var provider = new SerilogLoggerProvider ( serilogLogger ) ;
30
30
var logger = ( SerilogLogger ) provider . CreateLogger ( Name ) ;
31
31
32
+ if ( externalScopeProvider is not null )
33
+ {
34
+ provider . SetScopeProvider ( externalScopeProvider ) ;
35
+ }
36
+
32
37
return new Tuple < SerilogLogger , SerilogSink > ( logger , sink ) ;
33
38
}
34
39
@@ -397,6 +402,35 @@ public void NamedScopesAreCaptured()
397
402
Assert . Equal ( "Inner" , items [ 1 ] ) ;
398
403
}
399
404
405
+ [ Fact ]
406
+ public void ExternalScopesAreCaptured ( )
407
+ {
408
+ var externalScopeProvider = new FakeExternalScopeProvider ( ) ;
409
+ var ( logger , sink ) = SetUp ( LogLevel . Trace , externalScopeProvider ) ;
410
+
411
+ externalScopeProvider . Push ( new Dictionary < string , int > ( )
412
+ {
413
+ { "FirstKey" , 1 } ,
414
+ { "SecondKey" , 2 }
415
+ } ) ;
416
+
417
+ var scopeObject = new { ObjectKey = "Some value" } ;
418
+ externalScopeProvider . Push ( scopeObject ) ;
419
+
420
+ logger . Log ( LogLevel . Information , 0 , TestMessage , null ! , null ! ) ;
421
+
422
+ Assert . Single ( sink . Writes ) ;
423
+ Assert . True ( sink . Writes [ 0 ] . Properties . TryGetValue ( SerilogLoggerProvider . ScopePropertyName , out var scopeValue ) ) ;
424
+ var sequence = Assert . IsType < SequenceValue > ( scopeValue ) ;
425
+
426
+ var objectScope = ( ScalarValue ) sequence . Elements . Single ( e => e is ScalarValue ) ;
427
+ Assert . Equal ( scopeObject . ToString ( ) , ( string ? ) objectScope . Value ) ;
428
+
429
+ var dictionaryScope = ( DictionaryValue ) sequence . Elements . Single ( e => e is DictionaryValue ) ;
430
+ Assert . Equal ( 1 , ( ( ScalarValue ) dictionaryScope . Elements . Single ( pair => pair . Key . Value ! . Equals ( "FirstKey" ) ) . Value ) . Value ) ;
431
+ Assert . Equal ( 2 , ( ( ScalarValue ) dictionaryScope . Elements . Single ( pair => pair . Key . Value ! . Equals ( "SecondKey" ) ) . Value ) . Value ) ;
432
+ }
433
+
400
434
class FoodScope : IEnumerable < KeyValuePair < string , object > >
401
435
{
402
436
readonly string _name ;
@@ -446,6 +480,43 @@ class Person
446
480
public string ? LastName { get ; set ; }
447
481
}
448
482
483
+ class FakeExternalScopeProvider : IExternalScopeProvider
484
+ {
485
+ private readonly List < Scope > _scopes = new List < Scope > ( ) ;
486
+
487
+ public void ForEachScope < TState > ( Action < object ? , TState > callback , TState state )
488
+ {
489
+ foreach ( var scope in _scopes )
490
+ {
491
+ if ( scope . IsDisposed ) continue ;
492
+ callback ( scope . Value , state ) ;
493
+ }
494
+ }
495
+
496
+ public IDisposable Push ( object ? state )
497
+ {
498
+ var scope = new Scope ( state ) ;
499
+ _scopes . Add ( scope ) ;
500
+ return scope ;
501
+ }
502
+
503
+ private class Scope : IDisposable
504
+ {
505
+ public bool IsDisposed { get ; set ; } = false ;
506
+ public object ? Value { get ; set ; }
507
+
508
+ public Scope ( object ? value )
509
+ {
510
+ Value = value ;
511
+ }
512
+
513
+ public void Dispose ( )
514
+ {
515
+ IsDisposed = true ;
516
+ }
517
+ }
518
+ }
519
+
449
520
[ Theory ]
450
521
[ InlineData ( 1 ) ]
451
522
[ InlineData ( 10 ) ]
0 commit comments