我读过一些关于使用 Java 记录调试消息的各种方法,并且来自 C 背景,我的担忧如下:
这些库声称在禁用日志记录的情况下(例如生产环境)开销最小,但因为他们的 log()
函数的参数仍在评估,我担心的是,现实场景中的开销实际上根本不可忽略。
例如,log(myobject.toString(), "info message")
仍然具有评估 myobject.toString()
的开销,该开销可能相当大,甚至如果日志函数本身什么也不做。
有人有解决这个问题的方法吗?
PS:对于那些想知道为什么我提到 C 背景的人:C 允许您使用预处理器宏和编译时指令,这将完全删除与编译时调试相关的所有代码,包括宏参数(根本不会出现)。
编辑 :
读完第一批答案后,似乎java显然没有任何可以做到这一点的东西(想想在移动环境中的每个CPU都很重要的大循环中记录数字的余弦)。所以我要补充一点,我什至会选择基于 IDE 的解决方案。我最后的手段是构建类似“查找全部/替换”宏的东西。
我首先想到也许从面向方面的框架中获取的东西会有所帮助......
有人吗?
I've read a bit about the various ways of logging a debugging message with Java, and coming from a C background my concern is as follow :
Those libraries claim minimal overhead in case where logging is disabled (such as production environment), but since argument to their log()
function are still evaluated, my concern is that the overhead in real-world scenario will, in fact, not be negligible at all.
For example, a log(myobject.toString(), "info message")
still has the overhead of evaluating myobject.toString()
, which can be pretty big, even if the log function itself does nothing.
Does anyone has a solution to this issue ?
PS: for those wondering why I mentioned a C background : C lets you use preprocessor macro and compile-time instructions that will completely remove all the code related to debugging at compilation time, including macros parameters (which will simply not appear at all).
EDIT :
After having read the first batch of answers, it seems that java clearly doesn't have anything that would do the trick (think logging the cosine of a number in a big loop in a mobile environment where every bit of CPU matters). So i'll add that i would even go for an IDE based solution. My last resort being building something like a "find all / replace" macro.
I first thought that maybe something grabbed from an aspect oriented framework would help...
Anyone ?
发布评论
评论(5)
我认为 log4j FAQ 很好地解决了这个问题:
使用保护子句是避免字符串构造的一般方法。
其他流行的框架,例如 slf4j,采用 使用格式化字符串/参数化消息,以便除非需要,否则不会评估消息。
I think that the log4j FAQ does a good job of addressing this:
Using a guard clause is the general approach to avoid string construction here.
Other popular frameworks, such as slf4j, take the approach of using formatted strings / parameterized messages so that the message is not evaulated unless needed.
现代日志框架具有变量替换功能。然后,您的日志记录看起来像这样:
只有当日志记录设置为调试时,才会执行给定对象的 toString() 。否则它将忽略参数并返回。
Modern logging frameworks have variable replacement. Your logging then looks something like this:
The toString() of the given objects will then only be executed when logging is set to debug. Otherwise it will just ignore the parameters and return.
答案非常简单:不要在日志调用本身中调用昂贵的方法。另外,如果无法避免的话,请在日志记录调用周围使用警卫。
正如其他人指出的那样,如果您的日志记录框架中有可用的格式(即,如果您使用的是足够现代的格式来支持它,则应该其中的每一个,但事实并非如此) ,您应该依靠它来帮助支付记录时的费用。如果您选择的框架不已经支持格式化,那么要么切换要么编写您自己的包装器。
The answer is pretty simple: don't call expensive methods in the log call itself. Plus, use guards around the logging call, if you can't avoid it.
And as others have pointed out, if you have formatting available in your logging framework (i.e., if you're using one modern enough to support it, which should be every one of them but isn't), you should rely on that to help defray expense at the point of logging. If your framework of choice does not support formatting already, then either switch or write your own wrapper.
你是对的,评估
log()
调用的参数可能会增加不必要的开销,并且可能会很昂贵。这就是为什么大多数理智的日志框架也提供了一些字符串格式化函数,以便您可以编写如下内容:
这样,您的唯一开销就是对
debug()
的调用。如果该级别被停用,则不会执行任何操作;如果它被激活,则解释格式字符串,调用objectWithExpectiveToString()
上的toString()
并调用结果在记录之前插入到格式字符串中。某些日志语句使用
MessageFormat
< /a> 样式占位符 ({0}
),其他使用format()
style 占位符 (%s
),而其他人可能会采用第三种方法。You're right, evaluating the arguments to a
log()
call can add overhead that is unnecessary and could be expensive.That's why most sane logging frameworks provide some string formatting functions as well, so that you can write stuff like this:
This way your only overhead is the call to
debug()
. If that level is deactivated, then nothing more is done and if it is activated, then the format string is interpreted, thetoString()
onobjectWithExpensiveToString()
is called and the result inserted into the format string before it is logged.Some log statements use
MessageFormat
style placeholders ({0}
), others useformat()
style placeholders (%s
) and yet others might take a third approach.您可以使用一种有趣的方式(尽管有点冗长)来进行断言。当断言打开时,会有输出和开销,而当断言关闭时,没有输出,也绝对没有开销。
这里需要 returnsTrue() 函数,因为我不知道让表达式返回 true 的更好方法,而断言需要一个布尔值。
You can use a funny way - a bit verbose, though - with assertions. With assertions turned on, there will be output and overhead, and with assertions turned off, no output and absolutely no overhead.
The returnsTrue() function is needed here because I know no better way of making an expression return true, and assert requires a boolean.