63

使用 log4net,我看到了使用称为 NDC 的每个线程的上下文标签堆栈的可能性。

%x通过指定或%ndc格式参数,压入此堆栈的标签显示在 PatternLayout 中。

用法类似于:

ILog log = log4net.LogManager.GetLogger(...) ;

//pattern layout format: "[%ndc] - %message%newline"

log.Info("message 1"); 
using(log4net.NDC.Push("context")
{
    using(log4net.NDC.Push("inner_context")
    {
      log.Info("message 2"); 
    }
    log.Info("message 3"); 
}
log.Info("message 4"); 

输出类似于:

null - message 1
context inner_context - message 2
context - message 3
null - message 4

在您使用 log4net 的编程经验中,您什么时候发现此功能很有用?

4

4 回答 4

86

想要一个例子吗?

采用以下使用 ASP.NET MVC4 编写的 Web API:

// GET api/HypervResource
public string Get()
{
    logger.Debug("Start of service test");
    System.Threading.Thread.Sleep(5000); // simulate work
    logger.Debug("End of service test");
    return "HypervResource controller running, use POST to send JSON encoded RPCs";
}

当服务器并发 HTTP 请求时,日志记录可以交错。例如

2013-06-27 13:28:11,967 [10] DEBUG HypervResource.WmiCalls [(null)] - Start of service test
2013-06-27 13:28:12,976 [12] DEBUG HypervResource.WmiCalls [(null)] - Start of service test
2013-06-27 13:28:14,116 [13] DEBUG HypervResource.WmiCalls [(null)] - Start of service test
2013-06-27 13:28:16,971 [10] DEBUG HypervResource.WmiCalls [(null)] - End of service test
2013-06-27 13:28:17,979 [12] DEBUG HypervResource.WmiCalls [(null)] - End of service test
2013-06-27 13:28:19,119 [13] DEBUG HypervResource.WmiCalls [(null)] - End of service test

在这个简单的示例中,您可以使用线程 id 来区分请求,但是随着日志文件的复杂性增加,这可能会变得很棘手。

更好的选择是提供唯一标识符,将同一请求的日志消息组合在一起。我们可以将代码更新为以下内容:

// GET api/HypervResource
public string Get()
{
    using(log4net.NDC.Push(Guid.NewGuid().ToString()))
    {
        logger.Debug("Start of service test");
        System.Threading.Thread.Sleep(5000); // simulate work
        logger.Debug("End of service test");
        return "HypervResource controller running, use POST to send JSON encoded RPCs";
    }
}

这会生成一个日志,您可以使用 grep 来查看与特定请求相关的问题。例如

2013-06-27 14:04:31,431 [11] DEBUG HypervResource.WmiCalls [525943cb-226a-43c2-8bd5-03c258d58a79] - Start of service test
2013-06-27 14:04:32,322 [12] DEBUG HypervResource.WmiCalls [5a8941ee-6e26-4c1d-a1dc-b4d9b776630d] - Start of service test
2013-06-27 14:04:34,450 [13] DEBUG HypervResource.WmiCalls [ff2246f1-04bc-4451-9e40-6aa1efb94073] - Start of service test
2013-06-27 14:04:36,434 [11] DEBUG HypervResource.WmiCalls [525943cb-226a-43c2-8bd5-03c258d58a79] - End of service test
2013-06-27 14:04:37,325 [12] DEBUG HypervResource.WmiCalls [5a8941ee-6e26-4c1d-a1dc-b4d9b776630d] - End of service test
2013-06-27 14:04:39,453 [13] DEBUG HypervResource.WmiCalls [ff2246f1-04bc-4451-9e40-6aa1efb94073] - End of service test
于 2013-06-27T13:07:05.120 回答
25

在 ASP.NET 等服务器应用程序中。

例如,您可以将有关当前请求的信息推送到 NDC。

于 2008-12-02T16:51:12.740 回答
20

当您需要查看大量日志时,这些功能会派上用场。你什么时候会有很多日志?在具有交错输出的生产系统上诊断奇怪的错误。拥有更多上下文可以让您过滤输出或不输出不需要的日志。

嵌套上下文的另一种情况可能很有用,如果一个方法或某个特性在不同的上下文中被多次调用,并且您需要一种方法来区分它们。

于 2008-12-02T16:02:52.853 回答
10

NDC.Push已被弃用。现在(ThreadContext.Stacks["NDC"])的首选方式是:

var disposable = ThreadContext.Stacks["NDC"].Push("context");
try
{
  Log.Info("begin"); // optional, but nice
  ...
}
finally
{
  Log.Info("end"); // optional, but nice
  disposable.Dispose();
}

请记住检查您的转换模式,使其包括%property{NDC}

<layout type="log4net.Layout.PatternLayout">
  <conversionPattern
    value="%date [%2thread] %-5level [%property{NDC}] - %.10240message%newline" />
</layout>
于 2018-01-11T08:59:32.967 回答