【问题标题】:Does Asp.Net Core preserve CallContext across requests?Asp.Net Core 是否跨请求保留 CallContext?
【发布时间】:2019-05-14 11:56:02
【问题描述】:

Asp.Net Core 是否跨请求保留 CallContext?

我们有一个用例将上下文属性(用户名)附加到线程,可以从我们的日志框架(NLog,MDLC)中提取。据我所知,MDLC 使用CallContext

我们需要每个请求都具有清晰的上下文属性。是否需要一开始就清除CallContext?

【问题讨论】:

  • 你不应该在异步场景中依赖TLS。使用CallContextAsyncLocal<T>
  • 当您说跨请求时,您的意思是跨向您的 API 发出的连续 HTTP 请求吗?如果是这样,那么您不能保证它们无论如何都会针对同一个线程运行。整个方法似乎有点缺陷。
  • @tukaef 我们正在使用github.com/NLog/NLog/wiki/MDLC-Layout-Renderer。没有关于它如何工作的大量文档,但看起来它使用了 CallContext。我已经改写了我的问题。
  • 我真的不明白你想要完成什么,但你肯定不想将用户名之类的东西附加到线程上。在像 Web 服务器这样的多线程环境中,特定用户可以使用任意数量的不同线程,即使在同一个请求期间也是如此。你最终会在用户请求中泄漏。
  • @ChrisPratt 澄清一下,我们的用例是我们需要捕获与请求关联的用户名(通过 HTTP 标头传递)并将其记录在所有下游日志中。正如您所说,这是一个异步环境,请求可以切换线程。但是我认为 LogicalCallContext 应该通过 async / await (如果不是 Task.Run)。在我们的日志输出中,如果HTTPContex.Current 可用,我们会从 HTTP 标头捕获用户名,并回退到填充在中间件中的 LogicalCallContext。如果这不是正确的方法,您能推荐一个更好的方法吗?

标签: c# asp.net-core nlog


【解决方案1】:

NLog.Web.AspNetCore 提供了许多布局渲染器,可以捕获 HttpRequest-context-properties 进行日志记录(无需注入 NLog MDLC)。

这将探测 HttpRequest-Header 的值 JSNLog-RequestId,如果为空则回退到 AspNetCore-RequestId,如果也是空则回退到 AspNetCore-TraceIdentifer

${aspnet-request:header=JSNLog-RequestId:whenEmpty=${mdlc:item=RequestId:whenEmpty=${aspnet-traceIdentifier}}}

可用${aspnet} Layout-renderers列表

NLog 在 NetCore 上使用 AsyncLocal,如果您使用的是 Microsoft Extension Logging (MEL),那么您可以使用 ILogger.BeginScope 将属性注入到 NLog MDLC 中(并且在离开范围时清除)

NLog MDLC 也可以使用MappedDiagnosticsLogicalContext.SetScoped 填充,离开 using-scope 时也会清除。

【讨论】:

    【解决方案2】:

    我们使用 Serilog(应该或多或少等同于 NLog)。它与 Aspnet 核心的 ILogger 有很好的集成。

    您可以创建一个中间件并使用ILogger.BeginScope

    public class LoggerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<LoggerMiddleware> _logger;
    
        public LoggerMiddleware(RequestDelegate next, ILogger<LoggerMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }
    
        public async Task Invoke(HttpContext context)
        {
            var dictionary = new Dictionary<string, object>
            {
                { "Username", context.User?.Identity?.Name; }
            };
    
            using (_logger.BeginScope(dictionary))
            {
                await _next(context);
            }
        }
    }
    

    通过我们的 Serilog 集成,我们能够在我们的日志模板中使用 Username,并在整个 HTTP 调用(使用不同的线程)中保留它。

    您的 NLog 整数应该类似于此 wiki https://github.com/NLog/NLog.Web/wiki/Getting-started-with-ASP.NET-Core-2

    【讨论】:

    • BeginScope 如何与 Singleton、Scoped 或 Transient 对象中的现有 ILogger 实例一起工作?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 2018-03-12
    • 2010-10-15
    • 2019-08-05
    • 2014-09-13
    • 2019-12-16
    相关资源
    最近更新 更多