Skip to content

Commit

Permalink
Merge pull request #5 from cnblogs/add-more-extensions
Browse files Browse the repository at this point in the history
feat: add more extensions for ILoggingBuilder
  • Loading branch information
cnblogs-dudu authored Feb 12, 2023
2 parents 4d786ff + c59e75e commit 149b53f
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 9 deletions.
182 changes: 180 additions & 2 deletions src/Serilog.Extensions.Hosting/SerilogLoggingBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Extensions.Hosting;
using Serilog.Extensions.Logging;
using System;
using static Serilog.SerilogHostBuilderExtensions;
Expand All @@ -21,7 +24,7 @@ public static class SerilogLoggingBuilderExtensions
/// <param name="providers">A <see cref="LoggerProviderCollection"/> registered in the Serilog pipeline using the
/// <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>
/// <returns>The logging builder.</returns>
public static ILoggingBuilder AddSerilog(
this ILoggingBuilder builder,
Serilog.ILogger logger = null,
Expand Down Expand Up @@ -61,5 +64,180 @@ public static ILoggingBuilder AddSerilog(

return builder;
}

/// <summary>Sets Serilog as the logging provider.</summary>
/// <remarks>
/// A <see cref="ILoggingBuilder"/> 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 logging 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>
/// <remarks>If the static <see cref="Log.Logger"/> is a bootstrap logger (see
/// <c>LoggerConfigurationExtensions.CreateBootstrapLogger()</c>), and <paramref name="preserveStaticLogger"/> is
/// not specified, the the bootstrap logger will be reconfigured through the supplied delegate, rather than being
/// replaced entirely or ignored.</remarks>
/// <returns>The logging builder.</returns>
public static ILoggingBuilder AddSerilogFactory(
this ILoggingBuilder builder,
Action<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));

// This check is eager; replacing the bootstrap logger after calling this method is not supported.
#if !NO_RELOADABLE_LOGGER
var reloadable = Log.Logger as ReloadableLogger;
var useReload = reloadable != null && !preserveStaticLogger;
#else
const bool useReload = false;
#endif
var services = builder.Services;

LoggerProviderCollection loggerProviders = null;
if (writeToProviders)
{
loggerProviders = new LoggerProviderCollection();
}

services.AddSingleton(sp =>
{
Serilog.ILogger logger;
#if !NO_RELOADABLE_LOGGER
if (useReload)
{
reloadable!.Reload(cfg =>
{
if (loggerProviders != null)
cfg.WriteTo.Providers(loggerProviders);

configureLogger(sp, cfg);
return cfg;
});

logger = reloadable.Freeze();
}
else
#endif
{
var loggerConfiguration = new LoggerConfiguration();

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

configureLogger(sp, loggerConfiguration);
logger = loggerConfiguration.CreateLogger();
}

return new RegisteredLogger(logger);
});

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

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

Serilog.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, !useReload, loggerProviders);

if (writeToProviders)
{
foreach (var provider in sp.GetServices<ILoggerProvider>())
factory.AddProvider(provider);
}

return factory;
});

ConfigureDiagnosticContext(services, preserveStaticLogger);

return builder;
}

/// <summary>Sets Serilog as the logging provider.</summary>
/// <remarks>
/// A <see cref="ILoggingBuilder"/> 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 logging 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 logging builder.</returns>
public static ILoggingBuilder AddSerilog(
this ILoggingBuilder builder,
Action<IConfiguration, LoggerConfiguration> configureLogger,
bool preserveStaticLogger = false,
bool writeToProviders = false)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));

return AddSerilogFactory(
builder,
(sp, loggerConfiguration) =>
{
var configuration = sp.GetRequiredService<IConfiguration>();
configureLogger(configuration, loggerConfiguration);
},
preserveStaticLogger: preserveStaticLogger,
writeToProviders: writeToProviders);
}

/// <summary>Sets Serilog as the logging provider.</summary>
/// <remarks>
/// A <see cref="ILoggingBuilder"/> 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 logging 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 logging builder.</returns>
public static ILoggingBuilder AddSerilog(
this ILoggingBuilder builder,
Action<LoggerConfiguration> configureLogger,
bool preserveStaticLogger = false,
bool writeToProviders = false)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger));

return AddSerilogFactory(
builder,
(sp, loggerConfiguration) =>
{
configureLogger(loggerConfiguration);
},
preserveStaticLogger: preserveStaticLogger,
writeToProviders: writeToProviders);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@
<PropertyGroup Condition=" '$(TargetFramework)' == 'net4.8' ">
<DefineConstants>$(DefineConstants);NO_RELOADABLE_LOGGER</DefineConstants>
</PropertyGroup>

<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>

<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Serilog.Extensions.Hosting\Serilog.Extensions.Hosting.csproj" />
Expand All @@ -29,6 +41,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Serilog.Sinks.InMemory" Version="0.11.0" />
<PackageReference Include="Serilog.Sinks.InMemory.Assertions" Version="0.11.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,35 @@ namespace Serilog.Extensions.Hosting.Tests;
public class SerilogLoggingBuilderExtensionsTests
{
[Fact]
public async Task SerilogLoggingBuilderExtensions_AddSerilog_SuccessAsync()
public async Task LoggingBuilderExtensions_AddSerilog_SuccessAsync()
{
// Arrange
var builder = WebApplication.CreateBuilder();
var logger = new LoggerConfiguration()
.WriteTo.InMemory()
.CreateLogger();
builder.Logging.AddSerilog(logger);
builder.Logging.AddSerilog(logger => logger.WriteTo.InMemory());
builder.WebHost.UseTestServer();
var app = builder.Build();

// Act
var message = "Hello World!";
app.Logger.LogInformation("Hello World!");
var message = "Logging in memory";
app.Logger.LogInformation(message);
await app.StartAsync();

// Assert
InMemorySink.Instance.Should().HaveMessage(message);
}

[Fact]
public async Task LoggingBuilderExtensions_AddSerilogWithAppsettings_SuccessAsync()
{
// Arrange
var builder = WebApplication.CreateBuilder();
builder.Logging.AddSerilog((appConfig, loggerConfig) => loggerConfig.ReadFrom.Configuration(appConfig));
builder.WebHost.UseTestServer();
var app = builder.Build();

// Act
var message = "Logging in memory with appsettings.json";
app.Logger.LogInformation(message);
await app.StartAsync();

// Assert
Expand Down
16 changes: 16 additions & 0 deletions test/Serilog.Extensions.Hosting.Tests/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"WriteTo": [
{
"Name": "InMemory"
}
]
}
}

0 comments on commit 149b53f

Please sign in to comment.