使用log4j进行条件记录

Dav*_*acs 20 java log4j

我工作的Web应用程序偶尔会为某些用户开发数据完整性问题.我想打开跟踪级别日志记录,但由于我们每秒处理100个请求跟踪日志记录,因此每个请求都是不可能的.

有没有办法让log4j能够有条件地记录?换句话说,我希望只有在特定用户发出请求时才能获取跟踪日志.由于我事先不知道哪些用户会受到影响,我不能简单地暂时硬编码用户名.

编辑:

我想我需要更清楚一些.我可以轻松地为日志语句添加条件.例如

Logger logger = Logger.getLogger("foo");
String usernameFilter = "piglet";
String username = request.getParameter("username");
logger.setLevel(usernameFilter.equals(username) ? Level.TRACE : Level.INFO);
if (logger.isTraceEnabled()) {
   logger.trace("blah blah blah");
}
Run Code Online (Sandbox Code Playgroud)

困难在于动态地改变设置日志级别的条件.换句话说,在上面的示例中,我如何设置usernameFilter的值,而不是硬编码.

Mat*_*ell 20

您想在log4j或slf4j中查看嵌套诊断上下文映射诊断上下文.NDC/MDC允许您将数据插入到可由log4j过滤的会话中.

因此,您要将用户名定义为NDC,然后您可以更改log4j.properties以更改特定用户的日志记录级别.

MDC使用Map,而NDC基于堆栈原则.如果您正在使用slf4j,您甚至可以根据MDC中的信息创建单独的日志文件.

例如,我们在用户登录网站时执行此操作.我们想跟踪特定用户正在做什么(回顾性地),因此我们将用户名和会话ID添加到NDC,然后我们可以对这些用户进行过滤.

代码类似于以下内容:

public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        MDC.put("username", session.getParameter("username")); // or where ever t is stored
        chain.doFilter(request, response);
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的log4j.xml中,此过滤器基于用户:

  <appender name="UserDebug" class="org.apache.log4j.RollingFileAppender">
    <param name="File" value="userdebug.log"/>
    <param name="Append" value="true"/>
    <param name="MaxFileSize" value="5000KB"/>
    <param name="maxBackupIndex" value="5"/> 
          <layout class="org.apache.log4j.PatternLayout">
                  <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] user:%X{username} %-5p - %m%n" />
          </layout>

          <filter class="org.apache.log4j.varia.StringMatchFilter">
                  <param name="StringToMatch" value="user:fred" />
                  <param name="AcceptOnMatch" value="true" />
          </filter>

      <filter class="org.apache.log4j.varia.DenyAllFilter"/>
  </appender>
Run Code Online (Sandbox Code Playgroud)

%X {key}在MDC中输出MDC.get(key)的值.如果您想要一个更复杂的过滤器,您可以自己扩展它,并自己查看MDC中的值.


KC *_*ltz 7

Matthew Farwell的答案(使用MDC)对我有用,他提到编写自己的过滤器.我需要在某些情况下隐藏日志消息.具体来说,我们的健康检查呼叫比典型的用户使用频率更高,并且不必要地填写日志.Matthew的解决方案不适合我的情况,因为它要求您将MDC添加到实际的日志输出中.我只想使用MDC进行过滤,所以我扩展org.apache.log4j.spi.Filter了以下类:

/**
 * Log4J filter that stops certain log messages from being logged, based on a
 * value in the MDC (See Log4J docs).
 */
public class Log4JMDCFilter extends Filter
{

private String keyToMatch;
private String valueToMatch;
private boolean denyOnMatch = true;

/**
 * {@inheritDoc}
 */
public int decide(LoggingEvent event)
{
    if (keyToMatch != null && valueToMatch != null
        && valueToMatch.equals(event.getMDC(keyToMatch)))
    {
        return denyOnMatch ? DENY : ACCEPT;
    }

    return denyOnMatch ? ACCEPT : DENY;
}

/**
 * The key on which to filter.
 * 
 * @return key on which to filter
 */
public String getKeyToMatch()
{
    return keyToMatch;
}

/**
 * Sets the key on which to filter.
 * 
 * @param keyToMatch key on which to filter
 */
public void setKeyToMatch(String keyToMatch)
{
    this.keyToMatch = keyToMatch;
}

/**
 * Gets the value to match.
 * 
 * @return the value to match.
 */
public String getValueToMatch()
{
    return valueToMatch;
}

/**
 * Sets the value to match.
 * 
 * @param valueToMatch the value to match.
 */
public void setValueToMatch(String valueToMatch)
{
    this.valueToMatch = valueToMatch;
}

/**
 * Returns true if the log message should not be logged if a match is found.
 * 
 * @return true if the log message should not be logged if a match is found.
 */
public boolean isDenyOnMatch()
{
    return denyOnMatch;
}

/**
 * Set this to "true" if you do not want log messages that match the given
 * key/value to be logged. False if you only want messages that match to be
 * logged.
 * 
 * @param denyOnMatch "true" if you do not want log messages that match the
 *        given key/value to be logged. False if you only want messages that
 *        match to be logged.
 */
public void setDenyOnMatch(String denyOnMatch)
{
    this.denyOnMatch = Boolean.valueOf(denyOnMatch).booleanValue();
}

}
Run Code Online (Sandbox Code Playgroud)

使用以下log4j.xml片段激活过滤器("HEALTHCHECK"是键,"true"是我要过滤的值):

    <filter class="com.copart.hh.core.utils.Log4JMDCFilter">
        <param name="keyToMatch" value="HEALTHCHECK" />
        <param name="valueToMatch" value="true" />
        <param name="denyOnMatch" value="true" />
    </filter>
Run Code Online (Sandbox Code Playgroud)

然后,在任何你想要标记过滤的地方,输入如下代码:

MDC.put("HEALTHCHECK", "true");
try
{    
      // do healthcheck stuff that generates unnecessary logs
}
finally
{
    MDC.remove("HEALTHCHECK"); // not sure this is strictly necessary
}
Run Code Online (Sandbox Code Playgroud)