Wie protokolliere ich Ausnahmen in Java?

Lesezeit: 8 Minuten

Benutzeravatar von Steve Chambers
Steve Kammern

Es gibt ein häufiges Problem, auf das ich beim Protokollieren von Ausnahmen in Java einige Male gestoßen bin. Es scheint verschiedene Arten zu geben, mit denen man sich auseinandersetzen muss. ZB schließen einige andere Ausnahmen ein und einige haben überhaupt keine Nachricht – nur einen Typ.

Der meiste Code, den ich gesehen habe, protokolliert eine Ausnahme, indem er eine von beiden verwendet getMessage() oder toString(). Diese Methoden erfassen jedoch nicht immer alle Informationen, die zum Lokalisieren des Problems erforderlich sind – andere Methoden wie z getCause() und getStackTrace() stellen manchmal zusätzliche Informationen bereit.

Die Ausnahme, die ich mir gerade in meinem Eclipse-Inspect-Fenster ansehe, ist zum Beispiel eine InvocationTargetException. Die Ausnahme selbst hat keine Ursache, keine Nachricht, kein Stacktrace … aber das Ziel von getCause() ist es InvalidUseOfMatchersExceptionmit diesen Details ausgefüllt.

Also meine Frage ist: Angesichts einer Ausnahme von irgendein type als Eingabe, geben Sie bitte eine einzelne Methode an, die eine schön formatierte Zeichenfolge ausgibt, die enthält alle relevante Informationen über die Ausnahme (z. B. möglicherweise rekursives Aufrufen getCause() unter anderem?) Bevor ich gepostet habe, wollte ich es fast selbst versuchen, aber ich möchte das Rad wirklich nicht neu erfinden – so etwas muss doch schon oft gemacht worden sein …?

Benutzeravatar von flup
fluchen

Das java.util.logging Paket ist standardmäßig drin Java SE. Es ist Logger enthält eine überladene Protokollmethode, die akzeptiert Throwable Objekte. Es protokolliert Stacktraces von Ausnahmen und deren Ursache für Sie.

Zum Beispiel:

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);

Werde protokollieren:

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)

Intern macht das übrigens genau das, was @philipp-wendler vorschlägt. Siehe die Quellcode für SimpleFormatter.java. Dies ist nur eine Schnittstelle auf höherer Ebene.

  • Denken Sie daran, dass Sie bei Verwendung eines benutzerdefinierten Formatierers speziell die Verwendung von Throwables unterstützen müssen (ich habe gerade Oracle-Code kopiert), da dies sonst nicht funktioniert.

    – Dynamyt

    4. September 2013 um 0:34 Uhr

  • Zusätzlich zu dieser Idee habe ich überprüft, wie es mit SL4J geht, und Folgendes gegründet: baeldung.com/slf4j-log-Ausnahmen

    – Martin Larizzate

    13. Juni 2020 um 22:29 Uhr

  • Diese Antwort war hilfreich. Ich habe getestet, ob ich Ausnahmen so protokollieren kann, dass alle Informationen über den Fehler (einschließlich der Fehlermeldung der tief verschachtelten ursprünglichen Ausnahme) enthalten sind und dass das Programm auch weiterhin mehr Arbeit verarbeiten kann. Das hat gut funktioniert. Die protokollierte Zeichenfolge enthalten INFO: could not complete job for user Matt, com.mattwelke.Main$MathException: failed to do mathund Caused by: java.lang.IllegalArgumentException: you tried to divide 10.0 by 0. (was bedeutet, dass ich den gesamten Fehlerkontext, den ich brauchte, in den Protokollen erhalten habe). Ich kann mir vorstellen, dass dies auch mit SLF4J gut funktionieren würde.

    – Matt Welke

    24. Januar um 5:36 Uhr

Benutzeravatar von Harish Gokavarapu
Harish Gokavarapu

Es sollte ganz einfach sein, wenn Sie LogBack oder SLF4J verwenden. Ich mache es wie folgt

//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);
}

Benutzeravatar von Philipp Wendler
Philipp Wendler

Was ist los mit der printStacktrace() Methode zur Verfügung gestellt von Throwable (und damit jede Ausnahme)? Es zeigt alle angeforderten Informationen, einschließlich Typ, Nachricht und Stack-Trace der Root-Ausnahme und aller (verschachtelten) Ursachen. In Java 7 zeigt es Ihnen sogar die Informationen über “unterdrückte” Ausnahmen, die in einer try-with-resources-Anweisung auftreten können.

