【问题标题】:Enclosing calls to debug() in if isDebugEnabled(): a good policy?在 if isDebugEnabled() 中包含对 debug() 的调用:一个好的策略?
【发布时间】:2009-03-17 09:07:12
【问题描述】:

我们的团队有这样的日志记录政策

if (LOGGER.isDebugEnabled()) {  
  LOGGER.debug("model[" + model + "]");
}

而不是像这样简单地调用日志记录方法:

LOGGER.debug("model[" + model + "]");

这种做法能够导致一些performance improvement,但另一方面它使代码库更加复杂。 我们的应用程序没有性能问题,可能永远不会,引入该策略的理由很简单,因为它是一个很好的实践,所以每次我们做日志时都应该使用它。

你认为这是一个好政策吗?

【问题讨论】:

    标签: java performance logging coding-style


    【解决方案1】:

    您应该使用 SLF4J 并将 log4j 作为您的实现。使用 SLF4J,您可以使用参数化消息完全消除 isDebugEnabled()

    section about logging performance in the slf4j FAQ

    以下两行将产生完全相同的输出。但是,在禁用日志记录语句的情况下,第二种形式的性能将比第一种形式至少高出 30 倍。

    logger.debug("The new entry is " + entry + ".");

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

    【讨论】:

    • 当参数构建 entry 的成本很高时,字符串格式没有帮助。例如。考虑一些logger.debug("The new entry is {}.", getEntry());,您不知道getEntry() 的成本有多大。
    【解决方案2】:

    对于那些您正在构建的日志字符串实际上会显着影响性能的情况,这是一个很好的策略。这可能有两个原因:

    • 构建字符串需要做很多工作(例如,它必须进行查找,或者从许多非常小的片段构建一个非常大的字符串)
    • 它处于一个循环中,没有做太多其他工作(但经常被调用),因此即使是一个简单的日志字符串也花费在构建时间的比例更高

    这不应该是一揽子规则,我希望这两种情况相对很少见。

    当然,我们真正想要的是能够说,“调用调试方法,传入一个参数,该参数不是调试字符串本身,而是知道如何构建 调试字符串,当且仅当它是必要的。”如果没有简洁的闭包,这在 Java 中会很丑陋。 (即使在具有 lambda 表达式形式的闭包的语言中,捕获相关变量在某些情况下也可能很重要,具体取决于语言处理捕获的方式。)

    【讨论】:

    • SLF 的日志方法中没有字符串格式化选项吗?
    • 我一直提倡一直使用这条规则,因为开发者不需要考虑构建字符串(参数)的成本。有时,需要为调试日志构建额外的参数,例如getEntryDebugString()。最好多写几行代码和容易出错的代码,而不是为罕见的问题感到抱歉。
    【解决方案3】:

    我同意Michael A. Jackson的名言:

    程序优化的第一条规则:不要这样做。

    程序优化的第二条规则 - 仅适用于专家:暂时不要这样做。

    我认为,在大多数情况下,如果不确定性能提升是否显着/显着,就不值得让代码库变得更复杂。

    我知道性能改进,但我认为个人程序员有责任决定在特定情况下是否值得添加这些额外的代码行。在大多数情况下,额外的行只是增加了复杂性,而在性能方面没有任何明显的提升。

    【讨论】:

    • 很明显,如果作者问了这个问题,那就是在呼吁他进行优化。 IE。当你被要求优化一段代码时,你不要用上面的两个引号来回答。了解您来自哪里,但在这种情况下无济于事。
    • -1 来自我。我讨厌这个“不做优化”的口头禅。它存在严重缺陷 - 如果您的优化产生结果(甚至是微不足道的)并且它几乎是免费的(因为这样,if 语句是微不足道的)并且它并不复杂(并且在 if 中包含日志语句只是最简单的)然后继续做吧。
    • 不同意,这是为了避免麻烦而进行的清单活动,可以很容易地传达给初级开发人员,这样他们就不会意外引入某人不得不花费的错误稍后调试。
    【解决方案4】:

    我无法在网上找到参考资料(也许我在一本书中读到过),但有一个 BIOS 调用示例将字符串写入屏幕。

    检查将字符串写入屏幕的代码以确保字符串不会离开屏幕,然后调用函数将字符写入屏幕。

    检查了将字符写入屏幕的函数,以确保字符不会被从屏幕上写入。

    从将字符写入屏幕的函数中删除检查代码会大大提高速度。这是因为打印每个字符经常发生。他们还提供了一种方法,用于检查调用前未检查屏幕位置的情况。

    因此,如果您可以在高级别进行检查并避免在低级别进行检查,您可以显着加快速度。

    在您提供的情况下,如果代码处于循环中或者如果有很多日志记录语句,那么您将有明显的好处:

    • 删除字符串创建(以及相关的 GC)
    • 减少 if 语句的数量(尽管热点很可能会优化它们)。

    我想说,一般来说,如果有一个简单的解决方法,您不应该添加任何已知成本高昂的东西。这显然属于那种情况 - 添加 if 语句并不难,但稍后再添加会很痛苦,而且它提供了明确的速度/内存改进。

    我认为这与过早优化之间的区别在于,众所周知,这总是代价高昂,所以问题归结为您是否要添加到程序中的累积成本?成本几乎是恒定的,并且在编写代码时就知道了。

    【讨论】:

      【解决方案5】:

      我认为在编写日志消息之前调用 isDebugEnabled() 是有意义的。 之后您可以轻松地更新您的日志消息。 我们使用以下代码使日志消息更加灵活。

      if (LOGGER.isDebugEnabled()) {  
        String msg = "A Message {0} you can put {1} differet Objects in {2}"; //$NON-NLS-1$
        Object[] args = new Object[] {file1.toString(),new Integer(23),anObject};
        LOGGER.debug(MessageFormat.format(msg,args));
      }
      

      如果你没有 if 语句,你会浪费时间。请记住...您的项目变得更大,您的日志消息也变得更大。

      日志消息不应减慢您的代码速度!

      【讨论】:

      【解决方案6】:

      如果“模型”是已知的,那么仅仅记录它并不是那么昂贵。 但是,如果只为如下所示的日志记录而获取“模型”,则可能会影响简单性。

      LOGGER.debug("model[" + proxy.getModel() + "]");
      

      【讨论】:

        猜你喜欢
        • 2012-06-14
        • 2013-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-26
        相关资源
        最近更新 更多