@@ -24,6 +24,7 @@ public sealed class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher,
24
24
// May be null; if it is, Log.Logger will be lazily used
25
25
readonly ILogger ? _logger ;
26
26
readonly Action ? _dispose ;
27
+ readonly ThreadLocal < ScopeCollector > _scopeCollector = new ( ( ) => new ScopeCollector ( ) ) ;
27
28
#if FEATURE_ASYNCDISPOSABLE
28
29
readonly Func < ValueTask > ? _disposeAsync ;
29
30
#endif
@@ -90,35 +91,38 @@ public IDisposable BeginScope<T>(T state)
90
91
/// <inheritdoc />
91
92
public void Enrich ( LogEvent logEvent , ILogEventPropertyFactory propertyFactory )
92
93
{
93
- List < LogEventPropertyValue > ? scopeItems = null ;
94
+ var scopeCollector = _scopeCollector . Value ! ;
95
+
94
96
for ( var scope = CurrentScope ; scope != null ; scope = scope . Parent )
95
97
{
96
98
scope . EnrichAndCreateScopeItem ( logEvent , propertyFactory , out var scopeItem ) ;
97
99
98
100
if ( scopeItem != null )
99
101
{
100
- scopeItems ??= [ ] ;
101
- scopeItems . Add ( scopeItem ) ;
102
+ scopeCollector . AddItem ( scopeItem ) ;
102
103
}
103
104
}
104
105
105
- scopeItems ? . Reverse ( ) ;
106
+ scopeCollector . ReverseItems ( ) ;
106
107
107
- _externalScopeProvider ? . ForEachScope ( ( state , accumulatingLogEvent ) =>
108
+ _externalScopeProvider ? . ForEachScope ( static ( state , parameters ) =>
108
109
{
109
110
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 ) ;
111
116
112
117
if ( scopeItem != null )
113
118
{
114
- scopeItems ??= new List < LogEventPropertyValue > ( ) ;
115
- scopeItems . Add ( scopeItem ) ;
119
+ parameters . ScopeCollector . AddItem ( scopeItem ) ;
116
120
}
117
- } , logEvent ) ;
121
+ } , ( ScopeCollector : scopeCollector , PropertyFactory : propertyFactory , LogEvent : logEvent ) ) ;
118
122
119
- if ( scopeItems != null )
123
+ if ( scopeCollector . Complete ( ) is { } items )
120
124
{
121
- logEvent . AddPropertyIfAbsent ( new LogEventProperty ( ScopePropertyName , new SequenceValue ( scopeItems ) ) ) ;
125
+ logEvent . AddPropertyIfAbsent ( new LogEventProperty ( ScopePropertyName , new SequenceValue ( items ) ) ) ;
122
126
}
123
127
}
124
128
@@ -149,4 +153,27 @@ public ValueTask DisposeAsync()
149
153
return _disposeAsync ? . Invoke ( ) ?? default ;
150
154
}
151
155
#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
+ }
152
179
}
0 commit comments