Natürlich würdest du nicht schreiben wollen System.errwas die Version der Methode ohne Argumente tut, also verwenden Sie stattdessen eine der verfügbaren Überladungen.

Insbesondere, wenn Sie nur einen String erhalten möchten:

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

Wenn Sie zufällig das große verwenden Guave Bibliothek, es bietet eine Dienstprogrammmethode, die dies tut: com.google.common.base.Throwables#getStackTraceAsString(Throwable).

  • Vielen Dank für Ihre Antwort, aber ich denke, alles, was printStacktrace() tut, schreibt den von getStacktrace() zurückgegebenen Stacktrace in den Fehlerstrom? Das ist nicht wirklich das, was ich suche. Denken Sie auch nicht, dass es in dem Fall funktionieren würde, in dem die Ausnahme eine andere Ausnahme umschließt.

    – Steve Kammern

    30. Januar 2013 um 14:50 Uhr

  • Die Überladungen von printStackStrace auf ein anderes Ziel schreiben, nicht in den Fehlerstrom. Und die gedruckten Informationen enthalten alle Ursachen, einschließlich ihres Typs, ihrer Nachricht, ihres Stack-Trace und (verschachtelter) Ursachen.

    – Philipp Wendler

    30. Januar 2013 um 14:53 Uhr

  • Probieren Sie einfach meine 3 Codezeilen aus, ich bin sicher, Sie werden zufrieden sein.

    – Philipp Wendler

    30. Januar 2013 um 14:53 Uhr

  • Es ist nicht erforderlich, Ihre eigene Version zu erstellen, Standard-J2SE bietet dies. Siehe meine Antwort.

    – Fluch

    30. Januar 2013 um 15:12 Uhr

  • @flup Warum würdest du das als “selbst rollen” betrachten? Diesen Weg tut Verwenden Sie dazu die Standard-Java-Methode.

    – Philipp Wendler

    30. Januar 2013 um 15:19 Uhr

Ein Protokollierungsskript, das ich vor einiger Zeit geschrieben habe, könnte hilfreich sein, obwohl es nicht genau das ist, was Sie wollen. Es verhält sich wie ein System.out.println, aber mit viel mehr Informationen über StackTrace usw. Es bietet auch anklickbaren Text für 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;
}

Beachten Sie, dass diese Ausgabe an die Konsole erfolgt, Sie können das ändern, siehe http://docs.oracle.com/javase/1.4.2/docs/api/java/util/logging/Logger.html für weitere Informationen dazu.

Nun, das Folgende wird wahrscheinlich tun, was Sie wollen. Es durchläuft alle Ursachen eines Throwable und speichert es in einem String. Beachten Sie, dass dies nicht verwendet wird StringBuildersodass Sie ihn optimieren können, indem Sie ihn ändern.

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();
}

Grüße,
Daniel

Sie können auch Apache verwenden ExceptionUtils.

Beispiel:

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));
        }
    }

}

Konsolenausgabe:

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

  • Ab Version 3 der Sprachdienstprogramme wurde die Methode umbenannt in ExceptionUtils#getStackTrace(...)!

    – ltlBeBoy

    22. Juni um 14:31 Uhr

Benutzeravatar von Saclyr Barlonium
Saclyr Barlonium

Etwas, das ich tue, ist, eine statische Methode zu haben, die alle Ausnahmen behandelt, und ich füge das Protokoll zu einem JOptionPane hinzu, um es dem Benutzer anzuzeigen, aber Sie könnten das Ergebnis in eine Datei schreiben FileWriter eingewickelt in ein BufeeredWriter. Für die statische Hauptmethode mache ich Folgendes, um die nicht erfassten Ausnahmen abzufangen:

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 );
        }
    }
);

Und was die Methode angeht:

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 );
    }
}

In Ihrem Fall könnten Sie, anstatt den Benutzer anzuschreien, ein Protokoll schreiben, wie ich es Ihnen zuvor gesagt habe:

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...
    }
}

Ich hoffe, ich habe geholfen.

Einen schönen Tag noch. 🙂

  • Ab Version 3 der Sprachdienstprogramme wurde die Methode umbenannt in ExceptionUtils#getStackTrace(...)!

    – ltlBeBoy

    22. Juni um 14:31 Uhr

1424700cookie-checkWie protokolliere ich Ausnahmen in Java?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy