[英]How to log exceptions in Java?
在 Java 中記錄異常時,我遇到過幾次常見問題。似乎有各種不同的類型需要處理。 例如,有些包裝其他異常,有些根本沒有消息——只有一種類型。
我見過的大多數代碼都使用getMessage()
或toString()
記錄異常。 但是這些方法並不總能捕獲查明問題所需的所有信息 - 其他方法(例如getCause()
和getStackTrace()
有時會提供額外信息。
例如,我現在在 Eclipse Inspect window 中查看的異常是InvocationTargetException
。 異常本身沒有原因,沒有消息,沒有堆棧跟蹤...但是 getCause() 的目標是InvalidUseOfMatchersException
,並填充了這些詳細信息。
所以我的問題是:給定任何類型的異常作為輸入,請提供一個單一的方法,它將 output 一個格式良好的字符串,其中包含有關異常的所有相關信息(例如,可能遞歸調用getCause()
等等?)在發布之前,我幾乎要自己嘗試一下,但真的不想重新發明輪子——這樣的事情肯定已經做過很多次了……?
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
的源代碼。 這只是一個更高級別的接口。
Throwable
提供的printStacktrace()
方法有什么問題(以及每個異常)? 它顯示了您請求的所有信息,包括根異常的類型、消息和堆棧跟蹤以及所有(嵌套)原因。 在 Java 7 中,它甚至會向您顯示有關可能在 try-with-resources 語句中發生的“抑制”異常的信息。
當然,您不想寫入System.err
,該方法的無參數版本會執行此操作,因此請改用可用的重載之一。
特別是,如果您只想獲取一個字符串:
Exception e = ...
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();
如果你碰巧使用了很棒的Guava庫,它提供了一個實用方法: com.google.common.base.Throwables#getStackTraceAsString(Throwable)
。
如果您使用 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);
}
我前段時間編寫的日志記錄腳本可能會有所幫助,盡管它不是您想要的。 它的行為方式類似於 System.out.println,但包含有關 StackTrace 等的更多信息。它還為 Eclipse 提供可點擊文本:
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();
}
問候,
丹尼爾
您還可以使用 Apache 的ExceptionUtils。
例子:
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)
我要做的是擁有一個處理所有異常的靜態方法,並將日志添加到 JOptionPane 以將其顯示給用戶,但是您可以將結果寫入FileWriter
中包裝在BufeeredWriter
中的文件。 對於主要的靜態方法,要捕獲未捕獲的異常,我會這樣做:
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);
通過將開發人員消息與整個異常 object 一起記錄下來,我們可以看到開發人員消息后跟異常名稱和消息,然后是堆棧跟蹤。 這為我們提供了異常發生時調用了哪些方法的完整畫面,一直到庫級別。
參考/學分:
https://medium.com/w-logs/how-to-log-exception-properly-6aa80b62ff8a
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.