带有 Serilog 的 Mastransit
Mastransit with Serilog
地铁版本:7.2.2 | Serilog版本:2.10.0 | Serilog.Asp.net核心版本4.1.0 |
Asp.net核心5.0。
在配置总线之前,我在我的应用程序中设置了以下配置。
LogContext.ConfigureCurrentLogContext(loggerFactory);
虽然,根据这个document我不需要设置这个配置。
If you are using the new .AddMassTransit() configuration, combined with .AddBus(), then ILoggerFactory is automatically configured for you. In this case, the statement above is not required.
现在,
我的 Serilog 配置:
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} - {ApplicationName} - {SourceContext} - {Level:u3} - {CorrelationId} - {UserId} - {ClientVersion} => {Message:lj}{NewLine}{Exception}",
在我的 Http 服务中使用此模板,我看到了日志,例如:
2021-12-29 12:35:36.096 - xxxx- Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - INF - fcf97692-7af4-492c-9d1b-cf7456d1d371(corrId)- 11111(userId)- 1.2.1(ClientVer) , etc
但是当登录消费者时,没有记录我的 Serilog 属性(CorrelationId、UserId、ClientVersion 等)。
2021-12-29 12:35:36.096 - xxxx- Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - INF - - - , etc
我需要其他配置来解决这个问题吗?
当向消费者抛出异常时,我还想推送我的 Serilog 属性。此时它不是日志。如您所见:
2022-01-01 13:07:24.736 - xxx - MassTransit.ReceiveTransport - ERR - - - => R-FAULT "rabbitmq://localhost/xxx" "c8100000-568d-0050-fe74-08d9cd0a4d36" xxxx
样本:
我在 MassTransit 发布过滤器中推送了我的属性:
public class IntegrationEventPublishFilter<T> : IFilter<PublishContext<T>> where T : class
{
private readonly ICurrentRequest _currentRequest;
private readonly ICurrentUser _currentUser;
private readonly ILogger _logger;
public IntegrationEventPublishFilter(ICurrentRequest currentRequest, ILogger<IntegrationEventPublishFilter<T>> logger,
ICurrentUser currentUser)
{
_currentRequest = currentRequest;
_logger = logger;
_currentUser = currentUser;
}
public Task Send(PublishContext<T> context, IPipe<PublishContext<T>> next)
{
LogContext.PushProperty("UserId", _currentUser.UserId);
LogContext.PushProperty("ClientVersion", _currentRequest.ClientVersion);
LogContext.PushProperty("CorrelationId", _currentRequest.ReuqestId);
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
Serilog 设置 Json 文件:
{ "Serilog": {
"Using": [
"Serilog.Sinks.Async"
],
"MinimumLevel": {
"Default": "Information",
},
"Properties": {
"ApplicationName": "xxx
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "..\..\Logs\log.log",
"rollingInterval": "Hour",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} - {ApplicationName} - {SourceContext} - {Level:u3} - {CorrelationId} - {UserId} - {ClientVersion} => {Message:lj}{NewLine}{Exception}",
}
}
]
}
},
{
"Name": "Debug"
}
] }}
程序文件:
public static IHostBuilder CreateHostBuilder(string[] args)
{
IConfigurationRoot configurationRoot = null;
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((HostBuilderContext context, IConfigurationBuilder builder) =>
{
var environment = context.HostingEnvironment;
var environmentName = environment.IsProduction() ? "" : $".{environment.EnvironmentName}";
builder.Sources.Clear();
builder.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile($"appsettings{environmentName}.json")
.AddJsonFile($"serilogsettings{environmentName}.json")
configurationRoot = builder.Build();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseSerilog((hostingContext, loggerConfiguration) =>
{
loggerConfiguration
.Enrich.FromLogContext()
.Enrich.With<BaseExceptionItemsEnricher>()
.ReadFrom.Configuration(configurationRoot, "Serilog")
});
});
}
Serilog 丰富过滤器(在启动时注册并在 http 请求中正确工作。):
public class LogEnrichmentFilter : IActionFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public LogEnrichmentFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void OnActionExecuting(ActionExecutingContext context)
{
var httpContext = _httpContextAccessor.HttpContext;
httpContext.Request.Headers.TryGetValue("Request-Id", out StringValues requestIds);
var userId = httpContext.User.Claims.FirstOrDefault(x => x.Type == "UserId")?.Value;
httpContext.Request.Headers.TryGetValue("X-Client-Version", out StringValues clientVersions);
LogContext.PushProperty("UserId", userId);
LogContext.PushProperty("CorrelationId", requestIds.FirstOrDefault());
LogContext.PushProperty("ClientVersion", clientVersions.FirstOrDefault());
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
}
我的消费者:
public class MyConsumer :
IConsumer<myEvent>
{
private readonly ILogger _logger;
public MyConsumer (
ILogger<MyConsumer> logger)
{
_logger = logger;
}
public async Task Consume(ConsumeContext<MyEvent> context)
{
_logger.LogInformation("LOGGING TEST"); } } }
记录结果:
2021-12-29 12:35:38.183 - xxx - xxx.MyConsumer - INF - - - => LOGGING TEST
如您所见,不要记录我推送的属性(correlationId、userId 等)。
最后,我解决了这个问题。
我添加了两个过滤器:PublishContext 过滤器和 ConsumeContext 过滤器。
在 PublishContext 过滤器中,我将 Serilog 属性设置为 MT headers:
public class IntegrationEventPublishFilter<T> : IFilter<PublishContext<T>> where T : class
{
private readonly ICurrentRequest _currentRequest;
private readonly ICurrentUser _currentUser;
public IntegrationEventPublishFilter(ICurrentRequest currentRequest, ICurrentUser currentUser)
{
_currentRequest = currentRequest;
_currentUser = currentUser;
}
public Task Send(PublishContext<T> context, IPipe<PublishContext<T>> next)
{
context.Headers.Set("Language", _currentRequest.Language);
context.Headers.Set("UId", _currentUser.UserId);
context.Headers.Set("CorrelationId", _currentRequest.ReuqestId);
context.Headers.Set("ClientVersion", _currentRequest.ClientVersion);
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
并且,在 ConsumeContext 过滤器中,我得到了 MT headers 并推送到 Serilog LogContext:
public class IntegrationEventConsumeFilter<T> : IFilter<ConsumeContext<T>> where T : class
{
public Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
{
var acceptLanguage = context.Headers.Get<string>("Language");
CultureInfo.CurrentUICulture = string.IsNullOrWhiteSpace(acceptLanguage) ?
new CultureInfo("fa-IR") : new CultureInfo(acceptLanguage);
context.Headers.TryGetHeader("UId", out object userId);
context.Headers.TryGetHeader("ClientVersion", out object clientVersion);
context.Headers.TryGetHeader("CorrelationId", out object correlationId);
LogContext.PushProperty("UId", userId);
LogContext.PushProperty("ClientVersion", clientVersion);
LogContext.PushProperty("CorrelationId", correlationId);
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
}
地铁版本:7.2.2 | Serilog版本:2.10.0 | Serilog.Asp.net核心版本4.1.0 |
Asp.net核心5.0。
在配置总线之前,我在我的应用程序中设置了以下配置。
LogContext.ConfigureCurrentLogContext(loggerFactory);
虽然,根据这个document我不需要设置这个配置。
If you are using the new .AddMassTransit() configuration, combined with .AddBus(), then ILoggerFactory is automatically configured for you. In this case, the statement above is not required.
现在, 我的 Serilog 配置:
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} - {ApplicationName} - {SourceContext} - {Level:u3} - {CorrelationId} - {UserId} - {ClientVersion} => {Message:lj}{NewLine}{Exception}",
在我的 Http 服务中使用此模板,我看到了日志,例如:
2021-12-29 12:35:36.096 - xxxx- Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - INF - fcf97692-7af4-492c-9d1b-cf7456d1d371(corrId)- 11111(userId)- 1.2.1(ClientVer) , etc
但是当登录消费者时,没有记录我的 Serilog 属性(CorrelationId、UserId、ClientVersion 等)。
2021-12-29 12:35:36.096 - xxxx- Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - INF - - - , etc
我需要其他配置来解决这个问题吗?
当向消费者抛出异常时,我还想推送我的 Serilog 属性。此时它不是日志。如您所见:
2022-01-01 13:07:24.736 - xxx - MassTransit.ReceiveTransport - ERR - - - => R-FAULT "rabbitmq://localhost/xxx" "c8100000-568d-0050-fe74-08d9cd0a4d36" xxxx
样本:
我在 MassTransit 发布过滤器中推送了我的属性:
public class IntegrationEventPublishFilter<T> : IFilter<PublishContext<T>> where T : class
{
private readonly ICurrentRequest _currentRequest;
private readonly ICurrentUser _currentUser;
private readonly ILogger _logger;
public IntegrationEventPublishFilter(ICurrentRequest currentRequest, ILogger<IntegrationEventPublishFilter<T>> logger,
ICurrentUser currentUser)
{
_currentRequest = currentRequest;
_logger = logger;
_currentUser = currentUser;
}
public Task Send(PublishContext<T> context, IPipe<PublishContext<T>> next)
{
LogContext.PushProperty("UserId", _currentUser.UserId);
LogContext.PushProperty("ClientVersion", _currentRequest.ClientVersion);
LogContext.PushProperty("CorrelationId", _currentRequest.ReuqestId);
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
Serilog 设置 Json 文件:
{ "Serilog": {
"Using": [
"Serilog.Sinks.Async"
],
"MinimumLevel": {
"Default": "Information",
},
"Properties": {
"ApplicationName": "xxx
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "..\..\Logs\log.log",
"rollingInterval": "Hour",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} - {ApplicationName} - {SourceContext} - {Level:u3} - {CorrelationId} - {UserId} - {ClientVersion} => {Message:lj}{NewLine}{Exception}",
}
}
]
}
},
{
"Name": "Debug"
}
] }}
程序文件:
public static IHostBuilder CreateHostBuilder(string[] args)
{
IConfigurationRoot configurationRoot = null;
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((HostBuilderContext context, IConfigurationBuilder builder) =>
{
var environment = context.HostingEnvironment;
var environmentName = environment.IsProduction() ? "" : $".{environment.EnvironmentName}";
builder.Sources.Clear();
builder.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile($"appsettings{environmentName}.json")
.AddJsonFile($"serilogsettings{environmentName}.json")
configurationRoot = builder.Build();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseSerilog((hostingContext, loggerConfiguration) =>
{
loggerConfiguration
.Enrich.FromLogContext()
.Enrich.With<BaseExceptionItemsEnricher>()
.ReadFrom.Configuration(configurationRoot, "Serilog")
});
});
}
Serilog 丰富过滤器(在启动时注册并在 http 请求中正确工作。):
public class LogEnrichmentFilter : IActionFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public LogEnrichmentFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void OnActionExecuting(ActionExecutingContext context)
{
var httpContext = _httpContextAccessor.HttpContext;
httpContext.Request.Headers.TryGetValue("Request-Id", out StringValues requestIds);
var userId = httpContext.User.Claims.FirstOrDefault(x => x.Type == "UserId")?.Value;
httpContext.Request.Headers.TryGetValue("X-Client-Version", out StringValues clientVersions);
LogContext.PushProperty("UserId", userId);
LogContext.PushProperty("CorrelationId", requestIds.FirstOrDefault());
LogContext.PushProperty("ClientVersion", clientVersions.FirstOrDefault());
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
}
我的消费者:
public class MyConsumer :
IConsumer<myEvent>
{
private readonly ILogger _logger;
public MyConsumer (
ILogger<MyConsumer> logger)
{
_logger = logger;
}
public async Task Consume(ConsumeContext<MyEvent> context)
{
_logger.LogInformation("LOGGING TEST"); } } }
记录结果:
2021-12-29 12:35:38.183 - xxx - xxx.MyConsumer - INF - - - => LOGGING TEST
如您所见,不要记录我推送的属性(correlationId、userId 等)。
最后,我解决了这个问题。 我添加了两个过滤器:PublishContext 过滤器和 ConsumeContext 过滤器。
在 PublishContext 过滤器中,我将 Serilog 属性设置为 MT headers:
public class IntegrationEventPublishFilter<T> : IFilter<PublishContext<T>> where T : class
{
private readonly ICurrentRequest _currentRequest;
private readonly ICurrentUser _currentUser;
public IntegrationEventPublishFilter(ICurrentRequest currentRequest, ICurrentUser currentUser)
{
_currentRequest = currentRequest;
_currentUser = currentUser;
}
public Task Send(PublishContext<T> context, IPipe<PublishContext<T>> next)
{
context.Headers.Set("Language", _currentRequest.Language);
context.Headers.Set("UId", _currentUser.UserId);
context.Headers.Set("CorrelationId", _currentRequest.ReuqestId);
context.Headers.Set("ClientVersion", _currentRequest.ClientVersion);
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
并且,在 ConsumeContext 过滤器中,我得到了 MT headers 并推送到 Serilog LogContext:
public class IntegrationEventConsumeFilter<T> : IFilter<ConsumeContext<T>> where T : class
{
public Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
{
var acceptLanguage = context.Headers.Get<string>("Language");
CultureInfo.CurrentUICulture = string.IsNullOrWhiteSpace(acceptLanguage) ?
new CultureInfo("fa-IR") : new CultureInfo(acceptLanguage);
context.Headers.TryGetHeader("UId", out object userId);
context.Headers.TryGetHeader("ClientVersion", out object clientVersion);
context.Headers.TryGetHeader("CorrelationId", out object correlationId);
LogContext.PushProperty("UId", userId);
LogContext.PushProperty("ClientVersion", clientVersion);
LogContext.PushProperty("CorrelationId", correlationId);
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
}