2011-12-09 52 views
24

帮我在辩论在这里.. :)即使使用slf4j,你应该保护你的日志吗?

这里http://www.slf4j.org/faq.html#logging_performance的SLF4J网站表示由于参数测井,录井卫兵是没有必要的。即而不是写:

if(logger.isDebugEnabled()) { 
    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); 
} 

你可以逃脱:

Object entry = new SomeObject(); 
logger.debug("The entry is {}.", entry); 

这真的是好,或者它招致了(虽然更低)创建传递给此跟踪方法的静态字符串的成本..?

回答

33

我会尽力把我的两分钱从另一个角度

究竟是什么参数化日志记录的好处?

你只是推迟toString()调用字符串连接,直到真正需要的,这是当你真的要记录消息。当特定的日志记录操作被禁用时,这优化了性能。如果不确定,请检查source code for SLF4J

参数化日志记录是否使守卫在所有情况下都毫无用处?

在哪些情况下会记录卫士是有用的?

当有其他潜在的昂贵的操作。

例如(在这个特殊的记录操作被禁用的情况下),如果我们有没有记录后卫

logger.debug("User: {}", getUserService().getCurrentUser()); 
  1. 我们将支付成本从obj = getUserService().getCurrentUser()
  2. 我们将节省成本来自"User name: " + obj.toString()

如果我们使用记录后卫

if (logger.isDebugEnabled()) { 
    logger.debug("User: {}", getUserService().getCurrentUser()); 
} 
  1. 我们将支付logger.isDebugEnabled()
  2. 成本,我们将节省成本从obj = getUserService().getCurrentUser()
  3. 我们将节省的成本从"User name: " + obj.toString()

在后面的c当启用此特定日志记录操作时,我们会以检查isDebugEnabled()两次的价格节省两项成本。

注意:这仅仅是一个例子,不是试图在这里辩论好/坏的做法。

+0

谢谢fgelz,这是最明确的解释,所以我改变了主意。让你认为,尽管有所有不同的框架,但在日志世界中仍然有创新的空间,在推迟昂贵的操作方面..也许与代表 –

+0

@MarkD JDK 8项目Lambda将帮助创新(http:// openjdk .java.net/projects/lambda /) – fglez

+0

你也应该知道调用可变参数方法(如logger.debug(String,Object ..)实际上是在封面logger.debug(String,new Object [所以如果你没有包装这些调用,那么你每次遍历这个方法时都会创建一个新的Object数组,即使你从来没有写过内容,如果你有一个高性能的系统或者经历内存压力,那么随着时间的推移,这可能会增加;如果使用了很多,logger.isDebugEnabled()调用将被一个体面的JIT所吸引,SLF4J专门为此提供了一个和两个参数,但是对于更多的参数流失 – AlBlue

2

请不要使用if语句,因为我每次看这样的代码

if (logger.isDebug()) { 
    logger.debug("Of course it's debug {}", sadFace); 
} 

我哭了。

我希望创建静态字符串的成本很低,对于99%的用户来说是微不足道的。

+1

slf4j不使用'{0}'构造。 –

+0

但是,如果您正在使用大量调试日志记录,那么大多数大型应用程序都会执行这些日志记录,但这可能会造成很大的开销。您正在为垃圾收集器创建更多垃圾,并且一些系统已经在该区域中具有足够的压力。如果你没有任何调用来获取你的参数,并且你只使用一个常量字符串作为参数字符串,那很好,直到有人出现并改变它做一些稍微不重要的事情。从“编码标准”的角度来看,要求if语句更容易。 – Dogs

15

写作和阅读所有这些if(logger.isDebugEnabled()) {}可能会花费尽可能多的时间,因为他们为您节省。

当然,调用日志方法不是免费的,但调用isDebugEnabled()的情况也是如此。因此,如果您使用此模式(因为日志记录框架将两次检查该级别),您将为每个活动的日志语句支付更多费用。

它也混乱了代码。

在实践中,我没有发现性能损失足够大。

如果日志是你太慢了,写一个无阻塞的appender是推动日志事件到队列中,而不只是几张支票,并使用一个后台线程来处理它们。

背景:标准appender都是同步的,因此在多线程应用程序中登录可能导致大量小暂停,其中所有线程都等待将日志消息写入文件。

+0

感谢这就是我的想法..将等待看到什么一般conscensus,虽然我给你一个加号:)我喜欢关于appenders的提示,这可能是我们的问题。 –

+0

“调用日志方法”和“调用isDebugEnabled()”可以**实际上免费**,我认为有一些JIT优化。我不确定,但如果是为sl4jf提供实际高性能实现的人员,我会在rutime中为禁用级别放置一个无操作方法的实现,以便JIT可以删除该调用。 –

4

有这样的记录语句的问题:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); 

的是,它会做很多工作来值连接成一个String,而如果调试日志关闭时则从未使用过。因此,在这种情况下,在执行此行之前检查调试日志记录是否有效。当你只是路过参数:

logger.debug("The entry is {}.", entry); 

那么它不会不必要地需要建立一个从未使用String,并检查是没有必要的;只是将参数传递给方法没有很高的开销。

请注意,如果您在日志记录语句中的参数表达式相对较高,那么首先检查日志记录级别可能仍然有好处。

+0

您可以通过将它们放入匿名类中的toString()方法来处理昂贵的表达式,以便在实际需要之前不对其进行评估。 –

+0

@ThorbjørnRavnAndersen是的,但这会使代码非常冗长。 – Jesper

5

由于字符串的创建,不使用警卫。

相反,它通常用于避免潜在的昂贵参数表达式,如entry[i].retrieveExtendedDebugInformation().formatNicely()。为此,logback确保仅在实际打印日志消息时才计算参数,而在调用debug()之前,log4j总是评估参数。

这里唯一的候选人是String.valueOf(entry[i]),这也不是很贵,所以你可以说这个守卫是完全不需要的。

+1

好的 - 这是有道理的,是对事物的另一种观点。所以在你看来,这是一个判断是否需要警惕,而守卫只应该用于相对较少(昂贵的)操作?..引入减少这些操作数量的参数。 –

+0

是的,并且取决于多昂贵操作常常只在跟踪循环内的语句时才有意义。人们甚至可以争辩说,如果你的调试语句太昂贵,甚至当你处于更高的日志级别时,它们会减慢课程速度,那么你不会使用log.debug,特别是,你可能正在执行while条件){log.debug(complicatedExpression); doSomething();}'而你应该做'log.debug(complicatedExpression);而(条件){log.trace(simpleExpression); doSomething的();}'。但我不是狂热者,我承认我经常写警卫。 – wallenborn

+0

“非常昂贵”的代价仍然很高,无法为拥有大量日志语句的程序带来不同。 –

相关问题