将所有NLog日志绑定回WebAPI中原始请求的方法? [英] Approach for tying all NLog logs back to the original request within WebAPI?

查看:153
本文介绍了将所有NLog日志绑定回WebAPI中原始请求的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力使用WebAPI构建API,并且一直在使用NLog记录整个堆栈.我的API解决方案有两个主要项目,包括:

I am working to build an API using WebAPI, and have been using NLog for logging throughout the stack. My API solution has two main projects including:

  • 实现控制器和webapi内容的网站层本身
  • 以类似于CQRS的方式实现异步"命令和处理程序的服务层

我要实现的目标是自动生成一个可以附加到日志语句的唯一ID,以便为单个请求提供服务时编写的任何日志,无论它们来自哪个层,都可以链接回该原始日志.要求.我还希望这种方法无需传递唯一的ID,也不必让日志语句本身担心将其包含在调用中.

What I'm trying to achieve is to automatically generate a unique ID that I can attach to log statements so that any logs written while servicing a single request, no matter what layer they came from, can be linked back to that original request. I'd also like this to work without passing the unique ID around, or having the log statements themselves be concerned with including it in their calls.

出于这个目标,我开始考虑编写一个自定义的委托处理程序来拦截每个请求(以下

With that goal in mind I started looking into writing a custom delegating handler to intercept each request (following this post for guidance) and add a unique ID as a property within NLog. I ended up with the following:

/// <summary>
/// This class is a WebAPI message handler that helps establish the data and operations needed
/// to associate log statements through the entire stack back to the originating request.
/// 
/// Help from here: http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi
/// </summary>
public class InitializeLoggingMessageHandler : DelegatingHandler
{
    private ILogger _logger;

    // The logger is injected with Autofac
    //
    public InitializeLoggingMessageHandler(ILogger logger)
    {
        _logger = logger;
    }

    protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        // Get a unique ID for this request
        //
        var uniqueId = Guid.NewGuid().ToString();

        // Now that we have a unique ID for this request, we add it to NLog's MDC as a property
        //  we can use in the log layouts. We do NOT use the global diagnostic context because
        //  WebAPI is multi-threaded, and we want the ID to be scoped to just the thread servicing
        //  this request.
        //
        NLog.MappedDiagnosticsContext.Set("UniqueId", uniqueId);

        // Capture some details about the request for logging
        //
        var requestInfo = string.Format("{0} {1}", request.Method, request.RequestUri);
        var requestMessage = await request.Content.ReadAsByteArrayAsync();

        _logger.Info("Request: {0} - {1}", requestInfo, Encoding.UTF8.GetString(requestMessage));

        var response = await base.SendAsync(request, cancellationToken);

        return response;
    }
}

使用此代码,我可以在日志布局中使用唯一ID,如下所示:

With this code I can then use the unique ID in log layouts like so:

<target xsi:type="Debugger" name="DebugLogger" 
        layout="${longdate} ${logger} ${mdc:item=UniqueId} ${message}" />

此方法的问题在于,我正在使用NLog的MappedDiagnosticsContext尝试将唯一ID保存为可在布局中使用的属性(因此,执行日志记录的代码不需要知道).这是一种用于存储值的线程本地机制,因此在您拥有异步代码时,该机制就会崩溃,因为启动请求的线程可能不是执行所有请求的线程.

The problem with this approach is that I'm using NLog's MappedDiagnosticsContext to try to save the unique ID as a property that can be used within layouts (so my code doing the logging doesn't need to know). This is a thread-local mechanism for storing values, so it breaks down when you have async code since the thread that starts a request, may not be the one that executes all of it.

所以发生的情况是,第一条日志消息包含唯一的ID,但是后来的日志消息可能会丢失它,因为它们位于不同的线程上并且无法访问该值.我也不能在NLog中使用GlobalDiagnosticsContext,因为它确实是全局的,因此WebAPI中的多个请求将很容易覆盖唯一的I​​D,并且数据将毫无用处.

So what happens is the first log messages have the unique ID included, but the ones later on could be missing it since they're on a different thread and can't access the value. I also can't use the GlobalDiagnosticsContext within NLog because it's truly global, so multiple requests in WebAPI would easily overwrite the unique ID, and the data would be useless.

因此,为了将所有日志消息都与WebAPI内发起的请求相关联,我是否应该考虑另一种机制?

So with the goal of associating all log messages back to the request that originated within WebAPI, is there another mechanism that I should be considering?

推荐答案

看看

Take a look at LogicalCallContext. As of .NET 4.5, it supports async scenarios.

先生. Jeffrey Richter :

.NET Framework具有鲜为人知的功能,该功能使您可以将数据与逻辑"执行线程相关联.此功能称为逻辑 调用上下文,它允许数据流向其他线程,AppDomain,甚至流向其他进程中的线程.

The .NET Framework has a little-known facility that allows you to associate data with a "logical" thread-of-execution. This facility is called logical call context and it allows data to flow to other threads, AppDomains, and even to threads in other processes.

这篇关于将所有NLog日志绑定回WebAPI中原始请求的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