如何在 SeriLog Sink 中获取当前的 HttpContext?
How can I get the current HttpContext in a SeriLog Sink?
我正在使用 Building a Simple Sink 示例创建自己的实现 ILogEventSink
的 SeriLog 接收器,目的是从用户声明中记录一些信息。要访问 Core 中的 HttpContext,我通常会注入一个 IHttpContextAccessor
的实例,但该示例显示在扩展方法中创建一个接收器实例,例如
public class MySink : ILogEventSink
{
private readonly IFormatProvider _formatProvider;
public MySink(IFormatProvider formatProvider)
{
_formatProvider = formatProvider;
}
public void Emit(LogEvent logEvent)
{
// How to get HttpContext here?
}
}
public static class MySinkExtensions
{
public static LoggerConfiguration MySink(
this LoggerSinkConfiguration loggerConfiguration,
IFormatProvider formatProvider = null)
{
return loggerConfiguration.Sink(new MySink(formatProvider));
}
}
...然后使用水槽...
var log = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.MySink()
.CreateLogger();
如何在sink的Emit方法中获取到当前的HttpContext?或者是否可以让 DI 框架创建接收器?!
我有一个 MVC 站点 运行 Asp.Net 针对 .Net 4.6.2 运行时的 Core 2 框架使用 Serilog.AspNetCore v2.1.0.
更新 - 解决方法
根据@tsimbalar 的指示,我创建了类似于以下代码的中间件。在我的 StartUp.Configure
方法中,我使用 app.UseMiddleware<ClaimsMiddleware>();
添加它 在 应用程序身份验证步骤发生后(否则将不会加载声明)。
public class ClaimsMiddleware
{
private static readonly ILogger Log = Serilog.Log.ForContext<ClaimsMiddleware>();
private readonly RequestDelegate next;
public ClaimsMiddleware(RequestDelegate next)
{
this.next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
// Get values from claims here
var myVal = httpContext
.User
.Claims
.Where(x => x.Type == "MyVal")
.Select(x => x.Value)
.DefaultIfEmpty(string.Empty)
.SingleOrDefault();
using (LogContext.PushProperty("MyVal", myVal))
{
try
{
await next(httpContext);
}
// Never caught, because `LogException()` returns false.
catch (Exception ex) when (LogException(httpContext, ex)) { }
}
}
private static bool LogException(HttpContext httpContext, Exception ex)
{
var logForContext = Log.ForContext("StackTrace", ex.StackTrace);
logForContext.Error(ex, ex.Message);
return false;
}
}
更新
我想你可能想看看这篇文章:http://mylifeforthecode.github.io/enriching-serilog-output-with-httpcontext-information-in-asp-net-core/
想法是注册一个自定义中间件,它将在请求期间将所有上下文 信息添加到当前LogContext
。
要使其正常工作,您必须使用
配置您的记录器
Log.Logger = new LoggerConfiguration()
// snip ....MinimumLevel.Debug()
.Enrich.FromLogContext()
// snip ...
.CreateLogger();
Nicholas Blumhardt 的这篇文章也可能有所帮助:https://blog.getseq.net/smart-logging-middleware-for-asp-net-core/
警告 - 以下解决方案在这种情况下不起作用
如果记录器提前注册(在 Program.Main() 中),下面的解决方案将无法工作
首先,如果您想添加附加到已记录事件的额外信息,我相信您想要的是 Enricher。
然后你可以:
- 注册 IHttpContextAccessor into your ServiceCollection (for instance, using
AddHttpContextAccessor()
) : services.AddHttpContextAccessor();
- 创建
ILogEventEnricher
的实现,在其构造函数中接受 IHttpContextAccessor
- 配置记录器时,注入
IHttpContextAccessor
(通过将 IHttpContextAccessor
类型的参数添加到 Startup.Configure()
- 将此增强器添加到您的记录器
enricher 可能看起来像 https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/ClaimValueEnricher.cs .
你会配置你的记录器:
var logger = new LoggerConfiguration()
.EnrichWith(new MyEnricher(contextAccessor))
.WriteTo.Whatever()
.CreateLogger();
我一直在努力尝试做同样的事情,我终于找到了一个合适的解决方案。
创建 Logger 时不要添加 enricher。您必须在可以访问 IServiceProvider
的中间件中添加 enricher。关键是LogContext
有个方法,Push
,可以加一个enricher:
public async Task Invoke(HttpContext httpContext)
{
IServiceProvider serviceProvider = httpContext.RequestServices;
using (LogContext.Push(new LogEnricher(serviceProvider))) {
await _next(httpContext);
}
}
在 ConfigureServices
中,我添加了一个 services.AddScoped<HttpContextToLog>()
调用。
然后,我在几个地方填充 HttpContextToLog
对象,像这样访问它:
HttpContextToLog contextToLog = _serviceProvider.GetService<HttpContextToLog>();
在 Enrich
方法中,在 IActionFilter
中,在 IPageFilter
中,等等
我正在使用 Building a Simple Sink 示例创建自己的实现 ILogEventSink
的 SeriLog 接收器,目的是从用户声明中记录一些信息。要访问 Core 中的 HttpContext,我通常会注入一个 IHttpContextAccessor
的实例,但该示例显示在扩展方法中创建一个接收器实例,例如
public class MySink : ILogEventSink
{
private readonly IFormatProvider _formatProvider;
public MySink(IFormatProvider formatProvider)
{
_formatProvider = formatProvider;
}
public void Emit(LogEvent logEvent)
{
// How to get HttpContext here?
}
}
public static class MySinkExtensions
{
public static LoggerConfiguration MySink(
this LoggerSinkConfiguration loggerConfiguration,
IFormatProvider formatProvider = null)
{
return loggerConfiguration.Sink(new MySink(formatProvider));
}
}
...然后使用水槽...
var log = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.MySink()
.CreateLogger();
如何在sink的Emit方法中获取到当前的HttpContext?或者是否可以让 DI 框架创建接收器?!
我有一个 MVC 站点 运行 Asp.Net 针对 .Net 4.6.2 运行时的 Core 2 框架使用 Serilog.AspNetCore v2.1.0.
更新 - 解决方法
根据@tsimbalar 的指示,我创建了类似于以下代码的中间件。在我的 StartUp.Configure
方法中,我使用 app.UseMiddleware<ClaimsMiddleware>();
添加它 在 应用程序身份验证步骤发生后(否则将不会加载声明)。
public class ClaimsMiddleware
{
private static readonly ILogger Log = Serilog.Log.ForContext<ClaimsMiddleware>();
private readonly RequestDelegate next;
public ClaimsMiddleware(RequestDelegate next)
{
this.next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
// Get values from claims here
var myVal = httpContext
.User
.Claims
.Where(x => x.Type == "MyVal")
.Select(x => x.Value)
.DefaultIfEmpty(string.Empty)
.SingleOrDefault();
using (LogContext.PushProperty("MyVal", myVal))
{
try
{
await next(httpContext);
}
// Never caught, because `LogException()` returns false.
catch (Exception ex) when (LogException(httpContext, ex)) { }
}
}
private static bool LogException(HttpContext httpContext, Exception ex)
{
var logForContext = Log.ForContext("StackTrace", ex.StackTrace);
logForContext.Error(ex, ex.Message);
return false;
}
}
更新 我想你可能想看看这篇文章:http://mylifeforthecode.github.io/enriching-serilog-output-with-httpcontext-information-in-asp-net-core/
想法是注册一个自定义中间件,它将在请求期间将所有上下文 信息添加到当前LogContext
。
要使其正常工作,您必须使用
配置您的记录器Log.Logger = new LoggerConfiguration()
// snip ....MinimumLevel.Debug()
.Enrich.FromLogContext()
// snip ...
.CreateLogger();
Nicholas Blumhardt 的这篇文章也可能有所帮助:https://blog.getseq.net/smart-logging-middleware-for-asp-net-core/
警告 - 以下解决方案在这种情况下不起作用
如果记录器提前注册(在 Program.Main() 中),下面的解决方案将无法工作
首先,如果您想添加附加到已记录事件的额外信息,我相信您想要的是 Enricher。
然后你可以:
- 注册 IHttpContextAccessor into your ServiceCollection (for instance, using
AddHttpContextAccessor()
) :services.AddHttpContextAccessor();
- 创建
ILogEventEnricher
的实现,在其构造函数中接受IHttpContextAccessor
- 配置记录器时,注入
IHttpContextAccessor
(通过将IHttpContextAccessor
类型的参数添加到Startup.Configure()
- 将此增强器添加到您的记录器
enricher 可能看起来像 https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/ClaimValueEnricher.cs .
你会配置你的记录器:
var logger = new LoggerConfiguration()
.EnrichWith(new MyEnricher(contextAccessor))
.WriteTo.Whatever()
.CreateLogger();
我一直在努力尝试做同样的事情,我终于找到了一个合适的解决方案。
创建 Logger 时不要添加 enricher。您必须在可以访问 IServiceProvider
的中间件中添加 enricher。关键是LogContext
有个方法,Push
,可以加一个enricher:
public async Task Invoke(HttpContext httpContext)
{
IServiceProvider serviceProvider = httpContext.RequestServices;
using (LogContext.Push(new LogEnricher(serviceProvider))) {
await _next(httpContext);
}
}
在 ConfigureServices
中,我添加了一个 services.AddScoped<HttpContextToLog>()
调用。
然后,我在几个地方填充 HttpContextToLog
对象,像这样访问它:
HttpContextToLog contextToLog = _serviceProvider.GetService<HttpContextToLog>();
在 Enrich
方法中,在 IActionFilter
中,在 IPageFilter
中,等等