如何在 Java 中记录异常?

在 Java 中记录异常时,我遇到过几次常见的问题。似乎有各种各样的不同类型要处理。例如,有些异常包装其他异常,有些根本没有消息——只有一个类型。

我见过的大多数代码都使用 getMessage()toString()记录异常。但是这些方法并不总是能够捕捉到精确定位问题所需的所有信息——其他方法,如 getCause()getStackTrace()有时会提供额外的信息。

例如,我现在在 Eclipse 检查窗口中看到的异常是 InvocationTargetException。异常本身没有原因、没有消息、没有堆栈跟踪... ... 但是 getCause ()的目标是 InvalidUseOfMatchersException,其中填充了这些细节。

所以我的问题是: 如果有一个 任何类型的异常作为输入,请提供一个单独的方法,它将输出一个格式很好的字符串,其中包含 所有相关的异常信息(例如,可能递归地调用 getCause()等等)在发布之前,我几乎要尝试一下,但真的不想重新发明轮子-这样的事情肯定已经做过很多次了... ?

132510 次浏览

Throwable提供的 printStacktrace()方法(因此每个异常)有什么问题?它显示了您请求的所有信息,包括根异常的类型、消息和堆栈跟踪以及所有(嵌套的)原因。在 Java7中,它甚至向您显示了在 try-with-resources 语句中可能出现的“被抑制”异常的信息。

当然,您不希望写入 System.err,这是该方法的无参数版本所做的,因此应该使用可用的重载之一。

特别是,如果你只想得到一个字符串:

  Exception e = ...
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();

如果您碰巧使用了伟大的 番石榴库,它提供了一个实用程序方法: com.google.common.base.Throwables#getStackTraceAsString(Throwable)

我前段时间编写的一个日志脚本可能会有所帮助,尽管它并不完全是您想要的。它的行为类似于 System.out.println,但是包含有关 StackTrace 等的 更多信息。它还为 Eclipse 提供了 Clickable 文本:

private static final SimpleDateFormat   extended    = new SimpleDateFormat( "dd MMM yyyy (HH:mm:ss) zz" );


public static java.util.logging.Logger initLogger(final String name) {
final java.util.logging.Logger logger = java.util.logging.Logger.getLogger( name );
try {


Handler ch = new ConsoleHandler();
logger.addHandler( ch );


logger.setLevel( Level.ALL ); // Level selbst setzen


logger.setUseParentHandlers( false );


final java.util.logging.SimpleFormatter formatter = new SimpleFormatter() {


@Override
public synchronized String format(final LogRecord record) {
StackTraceElement[] trace = new Throwable().getStackTrace();
String clickable = "(" + trace[ 7 ].getFileName() + ":" + trace[ 7 ].getLineNumber() + ") ";
/* Clickable text in Console. */


for( int i = 8; i < trace.length; i++ ) {
/* 0 - 6 is the logging trace, 7 - x is the trace until log method was called */
if( trace[ i ].getFileName() == null )
continue;
clickable = "(" + trace[ i ].getFileName() + ":" + trace[ i ].getLineNumber() + ") -> " + clickable;
}


final String time = "<" + extended.format( new Date( record.getMillis() ) ) + "> ";


StringBuilder level = new StringBuilder("[" + record.getLevel() + "] ");
while( level.length() < 15 ) /* extend for tabby display */
level.append(" ");


StringBuilder name = new StringBuilder(record.getLoggerName()).append(": ");
while( name.length() < 15 ) /* extend for tabby display */
name.append(" ");


String thread = Thread.currentThread().getName();
if( thread.length() > 18 ) /* trim if too long */
thread = thread.substring( 0, 16 ) + "...";
else {
StringBuilder sb = new StringBuilder(thread);
while( sb.length() < 18 ) /* extend for tabby display */
sb.append(" ");
thread = sb.insert( 0, "Thread " ).toString();
}


final String message = "\"" + record.getMessage() + "\" ";


return level + time + thread + name + clickable + message + "\n";
}
};
ch.setFormatter( formatter );
ch.setLevel( Level.ALL );
} catch( final SecurityException e ) {
e.printStackTrace();
}
return logger;
}

注意这个输出到控制台,您可以更改它,请参阅 http://docs.oracle.com/javase/1.4.2/docs/api/java/util/logging/Logger.html了解更多信息。

现在,下面可能会做您想要的。它将遍历 Throwable 的所有原因,并将其保存在 String 中。请注意,这不使用 StringBuilder,因此您可以通过更改它来进行优化。

