Как регистрировать как можно больше информации для исключения Java?
Есть общая проблема, с которой я сталкивался несколько раз при регистрации Исключения. Кажется, существуют различные типы борьбы. Например. некоторые обертывают другие Исключения, у некоторых нет сообщения вообще - только тип.
В большинстве случаев я видел журналы Исключения с использованием либо getMessage()
, либо toString(
), но они не всегда фиксируют всю информацию, необходимую для определения проблемы. Другие методы, такие как getCause()
и getStackTrace()
, иногда предоставляют Дополнительная информация.
В качестве примера, Исключение, которое я сейчас просматриваю в окне Eclipse Inspect, является InvocationTargetException
. В самом Исключении нет причины, нет сообщения, нет stacktrace... но цель из getCause() - InvalidUseOfMatchersException
с этими подробными сведениями.
Итак, мой вопрос: учитывая исключение любого типа в качестве ввода, предоставьте один метод, который выведет красиво форматированную строку, содержащую всю релевантную информацию об исключении (например, возможно, рекурсивно вызывающий getCause()
среди других вещей?). публиковать этот вопрос, я почти собирался нанести удар по этому самому, но на самом деле не хочу изобретать велосипед - наверняка такая вещь должна была быть сделана много раз раньше...?
Пожалуйста, не назовите меня в какой-либо конкретной области ведения журнала или утилиты, чтобы это сделать. Я ищу фрагмент кода, а не библиотеку, поскольку у меня нет права добавлять внешние зависимости в проект, над которым я работаю, и он фактически регистрируется на части веб-страницы, а не на файле. Если это случай копирования фрагмента кода из такой структуры (и приписывания ему), что штраф: -)
Ответы
Ответ 1
java.util.logging
package является стандартным в 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)
Внутри, это делает то, что, кстати, предлагает @Филипп-Вендлер.
Смотрите исходный код для SimpleFormatter.java
. Это просто интерфейс более высокого уровня.
Ответ 2
Что не так с методом printStacktrace()
, предоставляемым Throwable
(и, следовательно, каждым исключением)? Он показывает всю запрашиваемую информацию, включая трассировку типа, сообщения и стека корневого исключения и всех (вложенных) причин. В 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)
.
Ответ 3
Это должно быть довольно просто, если вы используете 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);
}
Ответ 4
Что-то, что я делаю, это иметь статический метод, который обрабатывает все исключения, и я добавляю журнал в JOptionPane, чтобы показать его пользователю, но вы можете записать результат в файл в FileWriter
, который был поврежден в BufeeredWriter
.
Для основного статического метода, чтобы поймать исключения Uncout, я делаю:
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...
}
}
Надеюсь, я помог.
Иметь хороший день.:)
Ответ 5
Возможно, вам поможет журнал script, который я написал некоторое время назад, хотя это не совсем то, что вы хотите. Он действует таким же образом, как 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();
}
С уважением,
Danyel
Ответ 6
Вы также можете использовать 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)