内置字符串格式与字符串连接作为日志参数

新手上路,请多包涵

我正在使用 SonarLint ,它在下一行中向我显示了一个问题。

 LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

旁注:包含此行的方法可能会经常被调用。

这个问题的描述是

“先决条件”和日志记录参数不应要求评估(鱿鱼:S2629)

将需要进一步评估的消息参数传递到 Guava com.google.common.base.Preconditions 检查中可能会导致性能下降。这是因为无论是否需要它们,每个参数都必须在实际调用方法之前解析。

同样,将连接的字符串传递到日志记录方法中也会导致不必要的性能损失,因为每次调用该方法时都会执行连接,无论日志级别是否低到足以显示消息。

相反,您应该构建代码以将静态或预先计算的值传递到 Preconditions 条件检查和记录调用中。

具体来说,应该使用内置的字符串格式而不是字符串连接,如果消息是方法调用的结果,则应该完全跳过前提条件,而是有条件地抛出相关异常。

不合规代码示例

> logger.log(Level.DEBUG, "Something went wrong: " + message);  // Noncompliant; string concatenation performed even when log level too high to show DEBUG messages
>
> LOG.error("Unable to open file " + csvPath, e);  // Noncompliant
>
> Preconditions.checkState(a > 0, "Arg must be positive, but got " + a); // Noncompliant. String concatenation performed even when a > 0
>
> Preconditions.checkState(condition, formatMessage());  //Noncompliant. formatMessage() invoked regardless of condition
>
> Preconditions.checkState(condition, "message: %s", formatMessage()); // Noncompliant
>
> ```
>
> 合规解决方案
>
> ```
> logger.log(Level.SEVERE, "Something went wrong: %s", message);  // String formatting only applied if needed
>
> logger.log(Level.SEVERE, () -> "Something went wrong: " + message); //since Java 8, we can use Supplier , which will be evaluated lazily
>
> LOG.error("Unable to open file {}", csvPath, e);
>
> if (LOG.isDebugEnabled() {   LOG.debug("Unable to open file " + csvPath, e);  // this is compliant, because it will not evaluate if log level is above debug. }
>
> Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a);  // String formatting only applied if needed
>
> if (!condition) {   throw new IllegalStateException(formatMessage()); // formatMessage() only invoked conditionally }
>
> if (!condition) {   throw new IllegalStateException("message: " + formatMessage()); }
>
> ```

我不是 100% 确定我是否理解正确。那么为什么这真的是一个问题。特别是关于使用字符串连接时性能下降的部分。因为我经常读到字符串连接比格式化它更快。

**编辑:** 也许有人可以向我解释两者之间的区别

LOGGER.debug(“Comparing objects: ” + object1 + “ and ” + object2);

LOGGER.debug(“Comparing objects: {} and {}”,object1, object2);

”`

在后台。因为我认为 String 会在传递给方法之前创建。正确的?所以对我来说没有区别。但显然我错了,因为 SonarLint 正在抱怨它

原文由 Naxos84 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 637
2 个回答

我相信你在那里有答案。

连接是在条件检查之前计算的。因此,如果您有条件地调用您的日志记录框架 10K 次并且所有这些都评估为 false,那么您将无缘无故地连接 10K 次。

另请检查 此主题。并查看 Icaro 的回答评论。

也看看 StringBuilder

原文由 luso 发布,翻译遵循 CC BY-SA 3.0 许可协议

考虑以下日志记录语句:

 LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

这是什么“调试”?

这是日志语句的级别,而不是 LOGGER 的级别。看,有 2 个级别:

a) 日志语句之一(此处为调试):

 "Comparing objects: " + object1 + " and " + object2

b) 一个是LOGGER的级别。那么,什么是 LOGGER 对象的级别:这也必须在代码或某些 xml 中定义,否则它从它的祖先那里获取级别。

现在我为什么要说这一切?

现在,当且仅当:

 Level of logging statement >= Level of LOGGER defined/obtained from somewhere in the code

级别的可能值可以是

DEBUG < INFO <  WARN < ERROR

(根据日志记录框架,可以有更多)

现在让我们回到问题:

 "Comparing objects: " + object1 + " and " + object2

即使我们发现上面解释的“级别规则”失败,也总是会导致创建字符串。

然而,

 LOGGER.debug("Comparing objects: {} and {}",object1, object2);

如果满足“上面解释的级别规则”,只会导致字符串形成。

那么哪个更聪明呢?

查阅此 网址

原文由 Number945 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题