首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用log4j进行条件日志记录

使用log4j进行条件日志记录
EN

Stack Overflow用户
提问于 2011-09-13 23:25:51
回答 3查看 33.7K关注 0票数 22

我正在开发的web应用程序偶尔会为一些用户开发数据完整性问题。我想打开跟踪级别日志记录,但由于我们每秒要处理100个请求,跟踪日志记录每个请求都是不可能的。

有没有办法使log4j能够有条件地进行日志记录?换句话说,我希望只有在特定用户发出请求时才能获得跟踪日志。因为我事先不知道哪些用户会受到影响,所以我不能简单地临时硬编码用户名。

编辑:

我想我需要说得更清楚一点。我可以很容易地向我的日志语句中添加条件。例如

代码语言:javascript
复制
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");
}

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-09-13 23:32:09

您希望在log4j或slf4j中查看Nested Diagnostic ContextsMapped Diagnostic Contexts。NDC/MDC允许您将数据插入到可由log4j过滤的会话中。

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

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

例如,当用户登录到网站时,我们就这样做了。我们想要跟踪某个特定用户正在做什么(回溯),所以我们将用户名和会话id添加到NDC中,然后我们可以对它们进行post过滤。

代码类似于以下代码:

代码语言:javascript
复制
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);
    }
}

在您的log4j.xml中,这将根据用户进行过滤:

代码语言:javascript
复制
  <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>

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

票数 21
EN

Stack Overflow用户

发布于 2011-09-15 06:13:53

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

代码语言:javascript
复制
/**
 * 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();
}

}

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

代码语言:javascript
复制
    <filter class="com.copart.hh.core.utils.Log4JMDCFilter">
        <param name="keyToMatch" value="HEALTHCHECK" />
        <param name="valueToMatch" value="true" />
        <param name="denyOnMatch" value="true" />
    </filter>

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

代码语言:javascript
复制
MDC.put("HEALTHCHECK", "true");
try
{    
      // do healthcheck stuff that generates unnecessary logs
}
finally
{
    MDC.remove("HEALTHCHECK"); // not sure this is strictly necessary
}
票数 8
EN

Stack Overflow用户

发布于 2011-09-13 23:35:15

我发现这个blog article非常有用。这可能会帮助您为您的用户创建日志条件。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7404435

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档