简体   繁体   English

将logger.debug(“消息:” +文本)转换为logger.debug(消息:{}”,文本)

[英]Convert logger.debug(“message: ” + text) to logger.debug(message: {}", text)

I am trying to find the best way to address the issue of redundant string concatenation caused by using code of the following form: 我正在尝试找到最好的方法来解决由于使用以下形式的代码而导致的冗余字符串串联的问题:

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

In most cases the logger.level is higher than debug and the arg.toString() and the string concatenation are a waste that user up cpu cycles and briefly use up memory. 在大多数情况下, logger.level高于debug ,而arg.toString()和字符串连接是浪费用户的CPU周期并短暂地占用内存。

Before the introduction of varargs the recommended approach was to test the logger level first: 在引入varargs之前,推荐的方法是首先测试记录器级别:

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

But now the preferred form is 但是现在首选形式是

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

It is not very difficult to prefix each logger.debug with if (logger.isDebugEnabled()) (and its equivalent for the other methods) in a script, but I am trying to find the best way to convert the first form to the third. 这不是很困难的前缀每个logger.debugif (logger.isDebugEnabled())及其等价物的其他方法)的脚本,但我想找到的第一种形式转换到第三的最佳方式。

Any suggestions? 有什么建议么? The challenge is to insert the correct number brace pairs {} in the format string. 面临的挑战是在格式字符串中插入正确的数字大括号对{} I wish logback would append the remaining arguments not covered by the placeholder at the end but I cannot find a reference that it does that. 我希望logback会在末尾附加占位符未涵盖的其余参数,但我找不到能做到这一点的引用。

As an alternative, I am thinking to write a class Concatenator as pasted at end and convert the first form to 作为替代方案,我正在考虑编写最后粘贴的class Concatenator并将第一种形式转换为

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

The Concatenator class delays the call to arg.toString() and string concatenation until the logger calls toString() , thereby avoiding both if the logger is at a higher filter level. Concatenatorarg.toString()和字符串串联的调用延迟,直到logger调用toString()为止,从而避免了两者(如果记录器处于更高的过滤器级别)。 It does add the overhead of creating an Object[] and a Concatenator but that should be cheaper than the alternative. 它确实增加了创建Object[]Concatenator的开销,但是比其他方法要便宜。

Questions: 问题:

  • I think this conversion ( @1->@4 -- replace + with , and enclose in new Contatenator(...) ) is much easier. 我认为这种转换( @1->@4+替换+ ,并包含在new Contatenator(...) )要容易得多。 Is there something I am missing? 我有什么想念的吗?
  • Am I correct that @4 is much better than @1 ? 我正确@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();
    }
}

Unfortunately your approach isn't going to change anything. 不幸的是,您的方法不会改变任何事情。 In fact, it introduces an additional object instantiation/allocation (your Concatenator ). 实际上,它引入了附加的对象实例化/分配(您的Concatenator )。 You're also using StringBuffer which introduces synchronization overhead you don't need. 您还使用StringBuffer引入了不需要的同步开销。

The problem is the method signature for SLF4J's Logger.debug() calls. 问题是SLF4J的Logger.debug()调用的方法签名。 The first argument is always a String . 第一个参数始终是String This means you're going to have to call: 这意味着您将必须致电:

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

which means ... you're doing exactly the same thing as Java is going to do, but with more overhead. 这意味着...您正在做与Java完全相同的事情,但是开销更多。

The Java compiler handles the String concatenation operator ( + ) by creating a StringBuilder and doing exactly what you're doing in your Concatenator class on toString() . Java编译器通过创建StringBuilder并完全在toString() Concatenator类中进行操作来处理String串联运算符( + toString()

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

becomes: 变为:

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

(If you use javap to look at the generated bytecode, you'll see that's the case.) (如果您使用javap查看生成的字节码,则会看到这种情况。)

So, your current approach is going to be more expensive than what you have now. 因此,您当前的方法将比现在昂贵。

Edit: So, the way you could make this work is by doing ... 编辑:所以,您可以使这项工作的方法是通过...

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

This way your Concatenator is passed as an Object and its toString() not called unless the logger needs to. 这样,除非记录器需要,否则您的Concatenator作为Object传递,并且不调用其toString() Also, replace the StringBuffer in your class with StringBuilder . 另外,用StringBuffer替换类中的StringBuilder

And if I didn't answer your question directly ... is this better than the original? 而且,如果我没有直接回答您的问题……这比原来的要好吗? Probably ; 大概 ; The string concatenation isn't occurring unless it needs to. 除非需要,否则不会发生字符串串联。 You are, however, introducing an object instantiation/allocation. 但是,您将引入对象实例化/分配。 The only real way to see the differences would be to profile it / write a benchmark. 观察差异的唯一真实方法是剖析差异/编写基准。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM