【问题标题】:Logger slf4j advantages of formatting with {} instead of string concatenationLogger slf4j 使用 {} 格式化而不是字符串连接的优势
【发布时间】:2012-05-20 06:56:34
【问题描述】:

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

一个来自 slf4j 的例子

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

而不是

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

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

【问题讨论】:

    标签: optimization logging concatenation string-formatting slf4j


    【解决方案1】:

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

    (在 SLF4J 1.7 之前)但只能使用两个参数

    因为绝大多数日志语句都有 2 个或更少的参数,所以 SLF4J API 直到 1.6 版(仅)涵盖了大多数用例。 API 设计者从 API 1.7 版开始提供了带有可变参数的重载方法。

    如果您需要超过 2 个并且您被 1.7 之前的 SLF4J 卡住,那么只需使用字符串连接或 new Object[] { param1, param2, param3, ... }。它们的数量应该足够少,以至于性能不那么重要。

    【讨论】:

    • 应避免未使用的字符串连接(即调试语句)。请务必使用(过于冗长但高效)日志级别检查或(更小,但可能开销较小)对象数组参数。 (我更喜欢后者,所有条件都相同。)很难说字符串 concat 不重要/不会影响性能。理论上,对象数组的创建可以被内联和优化,并且“真的”没有什么不同(与一厢情愿的想法相比)。 (这不是过早的优化,只是第一次做正确/更好的事情。)
    • 为什么不为 System.out.println() 进行重载修改以遵循类似于 slf4j 的记录器,从而避免字符串连接?
    【解决方案2】:

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

    字符串连接在不知道是否需要的情况下做了很多工作(从 log4j 已知的传统“是否启用调试”测试),如果可能的话应该避免,因为 {} 允许延迟 toString() 调用并在确定事件是否需要捕获之后进行字符串构造。通过将记录器格式化为 single 字符串,我认为代码变得更清晰。

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

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

    【讨论】:

    • 注意:最新的 javadoc 显示了较新的 var-arg 语法 debug(String format, Object... arguments)。见slf4j.org/faq.html#logging_performance
    • 赞成,因为除了连接性能之外还提到了 .toString() 评估。这是在记录器内部发生的事情,记录器可以决定是否有必要调用该方法。如果不满足日志记录级别栏,则不会。
    【解决方案3】:

    由于 String 在 Java 中是不可变的,所以每对连接都必须将左右 String 复制到新的 String 中。所以,最好选择占位符。

    【讨论】:

    • 如果只有一对,这是正确的,但通常是不正确的,因为编译器会将串联转换为字符串构建器调用,从而导致代码更快,但分配不多。
    【解决方案4】:

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

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

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

    【讨论】:

    • 我不认为它更易于维护。如果value 的类型发生变化,您还必须返回并更改日志记录语句。 IDE 无法帮助您解决的问题。记录器应协助调试,而不是妨碍调试。 :-)
    • @ChetanNarsude IntelliJ 2016 至少告诉我格式字符串何时不适合格式参数。例如:String.format("%d", "Test") 产生 IntelliJ 警告 Argument type 'String' does not match the type of the format specifier '%d'.。不过,我不确定在使用上述解决方案时它是否仍然能够提供这种智能响应。
    • 这个速度是多少?
    • @ThorbjørnRavnAndersen 它内部非常原始,但当然比静态记录器慢
    • 包装 slf4j?这不是违背了使用 slf4j 的目的吗?另外,我看到很多人滥用 String.format 使得字符串在评估日志级别之前被格式化,例如:logger.info(String.format("hello %s", username))。
    【解决方案5】:

    我认为从作者的角度来看,主要是为了减少字符串连接的开销。我刚刚阅读了记录器的文档,您可以找到以下文字:

    /**
    * <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);
    

    【讨论】:

      【解决方案6】:

      合规日志记录对于应用程序开发非常重要,因为它会影响性能。

      所提到的不合规日志记录会导致每次调用时都会调用冗余的 toString() 方法,并且会导致冗余的临时内存分配和 CPU 处理,如大规模测试示例所示执行,我们可以查看冗余分配的临时内存:

      查看方法分析:

      注意:我是这篇博文的作者,Logging impact on application performance

      【讨论】:

        猜你喜欢
        • 2020-11-27
        • 2011-06-12
        • 2017-05-19
        • 1970-01-01
        • 1970-01-01
        • 2015-08-15
        • 1970-01-01
        • 2013-09-24
        相关资源
        最近更新 更多