Use async console logging with Serilog

This commit is contained in:
Jordan Brown
2020-05-07 16:12:01 -04:00
parent d2ab78c8ce
commit 335a3df061
7 changed files with 81 additions and 84 deletions
+42 -53
View File
@@ -64,7 +64,7 @@ namespace Tgstation.Server.Host.Core
public Application(
IConfiguration configuration,
IWebHostEnvironment hostingEnvironment)
: base(configuration, hostingEnvironment)
: base(configuration)
{
this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
}
@@ -97,63 +97,52 @@ namespace Tgstation.Server.Host.Core
// enable options which give us config reloading
services.AddOptions();
// setup file logging via serilog
services.AddLogging(builder =>
{
if (postSetupServices.FileLoggingConfiguration.Disable)
return;
static LogEventLevel? ConvertSeriLogLevel(LogLevel logLevel) =>
logLevel switch
{
LogLevel.Critical => LogEventLevel.Fatal,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Trace => LogEventLevel.Verbose,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.None => null,
_ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid log level {0}", logLevel)),
};
// common app data is C:/ProgramData on windows, else /usr/share
var logPath = !String.IsNullOrEmpty(postSetupServices.FileLoggingConfiguration.Directory)
? postSetupServices.FileLoggingConfiguration.Directory
: IOManager.ConcatPath(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"tgstation-server",
"Logs");
var microsoftEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.MicrosoftLogLevel);
services.SetupLogging(
config =>
{
if (microsoftEventLevel.HasValue)
config.MinimumLevel.Override("Microsoft", microsoftEventLevel.Value);
},
sinkConfig =>
{
if (postSetupServices.FileLoggingConfiguration.Disable)
return;
logPath = IOManager.ConcatPath(logPath, "tgs-{Date}.log");
// common app data is C:/ProgramData on windows, else /usr/share
var logPath = !String.IsNullOrEmpty(postSetupServices.FileLoggingConfiguration.Directory)
? postSetupServices.FileLoggingConfiguration.Directory
: IOManager.ConcatPath(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
AssemblyInformationProvider.VersionPrefix,
"Logs");
static LogEventLevel? ConvertLogLevel(LogLevel logLevel) =>
logLevel switch
{
LogLevel.Critical => LogEventLevel.Fatal,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Trace => LogEventLevel.Verbose,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.None => null,
_ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid log level {0}", logLevel)),
};
var logEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.LogLevel);
var logEventLevel = ConvertLogLevel(postSetupServices.FileLoggingConfiguration.LogLevel);
var microsoftEventLevel = ConvertLogLevel(postSetupServices.FileLoggingConfiguration.MicrosoftLogLevel);
var formatter = new MessageTemplateTextFormatter(
"{Timestamp:o} {RequestId,13} [{Level:u3}] {SourceContext:l}: {Message} ({EventId:x8}){NewLine}{Exception}",
null);
var formatter = new MessageTemplateTextFormatter(
"{Timestamp:o} {RequestId,13} [{Level:u3}] {SourceContext:l}: {Message} ({EventId:x8}){NewLine}{Exception}",
null);
var configuration = new LoggerConfiguration()
.Enrich
.FromLogContext()
.WriteTo
.Async(
w => w.RollingFile(
formatter,
logPath,
shared: true,
flushToDiskInterval: TimeSpan.FromSeconds(2)));
if (logEventLevel.HasValue)
configuration.MinimumLevel.Is(logEventLevel.Value);
if (microsoftEventLevel.HasValue)
configuration.MinimumLevel.Override("Microsoft", microsoftEventLevel.Value);
builder.AddSerilog(configuration.CreateLogger(), true);
});
services.RemoveEventLogging();
logPath = IOManager.ConcatPath(logPath, "tgs-{Date}.log");
var rollingFileConfig = sinkConfig.RollingFile(
formatter,
logPath,
logEventLevel ?? LogEventLevel.Verbose,
flushToDiskInterval: TimeSpan.FromSeconds(2));
});
// configure bearer token validation
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(jwtBearerOptions =>
@@ -21,7 +21,7 @@ namespace Tgstation.Server.Host.Extensions
return builder.ConfigureServices((context, services) =>
{
var setupApplication = new SetupApplication(context.Configuration, context.HostingEnvironment);
var setupApplication = new SetupApplication(context.Configuration);
setupApplication.ConfigureServices(services);
});
}
@@ -1,9 +1,10 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.EventLog;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Configuration;
using System;
using System.Globalization;
using System.Linq;
using Tgstation.Server.Host.Configuration;
namespace Tgstation.Server.Host.Extensions
@@ -44,24 +45,40 @@ namespace Tgstation.Server.Host.Extensions
}
/// <summary>
/// Removes the <see cref="EventLogLoggerProvider"/> from a given <paramref name="serviceCollection"/>.
/// Clear previous providers and configure logging.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to remove the <see cref="EventLogLoggerProvider"/> from.</param>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to configure.</param>
/// <param name="configurationAction">Additional configuration for a given <see cref="LoggerConfiguration"/>.</param>
/// <param name="sinkConfigurationAction">Additional configuration for a given <see cref="LoggerSinkConfiguration"/>.</param>
/// <returns>The updated <paramref name="serviceCollection"/>.</returns>
public static IServiceCollection RemoveEventLogging(this IServiceCollection serviceCollection)
{
if (serviceCollection == null)
throw new ArgumentNullException(nameof(serviceCollection));
public static IServiceCollection SetupLogging(
this IServiceCollection serviceCollection,
Action<LoggerConfiguration> configurationAction,
Action<LoggerSinkConfiguration> sinkConfigurationAction = null)
=> serviceCollection.AddLogging(builder =>
{
builder.ClearProviders();
// IMPORTANT: Remove the event log provider, it's shitty and causes issues
var eventLogDescriptor =
serviceCollection.FirstOrDefault(
descriptor => descriptor.ImplementationType == typeof(EventLogLoggerProvider));
var configuration = new LoggerConfiguration()
.MinimumLevel
.Verbose();
if (eventLogDescriptor != default)
serviceCollection.Remove(eventLogDescriptor);
configurationAction?.Invoke(configuration);
return serviceCollection;
}
configuration
.WriteTo
.Async(sinkConfiguration =>
{
sinkConfiguration.Console(
outputTemplate: "[{Timestamp:HH:mm:ss}] {Level:w3}: {SourceContext:l}{NewLine} {Message:lj}{NewLine}{Exception}");
sinkConfigurationAction?.Invoke(sinkConfiguration);
});
builder.AddSerilog(configuration.CreateLogger(), true);
#if DEBUG
builder.AddDebug();
#endif
});
}
}
@@ -9,7 +9,6 @@
},
"Docker": {
"commandName": "Docker",
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true
}
}
+1 -2
View File
@@ -45,8 +45,7 @@ namespace Tgstation.Server.Host
IHostBuilder CreateDefaultBuilder() => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, configuration) => configuration
.SetBasePath(
IOManager.ResolvePath()))
.ConfigureServices(services => services.RemoveEventLogging());
IOManager.ResolvePath()));
var setupWizardHostBuilder = CreateDefaultBuilder()
.UseSetupApplication();
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog.Events;
using System;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Core;
@@ -31,22 +32,13 @@ namespace Tgstation.Server.Host.Setup
/// </summary>
protected IConfiguration Configuration { get; }
/// <summary>
/// The <see cref="IHostEnvironment"/> for the <see cref="SetupApplication"/>
/// </summary>
readonly IHostEnvironment hostingEnvironment;
/// <summary>
/// Initializes a new instance of the <see cref="SetupApplication"/> <see langword="class"/>.
/// </summary>
/// <param name="configuration">The value of <see cref="Configuration"/>.</param>
/// <param name="hostingEnvironment">The value of <see cref="hostingEnvironment"/>.</param>
public SetupApplication(
IConfiguration configuration,
IHostEnvironment hostingEnvironment)
public SetupApplication(IConfiguration configuration)
{
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
}
/// <summary>
@@ -58,7 +50,7 @@ namespace Tgstation.Server.Host.Setup
if (services == null)
throw new ArgumentNullException(nameof(services));
services.RemoveEventLogging();
services.SetupLogging(config => config.MinimumLevel.Override("Microsoft", LogEventLevel.Warning));
services.AddSingleton(IOManager);
services.AddSingleton(AssemblyInformationProvider);
@@ -77,6 +77,7 @@
<!-- If this is updated, be sure to update the reference in the README.md -->
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>