Skip to content

Commit 8fb1e0f

Browse files
committed
Remove closure allocation in SerilogLoggerProvider.
1 parent 924776e commit 8fb1e0f

File tree

1 file changed

+33
-11
lines changed

1 file changed

+33
-11
lines changed

src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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,36 +91,41 @@ 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.ScopeItems?.Reverse();
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.ScopeItems != null)
120124
{
121-
logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeItems)));
125+
logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeCollector.ScopeItems)));
122126
}
127+
128+
scopeCollector.Clear();
123129
}
124130

125131
/// <inheritdoc />
@@ -149,4 +155,20 @@ public ValueTask DisposeAsync()
149155
return _disposeAsync?.Invoke() ?? default;
150156
}
151157
#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+
}
152174
}

0 commit comments

Comments
 (0)