Throwable e = ...
String detail = e.getClass().getName() + ": " + e.getMessage();
for( final StackTraceElement s : e.getStackTrace() )
detail += "\n\t" + s.toString();
while( ( e = e.getCause() ) != null ) {
detail += "\nCaused by: ";
for( final StackTraceElement s : e.getStackTrace() )
detail += "\n\t" + s.toString();
}

问候,
丹尼尔

java.util.logging包裹Java SE中的标准,它的 Logger包含一个重载的日志方法,该方法接受 Throwable对象。 它将为您记录异常的堆栈跟踪及其原因。

例如:

import java.util.logging.Level;
import java.util.logging.Logger;


[...]


Logger logger = Logger.getAnonymousLogger();
Exception e1 = new Exception();
Exception e2 = new Exception(e1);
logger.log(Level.SEVERE, "an exception was thrown", e2);

将记录:

SEVERE: an exception was thrown
java.lang.Exception: java.lang.Exception
at LogStacktrace.main(LogStacktrace.java:21)
Caused by: java.lang.Exception
at LogStacktrace.main(LogStacktrace.java:20)

顺便说一句,在内部,这完全符合@philipp-wendler 的建议。 看看 SimpleFormatter.java的源代码。这只是一个更高级的接口。

您还可以使用 Apache 的 例外工具。

例如:

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;




public class Test {


static Logger logger = Logger.getLogger(Test.class);


public static void main(String[] args) {


try{
String[] avengers = null;
System.out.println("Size: "+avengers.length);
} catch (NullPointerException e){
logger.info(ExceptionUtils.getFullStackTrace(e));
}
}


}

控制台输出:

java.lang.NullPointerException
at com.aimlessfist.avengers.ironman.Test.main(Test.java:11)

如果你正在使用 LogBack 或 SLF4J,它应该非常简单

//imports
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


//Initialize logger
Logger logger = LoggerFactory.getLogger(<classname>.class);
try {
//try something
} catch(Exception e){
//Actual logging of error
logger.error("some message", e);
}

我所做的就是使用一个静态方法来处理所有异常,并将日志添加到 JOptionPane 中以向用户显示它,但是您可以将结果写入包装在 BufeeredWriter中的 FileWriter文件中。 对于 main 静态方法,要捕获 Uncatch Exception,我需要:

SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
//Initializations...
}
});




Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException( Thread t, Throwable ex ) {
handleExceptions( ex, true );
}
}
);

至于方法:

public static void handleExceptions( Throwable ex, boolean shutDown ) {
JOptionPane.showMessageDialog( null,
"A CRITICAL ERROR APPENED!\n",
"SYSTEM FAIL",
JOptionPane.ERROR_MESSAGE );


StringBuilder sb = new StringBuilder(ex.toString());
for (StackTraceElement ste : ex.getStackTrace()) {
sb.append("\n\tat ").append(ste);
}




while( (ex = ex.getCause()) != null ) {
sb.append("\n");
for (StackTraceElement ste : ex.getStackTrace()) {
sb.append("\n\tat ").append(ste);
}
}


String trace = sb.toString();


JOptionPane.showMessageDialog( null,
"PLEASE SEND ME THIS ERROR SO THAT I CAN FIX IT. \n\n" + trace,
"SYSTEM FAIL",
JOptionPane.ERROR_MESSAGE);


if( shutDown ) {
Runtime.getRuntime().exit( 0 );
}
}

在这种情况下,您可以像我之前告诉您的那样编写一个日志,而不是向用户“尖叫”:

String trace = sb.toString();


File file = new File("mylog.txt");
FileWriter myFileWriter = null;
BufferedWriter myBufferedWriter = null;


try {
//with FileWriter(File file, boolean append) you can writer to
//the end of the file
myFileWriter = new FileWriter( file, true );
myBufferedWriter = new BufferedWriter( myFileWriter );


myBufferedWriter.write( trace );
}
catch ( IOException ex1 ) {
//Do as you want. Do you want to use recursive to handle
//this exception? I don't advise that. Trust me...
}
finally {
try {
myBufferedWriter.close();
}
catch ( IOException ex1 ) {
//Idem...
}


try {
myFileWriter.close();
}
catch ( IOException ex1 ) {
//Idem...
}
}

我希望我帮上忙了。

祝你愉快

最佳实践是记录整个异常:

log.error("Our custom message", ex);

通过将开发人员消息与整个异常对象一起记录,我们可以看到开发人员消息后面跟着异常名称和消息,然后是堆栈跟踪。 这给我们提供了异常发生时调用了哪些方法的完整图片,包括库级别 .

参考/学分:

Https://medium.com/w-logs/how-to-log-exception-properly-6aa80b62ff8a