Eclipse - Sonar S2629 可能误报新字符串

Eclipse - Sonar S2629 possible false positive with new String

我正在使用最新的 Eclipse 和 Sonar 插件

answer 中用于记录的是以下行:

log.debug("Request body: {}", new String(body, "UTF-8"));

只有在 DEBUG 级别才应该创建字符串:

/**
 * Log a message at the DEBUG level according to the specified format
 * and argument.
 * <p/>
 * <p>This form avoids superfluous object creation when the logger
 * is disabled for the DEBUG level. </p>
 *
 * @param format the format string
 * @param arg    the argument
 */
public void debug(String format, Object arg);

但 Sonar 将其标记为 squid:S2629:

"Preconditions" and logging arguments should not require evaluation (squid:S2629)

并给出串联的例子

logger.log(Level.DEBUG, "Something went wrong: " + message); // Noncompliant; string concatenation performed even when log level too high to show DEBUG messages

这是误报声纳警告还是我遗漏了什么?

这不是 的副本,后者通常询问规则概念,即连接,但不是通过创建对象格式化为 new String

另外 link 的一个答案说创建 new Date() 不会造成内置格式的问题:

public static void main(String[] args) {
    LOGGER.info("The program started at {}", new Date());
}

}

Logging this way, you avoid performance overhead for strings concatenation when nothing should be actually logged.

在非调试模式下行

log.debug("Request body: {}", new String(body, "UTF-8"));

而不是

log.debug(MessageFormatter.format("Request body: {}", new String(body, "UTF-8")));

避免创建通过 MessageFormatter.format(String messagePattern, Object arg) 创建的字符串,但不会创建由 new String(body, "UTF-8") 创建的另一个字符串。

这意味着它不是误报,因为参数是在调用日志记录方法之前先计算的。

只要SLF4J does not support lambda expression to lazy evaluate arguments,可以使用以下实用方法作为解决方法:

private static Object lazyToString(final Supplier<String> stringSupplier) {
    return new Object() {
        @Override
        public String toString() {
            return stringSupplier.get();
        }
    };
}

这个可以用来限制字节数组转字符串只能是DEBUG模式:

log.debug("Request body: {}", lazyToString(() -> new String(body, StandardCharsets.UTF_8)));

虽然使用 lambda 或惰性 lambda 很好,但仍然有很好的旧 isDebugEnabled 方式:

if (log.isDebugEnabled()) {
  log.debug("Request body: {}", new String(body, StandardCharsets.UTF_8));
}

这不会修复正在创建的 String(因为您毕竟想要显示它),但在禁用调试模式时不会消耗内存。