serilog 格式 SourceContext 仅显示程序集名称
serilog format SourceContext for showing only assembly name
我将我的项目配置为使用 Serilog 通过依赖注入进行日志记录。
我在 类 构造函数中使用以下模式:
namespace FlickPopper.API.Controllers {
public class ValuesController : Controller {
private ILogger<ValuesController> _logger;
public MyClass(ILogger<ValuesController> logger) {
_logger = logger;
}
}
}
通过这种方式,serilog 创建了调用 Log.ForContext<classname>
的记录器
我的设置是:
"Serilog": {
"WriteTo": [
{
"Name": "RollingFile",
"Args": {
"pathFormat": "Logs\log-{Date}.txt",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext" ]
}
所以,日志看起来是这样的:
2018-01-26 22:20:08 [INF] [FlickPopper.API.Controllers.ValuesController] Get all values
有什么方法可以格式化 SourceContext 属性 以仅在日志中显示程序集名称,类似这样的东西?
2018-01-26 22:20:08 [INF] [FlickPopper.API] Get all values
当您使用注入的 ILogger<T>
时,Serilog 记录器是由实现 ILoggerProvider
接口的 SerilogLoggerProvider 创建的。该接口只有一个方法:
public interface ILoggerProvider : IDisposable
{
ILogger CreateLogger(string categoryName);
}
传递的 categoryName
用作消息格式中 {SourceContext}
属性 的值。 ASP.NET 核心将其作为完全限定名称传递(例如 FlickPopper.API.Controllers.ValuesController
)。
所以这个字符串值不应在 Serilog 代码或配置中固定,而应在 ASP.NET 核心日志基础结构中固定。
首先负责创造该价值的 class 是 Logger<T>
class。 Logger<T>
的实例在您将 ILogger<T>
注入 class 时实例化。这是其构造函数的source code:
public class Logger<T> : ILogger<T>
{
public Logger(ILoggerFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
_logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T)));
}
// ...
}
调用 TypeNameHelper.GetTypeDisplayName(typeof(T))
returns 完全限定名称,然后传递给 ILoggerFactory
,最终传递给 SerilogLoggerProvider
。
因此,如果您想更改该行为并调整传递给 ILoggerFactory
的 categoryName
,您应该有自己的 ILogger<T>
实现,它需要调用 ILoggerFactory.CreateLogger()
.没那么难,因为 Logger<T>
class 很瘦,是基于 Microsoft.Extensions.Logging.Logger
实现的。这是 class,它是 Logger<T>
的副本,除了一行生成记录器类别名称:
public class LoggerEx<T> : ILogger<T>
{
private readonly ILogger _logger;
public LoggerEx(ILoggerFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
_logger = factory.CreateLogger(typeof(T).Assembly.GetName().Name);
}
IDisposable ILogger.BeginScope<TState>(TState state)
{
return _logger.BeginScope(state);
}
bool ILogger.IsEnabled(LogLevel logLevel)
{
return _logger.IsEnabled(logLevel);
}
void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_logger.Log(logLevel, eventId, state, exception, formatter);
}
}
您还应该在您的服务注册中用这个替换 ILogger<T>
的标准实现:
services.AddSingleton(typeof(ILogger<>), typeof(LoggerEx<>));
现在 LoggerEx<T>
的实例将被注入控制器并且 {SourceContext}
将具有您构建的值:
2018-01-27 09:54:21 [INF] [TestProject.TestApplication] Hello!
我将我的项目配置为使用 Serilog 通过依赖注入进行日志记录。 我在 类 构造函数中使用以下模式:
namespace FlickPopper.API.Controllers {
public class ValuesController : Controller {
private ILogger<ValuesController> _logger;
public MyClass(ILogger<ValuesController> logger) {
_logger = logger;
}
}
}
通过这种方式,serilog 创建了调用 Log.ForContext<classname>
我的设置是:
"Serilog": {
"WriteTo": [
{
"Name": "RollingFile",
"Args": {
"pathFormat": "Logs\log-{Date}.txt",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext" ]
}
所以,日志看起来是这样的:
2018-01-26 22:20:08 [INF] [FlickPopper.API.Controllers.ValuesController] Get all values
有什么方法可以格式化 SourceContext 属性 以仅在日志中显示程序集名称,类似这样的东西?
2018-01-26 22:20:08 [INF] [FlickPopper.API] Get all values
当您使用注入的 ILogger<T>
时,Serilog 记录器是由实现 ILoggerProvider
接口的 SerilogLoggerProvider 创建的。该接口只有一个方法:
public interface ILoggerProvider : IDisposable
{
ILogger CreateLogger(string categoryName);
}
传递的 categoryName
用作消息格式中 {SourceContext}
属性 的值。 ASP.NET 核心将其作为完全限定名称传递(例如 FlickPopper.API.Controllers.ValuesController
)。
所以这个字符串值不应在 Serilog 代码或配置中固定,而应在 ASP.NET 核心日志基础结构中固定。
首先负责创造该价值的 class 是 Logger<T>
class。 Logger<T>
的实例在您将 ILogger<T>
注入 class 时实例化。这是其构造函数的source code:
public class Logger<T> : ILogger<T>
{
public Logger(ILoggerFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
_logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T)));
}
// ...
}
调用 TypeNameHelper.GetTypeDisplayName(typeof(T))
returns 完全限定名称,然后传递给 ILoggerFactory
,最终传递给 SerilogLoggerProvider
。
因此,如果您想更改该行为并调整传递给 ILoggerFactory
的 categoryName
,您应该有自己的 ILogger<T>
实现,它需要调用 ILoggerFactory.CreateLogger()
.没那么难,因为 Logger<T>
class 很瘦,是基于 Microsoft.Extensions.Logging.Logger
实现的。这是 class,它是 Logger<T>
的副本,除了一行生成记录器类别名称:
public class LoggerEx<T> : ILogger<T>
{
private readonly ILogger _logger;
public LoggerEx(ILoggerFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
_logger = factory.CreateLogger(typeof(T).Assembly.GetName().Name);
}
IDisposable ILogger.BeginScope<TState>(TState state)
{
return _logger.BeginScope(state);
}
bool ILogger.IsEnabled(LogLevel logLevel)
{
return _logger.IsEnabled(logLevel);
}
void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_logger.Log(logLevel, eventId, state, exception, formatter);
}
}
您还应该在您的服务注册中用这个替换 ILogger<T>
的标准实现:
services.AddSingleton(typeof(ILogger<>), typeof(LoggerEx<>));
现在 LoggerEx<T>
的实例将被注入控制器并且 {SourceContext}
将具有您构建的值:
2018-01-27 09:54:21 [INF] [TestProject.TestApplication] Hello!