Java 使用log4j的条件日志记录
我正在使用的web应用程序偶尔会为一些用户开发数据完整性问题。我想打开跟踪级别的日志记录,但由于我们每秒处理100个请求,因此跟踪日志记录不可能处理每个请求 log4j有没有办法有条件地记录日志?换句话说,我希望只有在特定用户发出请求时才能获取跟踪日志。因为我事先不知道哪些用户会受到影响,所以我不能简单地临时硬编码用户名 编辑: 我想我需要更清楚一点。我可以轻松地向日志语句添加条件。比如说Java 使用log4j的条件日志记录,java,log4j,Java,Log4j,我正在使用的web应用程序偶尔会为一些用户开发数据完整性问题。我想打开跟踪级别的日志记录,但由于我们每秒处理100个请求,因此跟踪日志记录不可能处理每个请求 log4j有没有办法有条件地记录日志?换句话说,我希望只有在特定用户发出请求时才能获取跟踪日志。因为我事先不知道哪些用户会受到影响,所以我不能简单地临时硬编码用户名 编辑: 我想我需要更清楚一点。我可以轻松地向日志语句添加条件。比如说 Logger logger = Logger.getLogger("foo"); String usern
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的值。您要查看或在log4j或slf4j中查看。NDC/MDC允许您在会话中插入数据,这些数据可以通过log4j进行过滤
因此,您可以在NDC中定义用户名,然后更改log4j.properties以更改特定用户的日志记录级别
MDC使用映射,而NDC基于堆栈原则。如果正在使用,甚至可以根据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);
}
}
<appender name="file" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${catalina.home}/logs/uploader.log" />
<param name="append" value="false" />
<param name="threshold" value="info" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.StringMatchFilter">
<param name="StringToMatch" value="trackfile" />
<param name="AcceptOnMatch" value="true" />
</filter>
<filter class="org.apache.log4j.varia.DenyAllFilter" />
</appender>
在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>
%X{key}在MDC中输出MDC.get(key)的值。如果您想要一个更复杂的过滤器,您可以自己扩展它,自己查看MDC中的值。我发现这非常有用。这可能有助于您为用户创建登录条件。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();
}
}
使用以下log4j.xml片段激活过滤器(“HEALTHCHECK”是键,“true”是我正在筛选的值):
我做了如下的变通方法 在log4j.xml中配置日志目标,如下所示:
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);
}
}
<appender name="file" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${catalina.home}/logs/uploader.log" />
<param name="append" value="false" />
<param name="threshold" value="info" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.StringMatchFilter">
<param name="StringToMatch" value="trackfile" />
<param name="AcceptOnMatch" value="true" />
</filter>
<filter class="org.apache.log4j.varia.DenyAllFilter" />
</appender>
使用上述方法记录您的信息:
logfile("your log message")
我一定是遗漏了什么。在上面的例子中,“fred”不是最终硬编码到LoggingFilter类中吗?我不能这样做,因为我不知道我在编译时寻找的用户名。问题是你从其他地方获取用户名。我假设它存储在会话的某个地方,您必须选择它。我改变了这个例子,让它更清楚。啊哈!我明白了。现在有道理了。谢谢但是,我们的应用程序使用log4j.properties文件,而不是log4j.xml文件。可以通过属性文件将筛选器附加到appender吗?我不这样认为,请参阅的答案。XML是受支持的配置格式。您仍然可以使用属性文件进行后期筛选。这是一篇有趣的文章,谢谢。但它并没有超越将条件检查硬编码到应用程序中的必要性。我不确定是否有办法解决这个问题?最好是检查谁扮演什么角色。然后检查角色,而不是直接用户名。我认为最好的方法是将链接中的技术与从属性文件中读取过滤器值相结合。这是,我承认,有点混乱,但它允许我在特别的基础上快速改变过滤器。这对于一个长期的解决方案来说很糟糕,但是对于一些快速而肮脏的调查来说,它会很好地完成工作。+1更健壮、更快的解决方案。只有
decise()
方法中的最后一行应该是returnneutral
。MDC.remove()是避免内存泄漏所必需的。当然,如果你的健康检查很少,这没什么大不了的;但是每次执行MDC.put()都会消耗一些内存,我认为不会自动回收这些内存。
logfile("your log message")