将logger.debug(“message:”+ text)转换为logger.debug(message:{}“,text)

时间:2014-02-12 03:48:26

标签: java logging slf4j logback

我正在尝试找到解决使用以下格式代码导致的冗余字符串连接问题的最佳方法:

logger.debug("Entering loop, arg is: " + arg) // @1

在大多数情况下,logger.level高于debugarg.toString()和字符串连接是用户进行cpu循环并暂时耗尽内存的浪费。

在引入varargs之前,推荐的方法是首先测试记录器级别:

if (logger.isDebugEnabled())    
  logger.debug("Entering loop, arg is: " + arg);  // @2

但现在首选的形式是

  logger.debug("Entering loop, arg is: {}", arg); // @3

在脚本中为每个logger.debug添加if (logger.isDebugEnabled())(以及其他方法的等价物)前缀并不是很困难,但是我正在尝试找到转换它的最佳方法第一种形式到第三种形式。

有什么建议吗?挑战是在格式字符串中插入正确的数字括号对{}。我希望logback会在最后添加占位符未涵盖的剩余参数,但我找不到它所做的参考。

作为替代方案,我打算写一个class Concatenator粘贴在最后并将第一个表单转换为

logger.debug(new Concatenator("Entering loop, arg is: ", arg)); // @4

Concatenator类会将调用延迟到arg.toString()并将字符串连接延迟到logger调用toString(),从而避免两者都记录到记录器处于更高的过滤级别。它确实增加了创建Object[]Concatenator的开销,但这应该比替代方案便宜。

问题:

  • 我认为这种转化(@1->@4 - 将+替换为,并附在new Contatenator(...)中)要容易得多。我有什么遗失的东西吗?
  • 我是否更正确@4@1好多了?
public class Concatenator {
    final Object[] input;
    String output;
    public Concatenator(Object... input) {
        this.input = input;
    }
    public String toString() {
        if (output == null) {
            StringBuffer b = new StringBuffer();
            for (Object s : input) b.append(s.toString());
            output = b.toString();
        }
        return output;
    }
    public static void main(String args[]) {
        new Concatenator("a", "b", new X());
        System.out.println(new Concatenator("c", "d", new X()));
    }
}
class X {
    public String toString() {
        System.out.println("X.toString");
        return super.toString();
    }
}

1 个答案:

答案 0 :(得分:1)

不幸的是,你的方法不会改变任何东西。实际上,它引入了一个额外的对象实例化/分配(您的Concatenator)。您还使用了StringBuffer,这会引入您不需要的同步开销。

问题是SLF4J Logger.debug()调用的方法签名。第一个参数始终是String。这意味着你将不得不打电话:

logger.debug(new Concatenator("Entering loop, arg is: ", arg).toString());

这意味着......你正在做与Java将要做的完全相同的事情,但是有更多的开销。

Java编译器通过创建+来处理字符串连接运算符(StringBuilder),并在Concatenator上的toString()类中完成您正在执行的操作。

logger.debug("Entering loop, arg is: " + arg);

变为:

logger.debug(new StringBuilder()
                 .append("Entering loop, arg is: ")
                 .append(arg).toString());

(如果使用javap查看生成的字节码,您会看到情况。)

因此,您目前的方法将比现在的方法更昂贵。

编辑:因此,您可以通过以下方式完成此工作......

logger.debug("{}", new Concatenator("Entering loop, arg is: ", arg));

这样,除非记录器需要,否则Concatenator将作为Object传递并且其toString()未被调用。另外,将StringBuffer替换为StringBuilder

如果我没有直接回答你的问题......这比原来更好吗? 可能;除非需要,否则不会发生字符串连接。但是,您正在引入对象实例化/分配。唯一能够看到差异的真正的方式是分析它/写一个基准。