我在我的应用程序中使用log4j,只是想知道是否将(cc)对象传递到记录器中,如(1)中那样,这会比通过Exception
传递时消耗更多的资源,例如( 2)?换句话说,(2)会比(1)便宜吗?以下是我在log4j上的示例代码。
private static Logger logger = Logger.getLogger(...);
try {
...
}
catch( Exception e ) {
logger.error("blah blah blah", e); // (1)
logger.error("blah blah blah", e.fillInStackTrace()); // (2)
}
我在这里有点困惑,请帮忙澄清一下?
最佳答案
好吧,如果您查看fillInStackTrace
,您会注意到该方法是synchronized
。在Java(或任何语言)中,同步被认为是非常昂贵的操作,因为它需要获取和释放监视器。
另外,您应注意,从Throwable
返回的fillInStackTrace
仅记录有关在调用fillInStackTrace
时调用堆栈的堆栈帧的信息。此信息直接从JVM收集,然后转换为对象表示形式,这又会浪费资源。 (最终,fillInStackTrace
委托给native
方法,该方法扫描调用线程的当前调用堆栈。)
但是,您应该主要考虑要在日志中显示的内容,然后根据该条件进行决定。当您显式调用fillInStackTrace
时,当前调用堆栈将出现在日志中。这意味着您将不再看到进入try
块的更深层次的调用堆栈(考虑您的示例)。
毕竟,除非明确需要(2)所获得的信息,否则应始终使用变量(1)。我无法自发地想到标准日志记录范围内的一个解决方案(2)有意义的示例。但是,您可以利用(2),在编写某种框架时要减少堆栈跟踪中的噪音,并且要清除与该异常无关的一些顶部堆栈条目:例如,您可能使用某种ExceptionFactory
为您创建抛出的异常。然后,您不希望堆栈跟踪显示这些工厂方法的堆栈框架,因为它们与错误无关,并且会使尝试调试的用户感到困惑。因此,从工厂收到异常后,可以通过调用fillInStackTrace
手动填充堆栈跟踪。
请看此示例,直观了解两个调用的区别:
class Example {
public static void main(String[] args) {
try {
outerFunction();
}
catch (Throwable e) {
System.err.println("Outside:");
e.printStackTrace();
}
}
static void outerFunction() throws Throwable {
try {
innerFunction();
}
catch(Exception e) {
System.err.println("Inside:");
e.printStackTrace();
throw e.fillInStackTrace();
}
}
static void innerFunction() {
throw new RuntimeException("A custom exception");
}
}
将调用这两个不同的堆栈跟踪:
Inside:
java.lang.RuntimeException: A custom exception
at Example.innerFunction(Example.java:21)
at Example.outerFunction(Example.java:13)
at Example.main(Example.java:19)
Outside:
java.lang.RuntimeException: A custom exception
at Example.outerFunction(Example.java:13)
at Example.main(Example.java:19)