@@ -24,6 +24,7 @@ public sealed class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher,
2424 // May be null; if it is, Log.Logger will be lazily used
2525 readonly ILogger ? _logger ;
2626 readonly Action ? _dispose ;
27+ readonly ThreadLocal < ScopeCollector > _scopeCollector = new ( ( ) => new ScopeCollector ( ) ) ;
2728#if FEATURE_ASYNCDISPOSABLE
2829 readonly Func < ValueTask > ? _disposeAsync ;
2930#endif
@@ -90,35 +91,38 @@ public IDisposable BeginScope<T>(T state)
9091 /// <inheritdoc />
9192 public void Enrich ( LogEvent logEvent , ILogEventPropertyFactory propertyFactory )
9293 {
93- List < LogEventPropertyValue > ? scopeItems = null ;
94+ var scopeCollector = _scopeCollector . Value ! ;
95+
9496 for ( var scope = CurrentScope ; scope != null ; scope = scope . Parent )
9597 {
9698 scope . EnrichAndCreateScopeItem ( logEvent , propertyFactory , out var scopeItem ) ;
9799
98100 if ( scopeItem != null )
99101 {
100- scopeItems ??= [ ] ;
101- scopeItems . Add ( scopeItem ) ;
102+ scopeCollector . AddItem ( scopeItem ) ;
102103 }
103104 }
104105
105- scopeItems ? . Reverse ( ) ;
106+ scopeCollector . ReverseItems ( ) ;
106107
107- _externalScopeProvider ? . ForEachScope ( ( state , accumulatingLogEvent ) =>
108+ _externalScopeProvider ? . ForEachScope ( static ( state , parameters ) =>
108109 {
109110 SerilogLoggerScope . EnrichWithStateAndCreateScopeItem (
110- accumulatingLogEvent , propertyFactory , state , update : true , out var scopeItem ) ;
111+ parameters . LogEvent ,
112+ parameters . PropertyFactory ,
113+ state ,
114+ update : true ,
115+ out var scopeItem ) ;
111116
112117 if ( scopeItem != null )
113118 {
114- scopeItems ??= new List < LogEventPropertyValue > ( ) ;
115- scopeItems . Add ( scopeItem ) ;
119+ parameters . ScopeCollector . AddItem ( scopeItem ) ;
116120 }
117- } , logEvent ) ;
121+ } , ( ScopeCollector : scopeCollector , PropertyFactory : propertyFactory , LogEvent : logEvent ) ) ;
118122
119- if ( scopeItems != null )
123+ if ( scopeCollector . Complete ( ) is { } items )
120124 {
121- logEvent . AddPropertyIfAbsent ( new LogEventProperty ( ScopePropertyName , new SequenceValue ( scopeItems ) ) ) ;
125+ logEvent . AddPropertyIfAbsent ( new LogEventProperty ( ScopePropertyName , new SequenceValue ( items ) ) ) ;
122126 }
123127 }
124128
@@ -149,4 +153,27 @@ public ValueTask DisposeAsync()
149153 return _disposeAsync ? . Invoke ( ) ?? default ;
150154 }
151155#endif
156+
157+ /// <summary>
158+ /// A wrapper around a list to allow lazy initialization when iterating through scopes.
159+ /// </summary>
160+ sealed class ScopeCollector
161+ {
162+ List < LogEventPropertyValue > ? _scopeItems ;
163+
164+ public void AddItem ( LogEventPropertyValue scopeItem )
165+ {
166+ _scopeItems ??= [ ] ;
167+ _scopeItems . Add ( scopeItem ) ;
168+ }
169+
170+ public void ReverseItems ( ) => _scopeItems ? . Reverse ( ) ;
171+
172+ public List < LogEventPropertyValue > ? Complete ( )
173+ {
174+ var scopeItems = _scopeItems ;
175+ _scopeItems = null ;
176+ return scopeItems ;
177+ }
178+ }
152179}
0 commit comments