Java 将logger.debug(“消息:”文本)转换为logger.debug(“消息:{}”,文本)
我正试图找到解决因使用以下形式的代码而导致的冗余字符串连接问题的最佳方法:Java 将logger.debug(“消息:”文本)转换为logger.debug(“消息:{}”,文本),java,logging,slf4j,logback,Java,Logging,Slf4j,Logback,我正试图找到解决因使用以下形式的代码而导致的冗余字符串连接问题的最佳方法: logger.debug("Entering loop, arg is: " + arg) // @1 在大多数情况下,logger.level高于debug,arg.toString()和字符串串联是一种浪费,会占用大量cpu周期并短暂占用内存 在引入varargs之前,建议首先测试记录器级别: if (logger.isDebugEnabled()) logger.debug("Entering loo
logger.debug("Entering loop, arg is: " + arg) // @1
在大多数情况下,logger.level
高于debug
,arg.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会在末尾附加占位符未包含的其余参数,但我找不到这样做的引用
作为替代方案,我正在考虑编写一个粘贴在末尾的类连接器
,并将第一个表单转换为
logger.debug(new Concatenator("Entering loop, arg is: ", arg)); // @4
Concatenator
类延迟对arg.toString()
的调用和字符串连接,直到logger
调用toString()
,从而在记录器处于更高的过滤器级别时避免这两种情况。它确实增加了创建对象[]
和连接器的开销,但这应该比其他方法便宜
问题:
- 我认为这种转换(
@1->@4
--用,
替换+
,并用新的Contatenator(…)
括起来)要容易得多。我有什么遗漏吗
- 我认为
@4
比@1
好得多,对吗
公共类连接器{
最终对象[]输入;
字符串输出;
公共连接器(对象…输入){
这个输入=输入;
}
公共字符串toString(){
if(输出==null){
StringBuffer b=新的StringBuffer();
对于(对象s:input)b.append(s.toString());
输出=b.toString();
}
返回输出;
}
公共静态void main(字符串参数[]){
新的串联器(“a”,“b”,新的X());
System.out.println(新的串联器(“c”、“d”,新的X());
}
}
X类{
公共字符串toString(){
System.out.println(“X.toString”);
返回super.toString();
}
}
不幸的是,您的方法不会改变任何事情。事实上,它引入了一个额外的对象实例化/分配(您的连接器)。您还使用了StringBuffer
,这会引入您不需要的同步开销
问题是SLF4J的Logger.debug()
调用的方法签名。第一个参数始终是字符串
。这意味着你必须打电话:
logger.debug(new Concatenator("Entering loop, arg is: ", arg).toString());
也就是说。。。您所做的事情与Java将要做的事情完全相同,但开销更大
Java编译器通过创建一个StringBuilder
并在toString()
上的Concatenator
类中执行操作,来处理字符串连接运算符(+
)
变成:
logger.debug(new StringBuilder()
.append("Entering loop, arg is: ")
.append(arg).toString());
(如果您使用javap
查看生成的字节码,就会发现情况就是这样。)
因此,您当前的方法将比您现在的方法更昂贵
编辑:因此,您可以通过以下方式完成此工作:
logger.debug("{}", new Concatenator("Entering loop, arg is: ", arg));
这样,您的连接器
将作为对象传递,除非记录器需要,否则不会调用它的toString()
。另外,将类中的StringBuffer
替换为StringBuilder
如果我不直接回答你的问题。。。这比原来的好吗?可能除非需要,否则字符串连接不会发生。但是,您正在引入对象实例化/分配。看到差异的唯一真正方法是分析它/编写一个基准 我真的不明白你在问什么。此外,您在“@4”示例中使用的字符串连接与在“@1”中使用的字符串连接完全相同。感谢您指出我在@4
中的错误,我的意思是将+
转换为,
。我会更新问题并添加一些解释。所以。。。我是说,数你的args有那么难吗?也许我还错过了什么?你写的代码很好,我只是从来没有发现说“我有4样东西,我需要4{}
在我的格式字符串中”是一个问题。与使用printf
@BrianRoach相比,这并不是更不合理的要求,这正是我想知道的——在脚本中计算参数的数量和插入括号对是否容易?我可能有数百个实例,不想手动执行。另一方面,我的级联器很可能是一个令人讨厌的东西。哦,好吧。我现在明白了。我想我的大脑只是疲劳了。你想写一些东西来检查源文件,并为你做转换,并试图简化它必须做什么?谢谢。我的例子遇到了第一个参数必须是字符串的问题,如果我真的尝试过的话,我会发现:)你建议的附加参数很好地解决了这个问题StringBuffer
和StringBuilder
对局部变量的执行可能非常类似。我敢肯定,额外的对象创建不仅弥补了删除的toString/string concat带来的节省,而且还为Concatenator.toString()
时的开销提供了理由。注释?我只是重复一下,Stri中的开销
logger.debug("{}", new Concatenator("Entering loop, arg is: ", arg));