Skip to content

Commit 2baf34f

Browse files
authored
Merge pull request #20 from nblumhardt/supply-service-provider
Supply services when configuring Serilog inline
2 parents 6a8e484 + df44782 commit 2baf34f

File tree

6 files changed

+120
-29
lines changed

6 files changed

+120
-29
lines changed

appveyor.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
version: '{build}'
22
skip_tags: true
3-
image: Visual Studio 2017
4-
configuration: Release
3+
image: Visual Studio 2019
54
test: off
65
build_script:
76
- ps: ./Build.ps1

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"sdk": {
3-
"version": "2.2.105"
3+
"version": "3.1.201"
44
}
55
}

serilog-extensions-hosting.sln.DotSettings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
<s:Boolean x:Key="/Default/UserDictionary/Words/=appsettings/@EntryIndexedValue">True</s:Boolean>
33
<s:Boolean x:Key="/Default/UserDictionary/Words/=benaadams/@EntryIndexedValue">True</s:Boolean>
44
<s:Boolean x:Key="/Default/UserDictionary/Words/=destructure/@EntryIndexedValue">True</s:Boolean>
5+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enricher/@EntryIndexedValue">True</s:Boolean>
6+
<s:Boolean x:Key="/Default/UserDictionary/Words/=MEDI/@EntryIndexedValue">True</s:Boolean>
57
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2020 Serilog Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Serilog.Core;
16+
using Serilog.Events;
17+
18+
namespace Serilog.Extensions.Hosting
19+
{
20+
// Does nothing, but makes it easy to create an `ILogger` from a Serilog `Logger`
21+
// that will not dispose the underlying pipeline when disposed itself.
22+
class NullEnricher : ILogEventEnricher
23+
{
24+
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
25+
{
26+
}
27+
}
28+
}

src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Serilog support for .NET Core logging in hosted services</Description>
5-
<VersionPrefix>3.0.1</VersionPrefix>
5+
<VersionPrefix>3.1.0</VersionPrefix>
66
<Authors>Microsoft;Serilog Contributors</Authors>
77
<TargetFrameworks>netstandard2.0</TargetFrameworks>
88
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019 Serilog Contributors
1+
// Copyright 2020 Serilog Contributors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -13,9 +13,9 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using Microsoft.Extensions.DependencyInjection;
1617
using Microsoft.Extensions.Hosting;
1718
using Microsoft.Extensions.Logging;
18-
using Microsoft.Extensions.DependencyInjection;
1919
using Serilog.Extensions.Hosting;
2020
using Serilog.Extensions.Logging;
2121

@@ -26,16 +26,30 @@ namespace Serilog
2626
/// </summary>
2727
public static class SerilogHostBuilderExtensions
2828
{
29+
// Used internally to pass information through the container. We need to do this because if `logger` is the
30+
// root logger, registering it as a singleton may lead to disposal along with the container by MEDI. This isn't
31+
// always desirable, i.e. we may be handed a logger and `dispose: false`, so wrapping it keeps us in control
32+
// of when the logger is disposed.
33+
class RegisteredLogger
34+
{
35+
public RegisteredLogger(ILogger logger)
36+
{
37+
Logger = logger;
38+
}
39+
40+
public ILogger Logger { get; }
41+
}
42+
2943
/// <summary>
3044
/// Sets Serilog as the logging provider.
3145
/// </summary>
3246
/// <param name="builder">The host builder to configure.</param>
3347
/// <param name="logger">The Serilog logger; if not supplied, the static <see cref="Serilog.Log"/> will be used.</param>
3448
/// <param name="dispose">When <c>true</c>, dispose <paramref name="logger"/> when the framework disposes the provider. If the
35-
/// logger is not specified but <paramref name="dispose"/> is <c>true</c>, the <see cref="Log.CloseAndFlush()"/> method will be
36-
/// called on the static <see cref="Log"/> class instead.</param>
49+
/// logger is not specified but <paramref name="dispose"/> is <c>true</c>, the <see cref="Serilog.Log.CloseAndFlush()"/> method will be
50+
/// called on the static <see cref="Serilog.Log"/> class instead.</param>
3751
/// <param name="providers">A <see cref="LoggerProviderCollection"/> registered in the Serilog pipeline using the
38-
/// <c>WriteTo.Providers()</c> configuration method, enabling other <see cref="ILoggerProvider"/>s to receive events. By
52+
/// <c>WriteTo.Providers()</c> configuration method, enabling other <see cref="Microsoft.Extensions.Logging.ILoggerProvider"/>s to receive events. By
3953
/// default, only Serilog sinks will receive events.</param>
4054
/// <returns>The host builder.</returns>
4155
public static IHostBuilder UseSerilog(
@@ -71,14 +85,15 @@ public static IHostBuilder UseSerilog(
7185
return builder;
7286
}
7387

88+
7489
/// <summary>Sets Serilog as the logging provider.</summary>
7590
/// <remarks>
7691
/// A <see cref="HostBuilderContext"/> is supplied so that configuration and hosting information can be used.
7792
/// The logger will be shut down when application services are disposed.
7893
/// </remarks>
7994
/// <param name="builder">The host builder to configure.</param>
80-
/// <param name="configureLogger">The delegate for configuring the <see cref="LoggerConfiguration" /> that will be used to construct a <see cref="Microsoft.Extensions.Logging.Logger" />.</param>
81-
/// <param name="preserveStaticLogger">Indicates whether to preserve the value of <see cref="Log.Logger"/>.</param>
95+
/// <param name="configureLogger">The delegate for configuring the <see cref="Serilog.LoggerConfiguration" /> that will be used to construct a <see cref="Serilog.Core.Logger" />.</param>
96+
/// <param name="preserveStaticLogger">Indicates whether to preserve the value of <see cref="Serilog.Log.Logger"/>.</param>
8297
/// <param name="writeToProviders">By default, Serilog does not write events to <see cref="ILoggerProvider"/>s registered through
8398
/// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify
8499
/// <c>true</c> to write events to all providers.</param>
@@ -91,35 +106,80 @@ public static IHostBuilder UseSerilog(
91106
{
92107
if (builder == null) throw new ArgumentNullException(nameof(builder));
93108
if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));
109+
return UseSerilog(
110+
builder,
111+
(hostBuilderContext, services, loggerConfiguration) =>
112+
configureLogger(hostBuilderContext, loggerConfiguration),
113+
preserveStaticLogger: preserveStaticLogger,
114+
writeToProviders: writeToProviders);
115+
}
116+
117+
/// <summary>Sets Serilog as the logging provider.</summary>
118+
/// <remarks>
119+
/// A <see cref="HostBuilderContext"/> is supplied so that configuration and hosting information can be used.
120+
/// The logger will be shut down when application services are disposed.
121+
/// </remarks>
122+
/// <param name="builder">The host builder to configure.</param>
123+
/// <param name="configureLogger">The delegate for configuring the <see cref="Serilog.LoggerConfiguration" /> that will be used to construct a <see cref="Serilog.Core.Logger" />.</param>
124+
/// <param name="preserveStaticLogger">Indicates whether to preserve the value of <see cref="Serilog.Log.Logger"/>.</param>
125+
/// <param name="writeToProviders">By default, Serilog does not write events to <see cref="ILoggerProvider"/>s registered through
126+
/// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify
127+
/// <c>true</c> to write events to all providers.</param>
128+
/// <returns>The host builder.</returns>
129+
public static IHostBuilder UseSerilog(
130+
this IHostBuilder builder,
131+
Action<HostBuilderContext, IServiceProvider, LoggerConfiguration> configureLogger,
132+
bool preserveStaticLogger = false,
133+
bool writeToProviders = false)
134+
{
135+
if (builder == null) throw new ArgumentNullException(nameof(builder));
136+
if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));
94137

