无法将值放在MDC中

时间:2013-05-29 13:44:21

标签: java logging wicket mdc

我正在尝试在wicket的onBeginRequest() RequestCycle()中记录少量值。 但是这些值未记录在调试文件中。我将值放在RequestCycleListeners()中的MDC中。

以下是代码:

getRequestCycleListeners().add(new AbstractRequestCycleListener()
{       
public void onBeginRequest(RequestCycle cycle) 
{                   
  if( cycle.getRequest().getContainerRequest() instanceof HttpServletRequest )
  {
    HttpServletRequest containerRequest = 
        (HttpServletRequest)cycle.getRequest().getContainerRequest();

    MDC.put("serverName", containerRequest.getServerName());
    MDC.put("sessionId",  containerRequest.getSession().getId());

    LOGGER.debug("logging from RequestCycleListeners() !!!");
    WebClientInfo webClientInfo = new WebClientInfo(RequestCycle.get());
    System.out.println(webClientInfo.getUserAgent());
    System.out.println("webClientInfo.getProperties().getBrowserVersionMajor() " +containerRequest.getRemoteAddr());
}

};

我期待'serverName','sessionId'被记录在调试文件中。

我在扩展listener的类中添加了此WebApplication

我使用的是log4j.xml DEBUG appender如下所示:

<appender name="DEBUG" class="org.apache.log4j.rolling.RollingFileAppender">
  <param name="Append" value="true"/>
  <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="[%d{ISO8601} %t %5p] %m -- %X{serverName} -- %X{sessionId} -- %X{portNumber}%n"/>
  </layout>
  <filter class="org.apache.log4j.varia.LevelRangeFilter">
    <param name="LevelMin" value="DEBUG"/>
    <param name="LevelMax" value="WARN"/>
  </filter>
</appender>

我们在root标签中定义范围:

<root>
   <priority value="INFO" />
   <appender-ref ref="CONSOLE" />
   <appender-ref ref="DEBUG" />
   <appender-ref ref="ERROR" />
</root>

2 个答案:

答案 0 :(得分:21)

通常,如果通过配置在日志记录模式中包含MDC密钥,则MDC值仅输出到日志。由于slf4j只是一个外观,您需要在slf4j下面具有特定于框架的支持和配置才能使用MDC。阅读slf4j关于here的注释。

所以,例如,如果你使用log4j作为slf4j下面的impl,那么你需要log4j config(ConversionPattern),如:

%d %-5p [%c] [%X{serverName} %X{sessionId}] %m%n

其中%X{serverName} %X{sessionId}是从MDC中提取值的相关部分。

Here是使用没有sl4j的log4j的一个很好的例子。请参阅log4j javadoc hereX转化字符的说明。

请注意,logback的模式语法是相同的。请参阅logback here的详细信息。

另请注意,MDC的最佳实践(使用ThreadLocal引擎盖下)是在上下文不再在范围内时清除上下文(删除放在地图中的值)。这通常意味着在remove块中调用clearfinally,例如:

try {
    //...
    MDC.put("key1", value1);
    MDC.put("key2", value2);
    //...
} finally {
    //this
    MDC.remove("key1");
    MDC.remove("key2");
    //or this
    MDC.clear();
}

如果持有MDC的线程属于池以供以后重用,这一点尤其重要。您当然不希望无意中记录无效的上下文值,因为这只会引起混淆。

修改

您的log4j配置似乎有点奇怪,原因如下:

  1. 您在日志级别之后命名您的appender,这可能会导致混淆
  2. 您的RollingFileAppender未定义文件
  3. 您的root记录器将记录到3个不同的appender,其中一个名为DEBUG,但它配置为仅记录INFO级别和更高级别(基于{{1标签),所以不会记录调试语句
  4. 除非您单独配置了一些未显示的特定类别,否则我猜您的priority语句的 none 正在被记录,无论您尝试使用MDC。

答案 1 :(得分:0)

请注意,如果您使用的是 AsyncAppender ,则从您的线程清除 MDC 将无法保护您,因为日志事件和MDC处理发生在AsyncAppender的线程中。另请参阅this related bug

不幸的是,在v 1.2.17 最新发布的EOLed log4j-1.x版本中,AsyncAppender的Dispatcher-Thread在停止时也没有清除MDC。

因为AsyncAppender / Dispatcher非常简单,所以很容易通过添加

来修补它
finally
{
    MDC.clear();
}

org.apache.log4j.AsyncAppender.Dispatcher.run()方法的try-block中。

当然,也可以通过在ServletContainer中执行时不使用AsyncAppender来解决此问题。