【发布时间】:2010-02-09 15:29:47
【问题描述】:
我们知道我们可以通过其属性/配置文件配置 log4j 以关闭特定位置(Java 中的类或包)的日志。我的问题如下:
- log4j 实际上对这些标志做了什么?
- log4j 中的日志语句是否仍然被调用,但由于该标志而没有被写入文件或控制台?那么还有性能影响吗?
- 是不是像 C++ 中的#ifdef 在编译时生效然后可以限制性能影响?
谢谢,
【问题讨论】:
我们知道我们可以通过其属性/配置文件配置 log4j 以关闭特定位置(Java 中的类或包)的日志。我的问题如下:
谢谢,
【问题讨论】:
是的,日志语句仍将被执行。这就是为什么首先检查日志级别是一个很好的模式:类似于
if (log.isInfoEnabled()) {
log.info("My big long info string: " + someMessage);
}
这是为了避免在日志级别不支持INFO 语句时为信息String 重新分配空间。
它不像 #ifdef - #ifdef 是编译器指令,而 Log4J 配置是在运行时处理的。
编辑:我讨厌因为无知而被降级,所以这里有一篇文章支持我的回答。
来自http://surguy.net/articles/removing-log-messages.xml:
在 Log4J 中,如果您在 DEBUG 级别和当前 Appender 设置为仅记录 INFO 的消息 级别及以上,则消息将 不显示。表现 调用 log 方法的惩罚 本身是最小的 - 几纳秒。 但是,可能需要更长的时间 评估日志的参数 方法。例如:
logger.debug("大对象是 "+largeObject.toString());
评估 largeObject.toString() 可能 慢一点,它在之前被评估过 对记录器的调用,所以记录器 不能阻止它被评估, 即使它不会被使用。
编辑 2:来自 log4j 手册本身 (http://logging.apache.org/log4j/1.2/manual.html):
用户应注意以下性能问题。
关闭日志记录时的日志记录性能。 当日志完全关闭或仅关闭一组级别时,日志请求的成本包括方法调用和整数比较。在 233 MHz Pentium II 机器上,此成本通常在 5 到 50 纳秒范围内。
但是,方法调用涉及参数构造的“隐藏”成本。
比如对于一些logger cat,写,
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
会产生构造消息参数的成本,即将整数 i 和 entry[i] 转换为字符串,并连接中间字符串,无论是否记录消息。参数构造的成本可能相当高,并且取决于所涉及参数的大小。
为了避免参数构造成本写:
if(logger.isDebugEnabled() {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
如果禁用调试,则不会产生参数构造成本。另一方面,如果记录器启用了调试,则评估记录器是否启用会产生两倍的成本:一次在 debugEnabled 中,一次在调试中。这是一个微不足道的开销,因为评估记录器大约需要实际记录所需时间的 1%。
【讨论】:
我运行了一个简单的基准测试。
for (int j = 0; j < 5; j++) {
long t1 = System.nanoTime() / 1000000;
int iterations = 1000000;
for (int i = 0; i < iterations; i++) {
int test = i % 10;
log.debug("Test " + i + " has value " + test);
}
long t2 = System.nanoTime() / 1000000;
log.info("elapsed time: " + (t2 - t1));
long t3 = System.nanoTime() / 1000000;
for (int i = 0; i < iterations; i++) {
int test = i % 10;
if (log.isDebugEnabled()) {
log.debug("Test " + i + " has value " + test);
}
}
long t4 = System.nanoTime() / 1000000;
log.info("elapsed time 2: " + (t4 - t3));
}
elapsed time: 539
elapsed time 2: 17
elapsed time: 450
elapsed time 2: 18
elapsed time: 454
elapsed time 2: 19
elapsed time: 454
elapsed time 2: 17
elapsed time: 450
elapsed time 2: 19
对于 1.6.0_18,这让我感到惊讶,因为我原以为内联会阻止这种情况发生。也许带有逃逸分析的 Java 7 会。
但是我仍然不会将调试语句包装在 if 子句中,除非半微秒级的时间改进变得很重要!
【讨论】:
log4j 将处理日志语句,并检查特定记录器是否在特定日志记录级别启用。如果不是,则不会记录该语句。
这些检查比实际写入磁盘(或控制台)的成本要低得多,但它们仍然会产生影响。
不,java 没有 #ifdef 这样的概念(反正开箱即用,有 java 预编译器)
【讨论】: