Skip to content

Commit

Permalink
Merge pull request #23 from serilog/dev
Browse files Browse the repository at this point in the history
3.1.0 Release
  • Loading branch information
nblumhardt authored May 27, 2020
2 parents a9eb2e1 + 7ba1560 commit 806947a
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 75 deletions.
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

0 comments on commit 806947a

Please sign in to comment.