95138
builder.ConfigureServices((context, collection) =>
96139
{
97-
var loggerConfiguration = new LoggerConfiguration();
98-
99140
LoggerProviderCollection loggerProviders = null;
100141
if (writeToProviders)
101142
{
102143
loggerProviders = new LoggerProviderCollection();
103-
loggerConfiguration.WriteTo.Providers(loggerProviders);
104144
}
105-
106-
configureLogger(context, loggerConfiguration);
107-
var logger = loggerConfiguration.CreateLogger();
108145

109-
ILogger registeredLogger = null;
110-
if (preserveStaticLogger)
146+
collection.AddSingleton(services =>
111147
{
112-
registeredLogger = logger;
113-
}
114-
else
115-
{
116-
// Passing a `null` logger to `SerilogLoggerFactory` results in disposal via
117-
// `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op.
118-
Log.Logger = logger;
119-
}
148+
var loggerConfiguration = new LoggerConfiguration();
149+
150+
if (loggerProviders != null)
151+
loggerConfiguration.WriteTo.Providers(loggerProviders);
152+
153+
configureLogger(context, services, loggerConfiguration);
154+
var logger = loggerConfiguration.CreateLogger();
155+
156+
return new RegisteredLogger(logger);
157+
});
120158

159+
collection.AddSingleton(services =>
160+
{
161+
// How can we register the logger, here, but not have MEDI dispose it?
162+
// Using the `NullEnricher` hack to prevent disposal.
163+
var logger = services.GetRequiredService<RegisteredLogger>().Logger;
164+
return logger.ForContext(new NullEnricher());
165+
});
166+
121167
collection.AddSingleton<ILoggerFactory>(services =>
122168
{
169+
var logger = services.GetRequiredService<RegisteredLogger>().Logger;
170+
171+
ILogger registeredLogger = null;
172+
if (preserveStaticLogger)
173+
{
174+
registeredLogger = logger;
175+
}
176+
else
177+
{
178+
// Passing a `null` logger to `SerilogLoggerFactory` results in disposal via
179+
// `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op.
180+
Log.Logger = logger;
181+
}
182+
123183
var factory = new SerilogLoggerFactory(registeredLogger, true, loggerProviders);
124184

125185
if (writeToProviders)
@@ -130,9 +190,11 @@ public static IHostBuilder UseSerilog(
130190

131191
return factory;
132192
});
133-
134-
ConfigureServices(collection, logger);
193+
194+
// Null is passed here because we've already (lazily) registered `ILogger`
195+
ConfigureServices(collection, null);
135196
});
197+
136198
return builder;
137199
}
138200

0 commit comments

Comments
 (0)