Logger slf4j的优点是使用{}格式化而不是字符串连接


100

使用{}代替字符串串联有什么好处吗?

slf4j的示例

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

代替

logger.debug("Temperature set to"+ t + ". Old temperature was " + oldT);

我认为这与速度优化有关,因为可以根据配置文件在运行时避免参数评估(和字符串连接)。但是只有两个参数是可能的,因此有时除了字符串串联外别无选择。需要对此问题的看法。

Answers:


74

关于字符串连接性能。如果您有密集的日志记录语句,这可能会很重要。

(SLF4J 1.7之前)但只能有两个参数

因为绝大多数的日志记录语句具有2个或更少的参数,所以SLF4J API最高版本为1.6涵盖(仅)大多数用例。从API版本1.7开始,API设计人员就提供了带有varargs参数的重载方法。

对于那些需要大于2且使用1.7之前的SLF4J的情况,则只需使用字符串串联或即可new Object[] { param1, param2, param3, ... }。它们应该很少,以至于性能并不那么重要。


2
应避免使用未使用的字符串连接(即调试语句)。请使用(过于冗长但有效的)日志级别检查或(较小的开销,但可能开销较小)对象数组参数。(在所有条件相同的情况下,我更希望后者。)很难说字符串concat的重要性并不重要/不会影响性能。从理论上讲,对象数组的创建可以进行内联和优化,并且“确实”不会有所作为(与一厢情愿的想法相比)。(这不是过早的优化,这只是第一次做对/更好的事情。)
michael

为什么不对System.out.println()进行重载修改以使其类似于slf4j的记录器,从而避免字符串连接?
a3.14_Infinity 2014年

44

简短版:是的,它更快,代码更少!

字符串连接在不知道是否需要字符串连接的情况下做了很多工作(log4j中已知传统的“已启用调试”测试),并且应尽可能避免,因为{}允许延迟toString()调用和字符串构造确定事件是否需要捕获之后的时间。我认为,通过使记录器格式化单个字符串,代码将变得更加简洁。

您可以提供任意数量的参数。请注意,如果您使用的是sljf4j的旧版本,并且具有多个参数{},则必须使用new Object[]{a,b,c,d}语法来传递数组。参见例如http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String,java.lang.Object [])

关于速度:Ceki在其中一个列表上发布了基准测试。


6
注意:最新的javadoc显示了更新的var-arg语法debug(String format, Object... arguments)。参见slf4j.org/faq.html#logging_performance
michael

之所以推荐,是因为除了串联性能之外还提到了.toString()评估。这是在记录器内部发生的事情,记录器可以决定是否有必要调用该方法。如果不满足日志记录级别栏,则不会。
Chetan Narsude

6

由于String 在Java中是不可变的,因此对于每对串联,必须将左右String复制到新String中。因此,最好选择占位符。


2
如果只有一对,这是正确的,但通常是不正确的,因为编译器将串联转换为字符串生成器调用,从而导致执行速度更快的代码,并且分配的次数不多。
cdeszaq

3

另一个选择是String.format()。我们在jcabi-log(slf4j周围的静态实用程序包装器)中使用它。

Logger.debug(this, "some variable = %s", value);

它更具可维护性和可扩展性。此外,它很容易翻译。


3
我不认为它更容易维护。如果value更改类型,则必须返回并更改日志记录语句。IDE不能帮您的忙。记录器应协助调试,不要妨碍调试。:-)
Chetan Narsude

3
@ChetanNarsude IntelliJ 2016至少会告诉我何时格式字符串不适合格式参数。例如:String.format("%d", "Test")产生IntelliJ警告Argument type 'String' does not match the type of the format specifier '%d'.。但是,我不确定在使用上述解决方案时它是否仍然能够提供这种智能响应。
2016年

这是什么速度?
托尔比约恩Ravn的安徒生

@ThorbjørnRavnAndersen里面很原始,但是当然比静态记录器要慢
yegor256 '17

包装slf4j?这是否违反了使用slf4j的目的?另外,我已经看到很多人滥用String.format以便在评估日志级别之前将字符串格式化,例如:logger.info(String.format(“ hello%s”,用户名))。
Juan Bustamante

2

我认为从作者的角度来看,主要原因是为了减少字符串连接的开销。我刚刚阅读了记录器的文档,您会发现以下几句话:

/**
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the DEBUG level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before 
  invoking the method,
* even if this logger is disabled for DEBUG. The variants taking
* {@link #debug(String, Object) one} and {@link #debug(String, Object, Object) two}
* arguments exist solely in order to avoid this hidden cost.</p>
*/
*
 * @param format    the format string
 * @param arguments a list of 3 or more arguments
 */
public void debug(String format, Object... arguments);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.