Comment enregistrer autant d'informations que possible pour une exception Java?


Il y a un problème commun que j'ai rencontré à quelques reprises lors de la journalisation des exceptions. Il semble y avoir différents types différents à traiter. Par exemple, certains encapsulent d'autres exceptions, certains n'ont pas de message du tout - juste un type.

La plupart des codes que j'ai vus enregistrent des exceptions en utilisant getMessage() ou toString() mais celles - ci ne capturent pas toujours toutes les informations nécessaires pour identifier le problème-d'autres méthodes telles que getCause() et getStackTrace() fournissent parfois des informations supplémentaires.

À titre d'exemple, l'exception Je regarde en ce moment dans ma fenêtre d'inspection Eclipse est un InvocationTargetException. L'exception elle-même n'a pas de cause, pas de message, pas de stacktrace ... mais la cible de getCause () est InvalidUseOfMatchersException avec ces détails remplis.

Ma question est donc la suivante: Étant donné une exception de tout typeen entrée, veuillez fournir une méthode unique qui produira une chaîne bien formatée contenant toutes les informations pertinentes sur l'exception (par exemple, éventuellement appeler récursivementgetCause() entre autres choses?) Avant de poster cette question, j'allais presque avoir un coup de couteau à cela moi - même, mais je ne veux pas vraiment réinventer la roue-une telle chose a sûrement dû être faite plusieurs fois auparavant...?

Veuillez ne pas me pointer vers un cadre de journalisation ou d'utilitaire particulier pour le faire. Je cherche un fragment de code plutôt qu'une bibliothèque car je n'ai pas le droit d'ajouter des dépendances externes sur le projet sur lequel je travaille et il est en fait connecté à une partie d'une page Web plutôt qu'à un fichier. S'il s'agit de copier le fragment de code hors d'un tel cadre (et de l'attribuer), c'est bien :-)

Author: Steve Chambers, 2013-01-30

6 answers

Le java.util.logging le paquet est standard dans Java SE. Son Logger comprend une surcharge du journal méthode qui accepte Throwable objets. Il enregistrera les traces d'exceptions et leur cause pour vous.

Par exemple:

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

Enregistrera:

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)

En interne, cela fait exactement ce que @philipp-wendler suggère, d'ailleurs. Voir le code source pour SimpleFormatter.java. Ceci est juste une interface de niveau supérieur.

 58
Author: flup, 2017-09-12 18:40:11

Quel est le problème avec la méthode printStacktrace() fournie par Throwable (et donc chaque exception)? Il affiche toutes les informations que vous avez demandées, y compris le type, le message et la trace de pile de l'exception racine et de toutes les causes (imbriquées). Dans Java 7, il vous montre même les informations sur les exceptions "supprimées" qui peuvent se produire dans une instruction try-with-resources.

Bien sûr, vous ne voudriez pas écrire dans System.err, ce que fait la version sans argument de la méthode, alors utilisez plutôt l'un des surcharge.

En particulier, si vous voulez juste obtenir une chaîne:

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

Si vous utilisez la grande bibliothèque Guava, elle fournit une méthode utilitaire pour ce faire: com.google.common.base.Throwables#getStackTraceAsString(Throwable).

 17
Author: Philipp Wendler, 2017-01-18 22:09:59

Cela devrait être assez simple si vous utilisez LogBack ou SLF4J. Je le fais comme ci-dessous

//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);
}
 10
Author: Harish Gokavarapu, 2015-11-04 22:30:55

Quelque Chose que je fais, c'est d'avoir une méthode statique qui gère toutes les exceptions et j'ajoute le journal à un JOptionPane de montrer à l'utilisateur, mais vous pouvez écrire le résultat dans un fichier de FileWriter wraped dans un BufeeredWriter. Pour la méthode statique principale, pour attraper les exceptions non capturées, je fais:

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

Et quant à la méthode:

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

Dans votre cas, au lieu de "crier" à l'utilisateur, vous pouvez écrire un journal comme je vous l'ai dit avant:

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

J'espère avoir aidé.

Bonne journée. :)

 2
Author: Saclyr Barlonium, 2016-10-27 20:34:04

Un script de journalisation que j'ai écrit il y a quelque temps pourrait être utile, bien que ce ne soit pas exactement ce que vous voulez. Il agit comme un Système.hors.println mais avec beaucoup plus d'informations sur StackTrace etc. Il fournit également du texte cliquable pour 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;
}

Notez que cela sort sur la console, vous pouvez changer cela, voir http://docs.oracle.com/javase/1.4.2/docs/api/java/util/logging/Logger.html pour plus d'informations à ce sujet.

Maintenant, ce qui suit va probablement faire ce que vous voulez. Il passera par toutes les causes d'un Throwable et l'enregistrera dans une chaîne. Notez que cela n'utilise pas StringBuilder, vous pouvez donc optimiser en le modifiant.

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

Cordialement,
Danyel

 1
Author: Danyel, 2013-01-30 14:42:44

Vous pouvez également utiliser ExceptionUtils d'Apache.

Exemple:

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

}

Sortie console:

java.lang.NullPointerException
    at com.aimlessfist.avengers.ironman.Test.main(Test.java:11)
 1
Author: RIP_SunMicroSys, 2015-10-05 14:47:08