@@ -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,36 +91,41 @@ 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 . ScopeItems ? . Reverse ( ) ;
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 . ScopeItems != null )
120
124
{
121
- logEvent . AddPropertyIfAbsent ( new LogEventProperty ( ScopePropertyName , new SequenceValue ( scopeItems ) ) ) ;
125
+ logEvent . AddPropertyIfAbsent ( new LogEventProperty ( ScopePropertyName , new SequenceValue ( scopeCollector . ScopeItems ) ) ) ;
122
126
}
127
+
128
+ scopeCollector . Clear ( ) ;
123
129
}
124
130
125
131
/// <inheritdoc />
@@ -149,4 +155,20 @@ public ValueTask DisposeAsync()
149
155
return _disposeAsync ? . Invoke ( ) ?? default ;
150
156
}
151
157
#endif
158
+
159
+ /// <summary>
160
+ /// A wrapper around a list to allow lazy initialization when iterating through scopes.
161
+ /// </summary>
162
+ private sealed class ScopeCollector
163
+ {
164
+ public List < LogEventPropertyValue > ? ScopeItems { get ; private set ; }
165
+
166
+ public void AddItem ( LogEventPropertyValue scopeItem )
167
+ {
168
+ ScopeItems ??= [ ] ;
169
+ ScopeItems . Add ( scopeItem ) ;
170
+ }
171
+
172
+ public void Clear ( ) => this . ScopeItems = null ;
173
+ }
152
174
}
0 commit comments