-
Notifications
You must be signed in to change notification settings - Fork 105
/
Copy pathSerilogLoggerProvider.cs
166 lines (144 loc) · 5.08 KB
/
SerilogLoggerProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.Logging;
using Serilog.Core;
using Serilog.Events;
using FrameworkLogger = Microsoft.Extensions.Logging.ILogger;
using Serilog.Context;
namespace Serilog.Extensions.Logging;
/// <summary>
/// An <see cref="ILoggerProvider"/> that pipes events through Serilog.
/// </summary>
[ProviderAlias("Serilog")]
public sealed class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher, ISupportExternalScope
#if FEATURE_ASYNCDISPOSABLE
, IAsyncDisposable
#endif
{
internal const string OriginalFormatPropertyName = "{OriginalFormat}";
internal const string ScopePropertyName = "Scope";
// May be null; if it is, Log.Logger will be lazily used
readonly ILogger? _logger;
readonly Action? _dispose;
#if FEATURE_ASYNCDISPOSABLE
readonly Func<ValueTask>? _disposeAsync;
#endif
IExternalScopeProvider? _externalScopeProvider;
/// <summary>
/// Construct a <see cref="SerilogLoggerProvider"/>.
/// </summary>
/// <param name="logger">A Serilog logger to pipe events through; if null, the static <see cref="Log"/> class will be used.</param>
/// <param name="dispose">If true, the provided logger or static log class will be disposed/closed when the provider is disposed.</param>
public SerilogLoggerProvider(ILogger? logger = null, bool dispose = false)
{
if (logger != null)
_logger = logger.ForContext([this]);
if (dispose)
{
if (logger != null)
{
_dispose = () => (logger as IDisposable)?.Dispose();
#if FEATURE_ASYNCDISPOSABLE
_disposeAsync = async () =>
{
if (logger is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
(logger as IDisposable)?.Dispose();
}
};
#endif
}
else
{
_dispose = Log.CloseAndFlush;
#if FEATURE_ASYNCDISPOSABLE
_disposeAsync = Log.CloseAndFlushAsync;
#endif
}
}
}
/// <inheritdoc />
public FrameworkLogger CreateLogger(string name)
{
return new SerilogLogger(this, _logger, name);
}
/// <inheritdoc cref="IDisposable" />
public IDisposable BeginScope<T>(T state)
{
if (CurrentScope != null)
return new SerilogLoggerScope(this, state);
// The outermost scope pushes and pops the Serilog `LogContext` - once
// this enricher is on the stack, the `CurrentScope` property takes care
// of the rest of the `BeginScope()` stack.
var popSerilogContext = LogContext.Push(this);
return new SerilogLoggerScope(this, state, popSerilogContext);
}
/// <inheritdoc />
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
List<LogEventPropertyValue>? scopeItems = null;
for (var scope = CurrentScope; scope != null; scope = scope.Parent)
{
scope.EnrichAndCreateScopeItem(logEvent, propertyFactory, out var scopeItem);
if (scopeItem != null)
{
scopeItems ??= [];
scopeItems.Add(scopeItem);
}
}
scopeItems?.Reverse();
_externalScopeProvider?.ForEachScopeReversed((state, accumulatingLogEvent) =>
{
SerilogLoggerScope.EnrichWithStateAndCreateScopeItem(
accumulatingLogEvent, propertyFactory, state, update: false, out var scopeItem);
if (scopeItem != null)
{
scopeItems ??= new List<LogEventPropertyValue>();
scopeItems.Add(scopeItem);
}
}, logEvent);
if (scopeItems != null)
{
logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeItems)));
}
}
/// <inheritdoc />
public void SetScopeProvider(IExternalScopeProvider scopeProvider)
{
_externalScopeProvider = scopeProvider;
}
readonly AsyncLocal<SerilogLoggerScope?> _value = new();
internal SerilogLoggerScope? CurrentScope
{
get => _value.Value;
set => _value.Value = value;
}
/// <inheritdoc />
public void Dispose()
{
_dispose?.Invoke();
}
#if FEATURE_ASYNCDISPOSABLE
/// <inheritdoc />
public ValueTask DisposeAsync()
{
return _disposeAsync?.Invoke() ?? default;
}
#endif
}
file static class Extensions
{
public static void ForEachScopeReversed<TState>(this IExternalScopeProvider provider, Action<object?, TState> callback, TState state)
{
var list = new List<(object?, TState)>();
provider.ForEachScope((m, n) => list.Add((m, n)), state);
for (var i = list.Count - 1; i >= 0; i--)
{
callback(list[i].Item1, list[i].Item2);
}
}
}