Java 日志 API 开销

发布于 2024-11-03 17:52:54 字数 565 浏览 0 评论 0 原文

我读过一些关于使用 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 ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

雨后彩虹 2024-11-10 17:52:54

我认为 log4j FAQ 很好地解决了这个问题:

对于一些记录器l,写作,

l.debug("条目编号:" + i + " is " + String.valueOf(entry[i]));

产生构造消息参数的成本,即将整数i和entry[i]转换为字符串,并连接中间字符串。无论消息是否会被记录,都会如此。

如果你担心速度,那就写

 if(l.isDebugEnabled()) {
     l.debug("条目编号:" + i + " is " + String.valueOf(entry[i]));
 }

这样,如果禁用了 logger l 的调试,您将不会产生参数构造的成本。另一方面,如果记录器启用了调试,您将需要两次评估记录器是否启用的成本:一次在 debugEnabled 中,一次在调试中。这是一个微不足道的开销,因为评估记录器所花费的时间不到实际记录语句所需时间的 1%。

使用保护子句是避免字符串构造的一般方法。

其他流行的框架,例如 slf4j,采用 使用格式化字符串/参数化消息,以便除非需要,否则不会评估消息。

I think that the log4j FAQ does a good job of addressing this:

For some logger l, writing,

l.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

incurs the cost of constructing the message parameter, that is converting both integer i and entry[i] to a String, and concatenating intermediate strings. This, regardless of whether the message will be logged or not.

If you are worried about speed, then write

 if(l.isDebugEnabled()) {
     l.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
 }

This way you will not incur the cost of parameter construction if debugging is disabled for logger l. On the other hand, if the logger is debug enabled, you will incur the cost of evaluating whether the logger is enabled or not, twice: once in debugEnabled and once in debug. This is an insignificant overhead since evaluating a logger takes less than 1% of the time it takes to actually log a statement.

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.

时光暖心i 2024-11-10 17:52:54

现代日志框架具有变量替换功能。然后,您的日志记录看起来像这样:

log.debug("The value of my first object is %s and of my second object is %s", firstObject, secondObject).

只有当日志记录设置为调试时,才会执行给定对象的 toString() 。否则它将忽略参数并返回。

Modern logging frameworks have variable replacement. Your logging then looks something like this:

log.debug("The value of my first object is %s and of my second object is %s", firstObject, secondObject).

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.

情归归情 2024-11-10 17:52:54

答案非常简单:不要在日志调用本身中调用昂贵的方法。另外,如果无法避免的话,请在日志记录调用周围使用警卫。

 if(logger.isDebugEnabled()) {
    logger.debug("this is an "+expensive()+" log call.");
 }

正如其他人指出的那样,如果您的日志记录框架中有可用的格式(即,如果您使用的是足够现代的格式来支持它,则应该其中的每一个,但事实并非如此) ,您应该依靠它来帮助支付记录时的费用。如果您选择的框架已经支持格式化,那么要么切换要么编写您自己的包装器。

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.

 if(logger.isDebugEnabled()) {
    logger.debug("this is an "+expensive()+" log call.");
 }

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.

洋洋洒洒 2024-11-10 17:52:54

你是对的,评估 log() 调用的参数可能会增加不必要的开销,并且可能会很昂贵。

这就是为什么大多数理智的日志框架也提供了一些字符串格式化函数,以便您可以编写如下内容:

log.debug("Frobnicating {0}", objectWithExpensiveToString);

这样,您的唯一开销就是对 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:

log.debug("Frobnicating {0}", objectWithExpensiveToString);

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, the toString() on objectWithExpensiveToString() is called and the result inserted into the format string before it is logged.

Some log statements use MessageFormat style placeholders ({0}), others use format() style placeholders (%s) and yet others might take a third approach.

暮色兮凉城 2024-11-10 17:52:54

您可以使用一种有趣的方式(尽管有点冗长)来进行断言。当断言打开时,会有输出和开销,而当断言关闭时,没有输出,也绝对没有开销。

public static void main(String[] args) {
    assert returnsTrue(new Runnable() {
        @Override
        public void run() {
            // your logging code
        }
    });
}

public static boolean returnsTrue(Runnable r) {
    r.run();
    return true;
}

这里需要 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.

public static void main(String[] args) {
    assert returnsTrue(new Runnable() {
        @Override
        public void run() {
            // your logging code
        }
    });
}

public static boolean returnsTrue(Runnable r) {
    r.run();
    return true;
}

The returnsTrue() function is needed here because I know no better way of making an expression return true, and assert requires a boolean.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文