使用 Serilog 增强 ASP.NET Core 日志管道





5.00/5 (2投票s)
如何使用 Serilog 增强 ASP.NET Core 日志管道
引言
在这篇文章中,我想展示一下如何将默认的日志管道替换为 Serilog,它由社区实现了更多提供程序,并且还提供了一种记录结构化数据的方法。
背景故事
对于那些在 ASP.NET Core 1.1 或更早版本中创建过项目的人来说,您可能还记得 Program.cs 文件看起来像这样
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup()
.UseApplicationInsights()
.Build();
host.Run();
}
}
}
正如您所看到的,在 ASP.NET Core 的早期版本中,应用程序入口点的设置方式更加明确。现在,从 ASP.NET Core 2.0 及更高版本开始,默认的 Program.cs 文件看起来像这样
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
虽然默认构建器可以很好地清理代码,但它确实添加了一些默认(顾名思义)配置,这些配置并不那么明显。
如果我们看一下 WebHost.CreateDefaultBuilder
实际做了什么,我们会看到以下内容
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder();
if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey)))
{
builder.UseContentRoot(Directory.GetCurrentDirectory());
}
if (args != null)
{
builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
}
builder.UseKestrel((builderContext, options) =>
{
options.Configure(builderContext.Configuration.GetSection("Kestrel"));
})
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json",
optional: true, reloadOnChange: true);
if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
// THIS IS THE PART WE'RE INTERESTED IN. (INTEREST!!!)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.ConfigureServices((hostingContext, services) =>
{
// Fallback
services.PostConfigure(options =>
{
if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
{
// "AllowedHosts": "localhost;127.0.0.1;[::1]"
var hosts = hostingContext.Configuration["AllowedHosts"]?.Split
(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
// Fall back to "*" to disable.
options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
}
});
// Change notification
services.AddSingleton<IOptionsChangeTokenSource>(
new ConfigurationChangeTokenSource(hostingContext.Configuration));
services.AddTransient();
})
.UseIISIntegration()
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
});
return builder;
}
好吧,对于一个开始来说,这确实是大量的配置,幸好它隐藏在像 CreateDefaultBuilder
这样的简单调用后面。
现在,如果我们查看上面的代码片段(我用 INTEREST!!! 标记了它,以便于查找),您会看到,默认情况下,配置会将日志记录发送到控制台和调试通道,我们不需要这个,因为我们将使用不同的控制台,并且没有必要让两个提供程序同时写入同一个控制台。
更改
因此,我们要做的第一个更改如下
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(
(webHostBuilderContext, loggingBuilder) =>
{
loggingBuilder.ClearProviders();
})
.UseStartup();
}
通过此更改,我们清除了控制台和调试提供程序,因此基本上现在我们没有设置任何日志记录。
现在我们将添加以下 Nuget 包(请注意,其中只有两个是必需的,所有其他 sinks 都取决于您自己的选择)
Serilog
(这是主包,是必需的)Serilog.Extensions.Logging
(这用于与 ASP.NET Core 管道集成,它还将安装Serilog
作为依赖项)Serilog.Sinks.ColoredConsole
(此包添加了一个彩色控制台输出,使区分日志级别和消息更容易,这也将安装Serilog
作为依赖项)Serilog.Enrichers.Demystify
(此包处于预发布状态,但它使得来自涵盖async
方法的异常的长堆栈跟踪变成对开发人员更友好的堆栈跟踪)
安装这些包后,我们将再次更改 Program.cs 文件,它最终看起来像这样
namespace WebApplication1
{
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Extensions.Logging;
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(
(webHostBuilderContext, loggingBuilder) =>
{
loggingBuilder.ClearProviders();
Serilog.Debugging.SelfLog.Enable(Console.Error); // this outputs
// internal Serilog errors to the console in case something
// breaks with one of the Serilog extensions or the framework itself
Serilog.ILogger logger = new LoggerConfiguration()
.Enrich.FromLogContext() // this adds more information
// to the output of the log, like when receiving http requests,
// it will provide information about the request
.Enrich.WithDemystifiedStackTraces() // this will change the
// stack trace of an exception into a more readable form
// if it involves async
.MinimumLevel.Verbose() // this gives the minimum level to log,
// in production the level would be higher
.WriteTo.ColoredConsole() // one of the logger pipeline elements
// for writing out the log message
.CreateLogger();
loggingBuilder.AddProvider(new SerilogLoggerProvider
(logger)); // this adds the serilog provider from the start
})
.UseStartup();
}
}
现在,我们将 Serilog
集成到 ASP.NET Core 所有组件使用的日志记录主管道中。请注意,我们还可以访问 webHostBuilderContext
,它有一个 Configuration
属性,允许我们从应用程序配置中读取,以便我们可以设置更复杂的管道,并且还有一个 nuget 包允许 Serilog
从 appsettings.json 文件中读取。
可选地,Serilog
还允许日志消息携带一些附加属性,为此,我们需要将默认的 outputTemplate
从 "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message}{NewLine}{Exception}"
更改为 "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level} {Properties} {Message}{NewLine}{Exception}"
;注意 Properties
模板占位符,这是 serilog
将放置所有不在实际消息中的附加信息的地方,例如来自 http 请求的数据。要查看此更改的外观,请参见以下内容
Serilog.ILogger logger = new LoggerConfiguration()
.Enrich.FromLogContext() // this adds more information to the output of the log,
// like when receiving http requests, it will provide
// information about the request
.Enrich.WithDemystifiedStackTraces() // this will change the stack trace of an exception
// into a more readable form if it involves async
.MinimumLevel.Verbose() // this gives the minimum level to log, in production
// the level would be higher
.WriteTo.ColoredConsole(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss}
{Level} {Properties} {Message}{NewLine}{Exception}") // one of the logger pipeline
// elements for writing out the log message
.CreateLogger();
结论
请注意,设置日志记录管道的方式与应用程序一样多,这只是我的个人偏好。
另外,如果您想知道为什么我选择在 Program.cs 文件中进行更改而不是在 Startup.Configure()
方法中进行更改,正如一些示例在线显示的那样,那是因为我相信如果默认日志记录设置在它自己的专用函数中,那么这也应该设置,这也在使用 Startup
方法之前引入了 Seriloger,这反过来提供了更多信息。
我希望您喜欢这篇文章,并且它将帮助您更好地调试和维护您的应用程序。
谢谢,下次再见。
祝好!