Skip to content

3.1.0 Release #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ dlldata.c
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json

*_i.c
*_p.c
Expand Down
3 changes: 1 addition & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
version: '{build}'
skip_tags: true
image: Visual Studio 2017
configuration: Release
image: Visual Studio 2019
test: off
build_script:
- ps: ./Build.ps1
Expand Down
4 changes: 3 additions & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"sdk": {
"version": "2.2.105"
"allowPrerelease": false,
"version": "3.1.100",
"rollForward": "latestFeature"
}
}
31 changes: 7 additions & 24 deletions samples/SimpleServiceSample/PrintTimeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,22 @@

namespace SimpleServiceSample
{
public class PrintTimeService : IHostedService, IDisposable
public class PrintTimeService : BackgroundService
{
private readonly ILogger _logger;
private Timer _timer;

public PrintTimeService(ILogger<PrintTimeService> logger)
{
_logger = logger;
}

public Task StartAsync(CancellationToken cancellationToken)
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));

return Task.CompletedTask;
}

private void DoWork(object state)
{
_logger.LogInformation($"The current time is: {DateTimeOffset.UtcNow}");
}

public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);

return Task.CompletedTask;
}

public void Dispose()
{
_timer?.Dispose();
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("The current time is: {CurrentTime}", DateTimeOffset.UtcNow);
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
}
}
14 changes: 5 additions & 9 deletions samples/SimpleServiceSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ public static int Main(string[] args)
try
{
Log.Information("Getting the motors running...");

BuildHost(args).Run();

CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
Expand All @@ -46,11 +44,9 @@ public static int Main(string[] args)
}
}

public static IHost BuildHost(string[] args) =>
new HostBuilder()
.ConfigureHostConfiguration(BuildConfiguration)
.ConfigureServices(services => services.AddSingleton<IHostedService, PrintTimeService>())
.UseSerilog()
.Build();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices(services => services.AddHostedService<PrintTimeService>())
.UseSerilog();
}
}
10 changes: 10 additions & 0 deletions samples/SimpleServiceSample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"profiles": {
"SimpleServiceSample": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
17 changes: 5 additions & 12 deletions samples/SimpleServiceSample/SimpleServiceSample.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Content Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
<Content Include="appsettings.Development.json" CopyToOutputDirectory="PreserveNewest" DependentUpon="appsettings.json" />
</ItemGroup>


<ItemGroup>
<ProjectReference Include="..\..\src\Serilog.Extensions.Hosting\Serilog.Extensions.Hosting.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions serilog-extensions-hosting.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=appsettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=benaadams/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=destructure/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enricher/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MEDI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
28 changes: 28 additions & 0 deletions src/Serilog.Extensions.Hosting/Extensions/Hosting/NullEnricher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2020 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Serilog.Core;
using Serilog.Events;

namespace Serilog.Extensions.Hosting
{
// Does nothing, but makes it easy to create an `ILogger` from a Serilog `Logger`
// that will not dispose the underlying pipeline when disposed itself.
class NullEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Description>Serilog support for .NET Core logging in hosted services</Description>
<VersionPrefix>3.0.0</VersionPrefix>
<VersionPrefix>3.1.0</VersionPrefix>
<Authors>Microsoft;Serilog Contributors</Authors>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand Down
112 changes: 87 additions & 25 deletions src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2019 Serilog Contributors
// Copyright 2020 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -13,9 +13,9 @@
// limitations under the License.

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Serilog.Extensions.Hosting;
using Serilog.Extensions.Logging;

Expand All @@ -26,16 +26,30 @@ namespace Serilog
/// </summary>
public static class SerilogHostBuilderExtensions
{
// Used internally to pass information through the container. We need to do this because if `logger` is the
// root logger, registering it as a singleton may lead to disposal along with the container by MEDI. This isn't
// always desirable, i.e. we may be handed a logger and `dispose: false`, so wrapping it keeps us in control
// of when the logger is disposed.
class RegisteredLogger
{
public RegisteredLogger(ILogger logger)
{
Logger = logger;
}

public ILogger Logger { get; }
}

/// <summary>
/// Sets Serilog as the logging provider.
/// </summary>
/// <param name="builder">The host builder to configure.</param>
/// <param name="logger">The Serilog logger; if not supplied, the static <see cref="Serilog.Log"/> will be used.</param>
/// <param name="dispose">When <c>true</c>, dispose <paramref name="logger"/> when the framework disposes the provider. If the
/// logger is not specified but <paramref name="dispose"/> is <c>true</c>, the <see cref="Log.CloseAndFlush()"/> method will be
/// called on the static <see cref="Log"/> class instead.</param>
/// logger is not specified but <paramref name="dispose"/> is <c>true</c>, the <see cref="Serilog.Log.CloseAndFlush()"/> method will be
/// called on the static <see cref="Serilog.Log"/> class instead.</param>
/// <param name="providers">A <see cref="LoggerProviderCollection"/> registered in the Serilog pipeline using the
/// <c>WriteTo.Providers()</c> configuration method, enabling other <see cref="ILoggerProvider"/>s to receive events. By
/// <c>WriteTo.Providers()</c> configuration method, enabling other <see cref="Microsoft.Extensions.Logging.ILoggerProvider"/>s to receive events. By
/// default, only Serilog sinks will receive events.</param>
/// <returns>The host builder.</returns>
public static IHostBuilder UseSerilog(
Expand Down Expand Up @@ -71,14 +85,15 @@ public static IHostBuilder UseSerilog(
return builder;
}


/// <summary>Sets Serilog as the logging provider.</summary>
/// <remarks>
/// A <see cref="HostBuilderContext"/> is supplied so that configuration and hosting information can be used.
/// The logger will be shut down when application services are disposed.
/// </remarks>
/// <param name="builder">The host builder to configure.</param>
/// <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>
/// <param name="preserveStaticLogger">Indicates whether to preserve the value of <see cref="Log.Logger"/>.</param>
/// <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>
/// <param name="preserveStaticLogger">Indicates whether to preserve the value of <see cref="Serilog.Log.Logger"/>.</param>
/// <param name="writeToProviders">By default, Serilog does not write events to <see cref="ILoggerProvider"/>s registered through
/// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify
/// <c>true</c> to write events to all providers.</param>
Expand All @@ -91,35 +106,80 @@ public static IHostBuilder UseSerilog(
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));
return UseSerilog(
builder,
(hostBuilderContext, services, loggerConfiguration) =>
configureLogger(hostBuilderContext, loggerConfiguration),
preserveStaticLogger: preserveStaticLogger,
writeToProviders: writeToProviders);
}

/// <summary>Sets Serilog as the logging provider.</summary>
/// <remarks>
/// A <see cref="HostBuilderContext"/> is supplied so that configuration and hosting information can be used.
/// The logger will be shut down when application services are disposed.
/// </remarks>
/// <param name="builder">The host builder to configure.</param>
/// <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>
/// <param name="preserveStaticLogger">Indicates whether to preserve the value of <see cref="Serilog.Log.Logger"/>.</param>
/// <param name="writeToProviders">By default, Serilog does not write events to <see cref="ILoggerProvider"/>s registered through
/// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify
/// <c>true</c> to write events to all providers.</param>
/// <returns>The host builder.</returns>
public static IHostBuilder UseSerilog(
this IHostBuilder builder,
Action<HostBuilderContext, IServiceProvider, LoggerConfiguration> configureLogger,
bool preserveStaticLogger = false,
bool writeToProviders = false)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));

