Skip to content

Commit d8c75ec

Browse files
authored
Merge pull request #277 from AndreReise/avoid-closure-allocation
Remove closure allocation in SerilogLoggerProvider.
2 parents 924776e + 46dfbf4 commit d8c75ec

File tree

1 file changed

+38
-11
lines changed

1 file changed

+38
-11
lines changed

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

Lines changed: 38 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,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

Comments
 (0)