Skip to content

Commit d5eb130

Browse files
committed
Make SerilogLoggerProvider implement IAsyncDisposable on .NET 6 or later.
1 parent 4f3305a commit d5eb130

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Threading.Tasks;
45
using Microsoft.Extensions.Logging;
56
using Serilog.Core;
67
using Serilog.Events;
@@ -14,13 +15,19 @@ namespace Serilog.Extensions.Logging;
1415
/// </summary>
1516
[ProviderAlias("Serilog")]
1617
public class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher, ISupportExternalScope
18+
#if NET6_0_OR_GREATER
19+
, IAsyncDisposable
20+
#endif
1721
{
1822
internal const string OriginalFormatPropertyName = "{OriginalFormat}";
1923
internal const string ScopePropertyName = "Scope";
2024

2125
// May be null; if it is, Log.Logger will be lazily used
2226
readonly ILogger? _logger;
2327
readonly Action? _dispose;
28+
#if NET6_0_OR_GREATER
29+
readonly Func<ValueTask>? _disposeAsync;
30+
#endif
2431
private IExternalScopeProvider? _externalScopeProvider;
2532

2633
/// <summary>
@@ -36,9 +43,25 @@ public SerilogLoggerProvider(ILogger? logger = null, bool dispose = false)
3643
if (dispose)
3744
{
3845
if (logger != null)
46+
{
3947
_dispose = () => (logger as IDisposable)?.Dispose();
48+
#if NET6_0_OR_GREATER
49+
_disposeAsync = () =>
50+
{
51+
// Dispose via IAsyncDisposable if possible, otherwise fall back to IDisposable
52+
if (logger is IAsyncDisposable asyncDisposable) return asyncDisposable.DisposeAsync();
53+
else (logger as IDisposable)?.Dispose();
54+
return default;
55+
};
56+
#endif
57+
}
4058
else
59+
{
4160
_dispose = Log.CloseAndFlush;
61+
#if NET6_0_OR_GREATER
62+
_disposeAsync = Log.CloseAndFlushAsync;
63+
#endif
64+
}
4265
}
4366
}
4467

@@ -113,4 +136,12 @@ public void Dispose()
113136
{
114137
_dispose?.Invoke();
115138
}
139+
140+
#if NET6_0_OR_GREATER
141+
/// <inheritdoc />
142+
public ValueTask DisposeAsync()
143+
{
144+
return _disposeAsync?.Invoke() ?? default;
145+
}
146+
#endif
116147
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Logging;
3+
using Serilog.Core;
4+
using Serilog.Events;
5+
using Xunit;
6+
7+
namespace Serilog.Extensions.Logging.Tests;
8+
9+
public class DisposeTests
10+
{
11+
private readonly DisposableSink _sink;
12+
private readonly Logger _serilogLogger;
13+
14+
public DisposeTests()
15+
{
16+
_sink = new DisposableSink();
17+
_serilogLogger = new LoggerConfiguration()
18+
.WriteTo.Sink(_sink)
19+
.CreateLogger();
20+
}
21+
22+
[Fact]
23+
public void AddSerilog_must_dispose_the_provider_when_dispose_is_true()
24+
{
25+
var services = new ServiceCollection()
26+
.AddLogging(builder => builder.AddSerilog(logger: _serilogLogger, dispose: true))
27+
.BuildServiceProvider();
28+
29+
// Get a logger so that we ensure SerilogLoggerProvider is created
30+
var logger = services.GetRequiredService<ILogger<DisposeTests>>();
31+
logger.LogInformation("Hello, world!");
32+
33+
services.Dispose();
34+
Assert.True(_sink.DisposeCalled);
35+
Assert.False(_sink.DisposeAsyncCalled);
36+
}
37+
38+
#if NET8_0_OR_GREATER
39+
[Fact]
40+
public async Task AddSerilog_must_async_dispose_the_provider_when_dispose_is_true()
41+
{
42+
var services = new ServiceCollection()
43+
.AddLogging(builder => builder.AddSerilog(logger: _serilogLogger, dispose: true))
44+
.BuildServiceProvider();
45+
46+
// Get a logger so that we ensure SerilogLoggerProvider is created
47+
var logger = services.GetRequiredService<ILogger<DisposeTests>>();
48+
logger.LogInformation("Hello, world!");
49+
50+
await services.DisposeAsync();
51+
Assert.False(_sink.DisposeCalled);
52+
Assert.True(_sink.DisposeAsyncCalled);
53+
}
54+
#endif
55+
56+
private sealed class DisposableSink : ILogEventSink, IDisposable, IAsyncDisposable
57+
{
58+
public bool DisposeAsyncCalled { get; private set; }
59+
public bool DisposeCalled { get; private set; }
60+
61+
public void Dispose() => DisposeCalled = true;
62+
public ValueTask DisposeAsync()
63+
{
64+
DisposeAsyncCalled = true;
65+
return default;
66+
}
67+
68+
public void Emit(LogEvent logEvent)
69+
{
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)