builder.ConfigureServices((context, collection) =>
{
var loggerConfiguration = new LoggerConfiguration();

LoggerProviderCollection loggerProviders = null;
if (writeToProviders)
{
loggerProviders = new LoggerProviderCollection();
loggerConfiguration.WriteTo.Providers(loggerProviders);
}

configureLogger(context, loggerConfiguration);
var logger = loggerConfiguration.CreateLogger();

ILogger registeredLogger = null;
if (preserveStaticLogger)
collection.AddSingleton(services =>
{
registeredLogger = logger;
}
else
{
// Passing a `null` logger to `SerilogLoggerFactory` results in disposal via
// `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op.
Log.Logger = logger;
}
var loggerConfiguration = new LoggerConfiguration();

if (loggerProviders != null)
loggerConfiguration.WriteTo.Providers(loggerProviders);

configureLogger(context, services, loggerConfiguration);
var logger = loggerConfiguration.CreateLogger();

return new RegisteredLogger(logger);
});

collection.AddSingleton(services =>
{
// How can we register the logger, here, but not have MEDI dispose it?
// Using the `NullEnricher` hack to prevent disposal.
var logger = services.GetRequiredService<RegisteredLogger>().Logger;
return logger.ForContext(new NullEnricher());
});

collection.AddSingleton<ILoggerFactory>(services =>
{
var logger = services.GetRequiredService<RegisteredLogger>().Logger;

ILogger registeredLogger = null;
if (preserveStaticLogger)
{
registeredLogger = logger;
}
else
{
// Passing a `null` logger to `SerilogLoggerFactory` results in disposal via
// `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op.
Log.Logger = logger;
}

var factory = new SerilogLoggerFactory(registeredLogger, true, loggerProviders);

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

return factory;
});

ConfigureServices(collection, logger);

// Null is passed here because we've already (lazily) registered `ILogger`
ConfigureServices(collection, null);
});

return builder;
}

Expand Down