programing

Java 예외에 대해 가능한 한 많은 정보를 기록하는 방법은 무엇입니까?

randomtip 2021. 1. 14. 08:12
반응형

Java 예외에 대해 가능한 한 많은 정보를 기록하는 방법은 무엇입니까?


예외를 로깅 할 때 몇 번 만난 일반적인 문제가 있습니다. 다루어야 할 다양한 유형이있는 것 같습니다. 예를 들어 일부는 다른 예외를 래핑하고 일부는 메시지를 전혀 가지고 있지 않습니다.

대부분의 코드 내가 사용 중 로그 예외를 본 적이 getMessage()toString() 그러나 이들은 항상 문제를 정확하게 파악하는 데 필요한 모든 정보를 수집하지 않습니다 - 같은 다른 방법을 getCause()하고 getStackTrace()때로는 추가 정보를 제공합니다.

예를 들어 Eclipse Inspect 창에서 지금보고있는 예외는 InvocationTargetException. 예외 자체에는 원인, 메시지, 스택 추적이 없지만 getCause ()의 대상은 InvalidUseOfMatchersException이러한 세부 정보가 채워져 있습니다.

그래서 내 질문은 다음과 같습니다. 입력으로 모든 유형 의 예외가 주어지면 예외에 대한 모든 관련 정보를 포함하는 멋진 형식의 문자열을 출력하는 단일 메서드를 제공하십시오 (예 : 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 참조하십시오 . 이것은 단지 더 높은 수준의 인터페이스입니다.


에서 printStacktrace()제공 하는 방법 Throwable(및 모든 예외)에 어떤 문제가 있습니까? 루트 예외의 유형, 메시지 및 스택 추적과 모든 (중첩 된) 원인을 포함하여 요청한 모든 정보를 표시합니다. Java 7에서는 try-with-resources 문에서 발생할 수있는 "억제 된"예외에 대한 정보도 표시합니다.

물론 System.err메서드의 인수가없는 버전 인 에 작성하고 싶지 않으므로 대신 사용 가능한 오버로드 중 하나를 사용하십시오.

특히, String을 얻고 싶다면 :

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

감사합니다,
Danyel


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)

Something that I do is to have a static method that handles all exceptions and I add the log to a JOptionPane to show it to the user, but you could write the result to a file in FileWriter wraped in a BufeeredWriter. For the main static method, to catch the Uncaught Exceptions I do:

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

And as for the method:

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 you case, instead of "screaming" to the user, you could write a log like I told you before:

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

I hope I have helped.

Have a nice day. :)

ReferenceURL : https://stackoverflow.com/questions/14606293/how-to-log-as-much-information-as-possible-for-a-java-exception

반응형