Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将所有NLog日志绑定回WebAPI中的原始请求的方法?_C#_Logging_Asp.net Web Api_Nlog - Fatal编程技术网

C# 将所有NLog日志绑定回WebAPI中的原始请求的方法?

C# 将所有NLog日志绑定回WebAPI中的原始请求的方法?,c#,logging,asp.net-web-api,nlog,C#,Logging,Asp.net Web Api,Nlog,我正在使用WebAPI构建一个API,并一直在使用NLog记录整个堆栈。我的API解决方案有两个主要项目,包括: 实现控制器和webapi的网站层本身 一个服务层,以类似CQRS的方式实现“异步”命令和处理程序 我试图实现的是自动生成一个唯一的ID,我可以将它附加到日志语句中,以便在为单个请求提供服务时编写的任何日志,无论它们来自哪个层,都可以链接回原始请求。我还希望这种方法能够在不传递唯一ID的情况下工作,也不希望日志语句本身将其包含在调用中 考虑到这个目标,我开始考虑编写一个定制的委托处

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

  • 实现控制器和webapi的网站层本身
  • 一个服务层,以类似CQRS的方式实现“异步”命令和处理程序
我试图实现的是自动生成一个唯一的ID,我可以将它附加到日志语句中,以便在为单个请求提供服务时编写的任何日志,无论它们来自哪个层,都可以链接回原始请求。我还希望这种方法能够在不传递唯一ID的情况下工作,也不希望日志语句本身将其包含在调用中

考虑到这个目标,我开始考虑编写一个定制的委托处理程序来拦截每个请求(以下是指导),并在NLog中添加一个唯一的ID作为属性。我的结论如下:

/// <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;
    }
}
//
///此类是一个WebAPI消息处理程序,可帮助建立所需的数据和操作
///将整个堆栈中的日志语句关联回原始请求。
/// 
///这里的帮助:http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi
/// 
公共类InitializeLoggingMessageHandler:DelegatingHandler
{
私人ILogger_记录器;
//记录器被注入Autofac
//
公共初始设置LoggingMessageHandler(ILogger记录器)
{
_记录器=记录器;
}
受保护的异步重写System.Threading.Tasks.Task SendAsync(HttpRequestMessage请求,System.Threading.CancellationToken CancellationToken)
{
//获取此请求的唯一ID
//
var uniqueId=Guid.NewGuid().ToString();
//现在我们有了这个请求的唯一ID,我们将它作为属性添加到NLog的MDC中
//我们可以在日志布局中使用。我们不使用全局诊断上下文,因为
//WebAPI是多线程的,我们希望ID的作用域仅限于线程服务
//这是我的请求。
//
NLog.MappedDiagnosticsContext.Set(“UniqueId”,UniqueId);
//捕获有关日志记录请求的一些详细信息
//
var requestInfo=string.Format(“{0}{1}”,request.Method,request.RequestUri);
var requestMessage=wait request.Content.ReadAsByteArrayAsync();
_Info(“请求:{0}-{1}”,requestInfo,Encoding.UTF8.GetString(requestMessage));
var response=await base.sendaync(请求、取消令牌);
返回响应;
}
}
使用此代码,我可以在日志布局中使用唯一ID,如下所示:

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

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

因此,第一个日志消息包含唯一ID,但后面的日志消息可能会丢失该ID,因为它们位于不同的线程上,无法访问该值。我也不能在NLog中使用GlobalDiagnosticsContext,因为它是真正的全局性的,所以WebAPI中的多个请求很容易覆盖唯一的ID,并且数据将是无用的

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

看看。从.NET4.5开始,it场景已经出现

先生:

NET Framework有一个鲜为人知的功能,允许您将数据与“逻辑”执行线程相关联。此功能称为逻辑 调用上下文,它允许数据流到其他线程、AppDomain,甚至流到其他进程中的线程

弗尔。1.0能够捕获使用ILogger.BeginScope创建的上下文属性。可以使用NLog
${mdlc}
提取这些数据

默认情况下,Microsoft引擎将注入诸如
RequestId
RequestPath
等属性


另请参见:

您最终采用了什么解决方案?我实际上决定使用Serilog而不是NLog进行日志记录。Serilog使用(我假定)下面描述的LogicalCallContext方法提供线程安全上下文。有了Serilog,我可以使用我在问题中描述的技术,但它实际上是有效的:)接受这个答案,因为它最终是有效的,但是我实际上切换到Serilog作为我们的日志库,因为它提供了我所需要的。4.1引入了利用
LogicalCallContext
${mdlc}
。另见