Program Tip

스택 추적이란 무엇이며 애플리케이션 오류를 디버그하는 데 어떻게 사용할 수 있습니까?

programtip 2020. 10. 2. 23:10
반응형

스택 추적이란 무엇이며 애플리케이션 오류를 디버그하는 데 어떻게 사용할 수 있습니까?


때때로 내 응용 프로그램을 실행할 때 다음과 같은 오류가 발생합니다.

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

사람들은 이것을 "스택 트레이스"라고 불렀습니다. 스택 추적이란 무엇입니까? 내 프로그램에서 발생하는 오류에 대해 무엇을 알려줄 수 있습니까?


이 질문에 대해-초보 프로그래머가 "오류가 발생하는"질문을 자주보고 스택 추적이 무엇인지 또는 어떻게 사용할 수 있는지 이해하지 못한 채 스택 추적과 임의의 코드 블록을 붙여 넣습니다. 이 질문은 스택 추적의 값을 이해하는 데 도움이 필요할 수있는 초보 프로그래머를위한 참고 자료입니다.


간단히 말해서 스택 추적 은 예외가 발생했을 때 애플리케이션이 중간에 있었던 메서드 호출 목록입니다.

간단한 예

질문에 제공된 예를 사용하여 응용 프로그램에서 예외가 발생한 위치를 정확하게 확인할 수 있습니다. 스택 추적을 살펴 보겠습니다.

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

이것은 매우 간단한 스택 추적입니다. "at ..."목록의 시작 부분에서 시작하면 오류가 발생한 위치를 알 수 있습니다. 우리가 찾고있는 것은 애플리케이션의 일부인 최상위 메서드 호출입니다. 이 경우 다음과 같습니다.

at com.example.myproject.Book.getTitle(Book.java:16)

이것을 디버그하기 위해 우리는 Book.java다음과 같은 line을 열어 볼 수 있습니다 16.

15   public String getTitle() {
16      System.out.println(title.toString());
17      return title;
18   }

이것은 (아마도 title) 무언가 null가 위의 코드에 있음을 나타냅니다 .

일련의 예외가있는 예

때때로 응용 프로그램은 예외를 포착하고 다른 예외의 원인으로 다시 던집니다. 일반적으로 다음과 같습니다.

34   public void getBookIds(int id) {
35      try {
36         book.getId(id);    // this method it throws a NullPointerException on line 22
37      } catch (NullPointerException e) {
38         throw new IllegalStateException("A book has a null property", e)
39      }
40   }

이것은 다음과 같은 스택 추적을 제공 할 수 있습니다.

Exception in thread "main" java.lang.IllegalStateException: A book has a null property
        at com.example.myproject.Author.getBookIds(Author.java:38)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
        at com.example.myproject.Book.getId(Book.java:22)
        at com.example.myproject.Author.getBookIds(Author.java:36)
        ... 1 more

이것에 대해 다른 점은 "원인"입니다. 때때로 예외에는 여러 "원인"섹션이 있습니다. 이러한 경우 일반적으로 스택 추적에서 가장 낮은 "원인"섹션 중 하나 인 "근본 원인"을 찾으려고합니다. 우리의 경우 다음과 같습니다.

Caused by: java.lang.NullPointerException <-- root cause
        at com.example.myproject.Book.getId(Book.java:22) <-- important line

다시 말하지만,이 예외와 함께 우리는 라인을보고 싶어 22Book.java가 발생할 수 있습니다 무엇을 참조하십시오 NullPointerException여기.

라이브러리 코드를 사용한 더 어려운 예제

일반적으로 스택 추적은 위의 두 가지 예보다 훨씬 더 복잡합니다. 다음은 예입니다 (긴 하나이지만 여러 수준의 연결 예외를 보여줍니다).

javax.servlet.ServletException: Something bad happened
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: com.example.myproject.MyProjectServletException
    at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
    ... 27 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822)
    at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
    at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
    at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
    at $Proxy19.save(Unknown Source)
    at com.example.myproject.MyEntityService.save(MyEntityService.java:59) <-- relevant call (see notes below)
    at com.example.myproject.MyServlet.doPost(MyServlet.java:164)
    ... 32 more
Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    at org.hsqldb.jdbc.Util.throwError(Unknown Source)
    at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)
    ... 54 more

이 예에는 더 많은 것이 있습니다. 우리가 주로 염려하는 것은 우리 코드 에서 나온 메소드를 찾는 com.example.myproject입니다. 두 번째 예 (위)에서 먼저 다음과 같은 근본 원인을 찾아 보겠습니다.

Caused by: java.sql.SQLException

그러나 그 아래의 모든 메서드 호출은 라이브러리 코드입니다. 따라서 위의 "원인"으로 이동하여 코드에서 시작된 첫 번째 메서드 호출을 찾습니다.

at com.example.myproject.MyEntityService.save(MyEntityService.java:59)

이전 예제와 마찬가지로 MyEntityService.java온라인 에서 봐야 합니다 59. 왜냐하면이 오류가 발생한 곳이기 때문입니다 (SQLException이 오류를 나타 내기 때문에 무엇이 잘못되었는지는 약간 분명하지만 디버깅 절차는 우리가 추구하는 것입니다).


나는이 답변을 게시하고 있으므로 최상위 답변 (활동별로 정렬했을 때)은 명백한 잘못된 답변이 아닙니다.

Stacktrace 란 무엇입니까?

스택 추적은 매우 유용한 디버깅 도구입니다. 포착되지 않은 예외가 발생했을 때 (또는 스택 추적이 수동으로 생성 된 시간) 호출 스택 (해당 지점까지 호출 된 함수 스택)을 보여줍니다. 이는 오류가 발생한 위치뿐만 아니라 해당 코드 위치에서 프로그램이 어떻게 끝났는지 보여주기 때문에 매우 유용합니다. 이것은 다음 질문으로 이어집니다.

예외 란 무엇입니까?

예외는 런타임 환경에서 오류가 발생했음을 알리기 위해 사용하는 것입니다. 인기있는 예는 NullPointerException, IndexOutOfBoundsException 또는 ArithmeticException입니다. 이들 각각은 불가능한 일을하려고 할 때 발생합니다. 예를 들어 Null 개체를 역 참조하려고하면 NullPointerException이 발생합니다.

Object a = null;
a.toString();                 //this line throws a NullPointerException

Object[] b = new Object[5];
System.out.println(b[10]);    //this line throws an IndexOutOfBoundsException,
                              //because b is only 5 elements long
int ia = 5;
int ib = 0;
ia = ia/ib;                   //this line throws an  ArithmeticException with the 
                              //message "/ by 0", because you are trying to
                              //divide by 0, which is not possible.

Stacktraces / 예외를 어떻게 처리해야합니까?

먼저 예외의 원인을 찾으십시오. 예외의 이름을 검색하여 예외의 원인이 무엇인지 알아보십시오. 대부분의 경우 잘못된 코드로 인해 발생합니다. 위의 주어진 예에서 모든 예외는 잘못된 코드로 인해 발생합니다. 따라서 NullPointerException 예제의 경우 a해당 시점에 null이 아닌지 확인할 수 있습니다. 예를 들어 a다음과 같은 검사를 초기화 하거나 포함 할 수 있습니다.

if (a!=null) {
    a.toString();
}

이렇게하면 문제가되는 줄이 실행되지 않습니다 a==null. 다른 예도 마찬가지입니다.

때로는 예외가 발생하지 않는지 확인할 수 없습니다. 예를 들어 프로그램에서 네트워크 연결을 사용하는 경우 컴퓨터가 인터넷 연결을 끊는 것을 막을 수 없습니다 (예 : 사용자가 컴퓨터의 네트워크 연결을 끊는 것을 막을 수 없습니다). 이 경우 네트워크 라이브러리에서 예외가 발생할 수 있습니다. 이제 예외를 잡아서 처리 해야합니다. 즉, 네트워크 연결이있는 예에서 연결을 다시 열거 나 사용자에게 알려야합니다. 또한 catch를 사용할 때마다 항상 catch하려는 예외 만 catch하고 다음 과 같은 광범위한 catch 문을 사용하지 마십시오.catch (Exception e)모든 예외를 포착합니다. 그렇지 않으면 실수로 잘못된 예외를 포착하고 잘못된 방식으로 반응 할 수 있기 때문에 이것은 매우 중요합니다.

try {
    Socket x = new Socket("1.1.1.1", 6789);
    x.getInputStream().read()
} catch (IOException e) {
    System.err.println("Connection could not be established, please try again later!")
}

왜 사용하지 말아야 catch (Exception e)합니까?

모든 예외를 포착해서는 안되는 이유를 보여주는 간단한 예를 사용하겠습니다.

int mult(Integer a,Integer b) {
    try {
        int result = a/b
        return result;
    } catch (Exception e) {
        System.err.println("Error: Division by zero!");
        return 0;
    }
}

어떤이 코드가 시도된다 캐치하는 것입니다 ArithmeticException0으로 가능한 부문에 의한 그러나 그것은 또한 가능한 잡는다 NullPointerException경우 발생되는 a또는 b됩니다 null. 즉, a를 얻을 수 NullPointerException있지만 ArithmeticException으로 취급하고 아마도 잘못된 일을 할 것입니다. 가장 좋은 경우에는 여전히 NullPointerException이 있다는 것을 놓칩니다. 그런 것들은 디버깅을 훨씬 더 어렵게 만들므로 그렇게하지 마십시오.

TLDR

  1. Figure out what is the cause of the exception and fix it, so that it doesn't throw the exception at all.
  2. If 1. is not possible, catch the specific exception and handle it.

    • Never just add a try/catch and then just ignore the exception! Don't do that!
    • Never use catch (Exception e), always catch specific Exceptions. That will save you a lot of headaches.

To add on to what Rob has mentioned. Setting break points in your application allows for the step-by-step processing of the stack. This enables the developer to use the debugger to see at what exact point the method is doing something that was unanticipated.

Since Rob has used the NullPointerException (NPE) to illustrate something common, we can help to remove this issue in the following manner:

if we have a method that takes parameters such as: void (String firstName)

우리 코드 firstName에서 값 포함하는 것을 평가하고 싶을 것입니다. 다음과 같이 할 것입니다 :if(firstName == null || firstName.equals("")) return;

위의 내용 firstName은 안전하지 않은 매개 변수로 사용 하는 것을 방지합니다 . 따라서 처리하기 전에 null 검사를 수행하면 코드가 제대로 실행되는지 확인할 수 있습니다. 메서드가있는 객체를 활용하는 예제를 확장하려면 여기를 참조하십시오.

if(dog == null || dog.firstName == null) return;

위의 순서는 null을 확인하는 적절한 순서입니다. 기본 개체 (이 경우에는 dog)로 시작한 다음 처리 전에 모든 것이 유효한지 확인하기 위해 가능성 트리를 따라 걷기 시작합니다. 순서가 바뀌면 NPE가 잠재적으로 던져지고 프로그램이 중단 될 수 있습니다.


Throwable 제품군에서 제공하는 스택 추적 기능이 하나 더 있습니다 . 스택 추적 정보 조작 할 수 있습니다.

표준 동작 :

package test.stack.trace;

public class SomeClass {

    public void methodA() {
        methodB();
    }

    public void methodB() {
        methodC();
    }

    public void methodC() {
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

스택 추적 :

Exception in thread "main" java.lang.RuntimeException
    at test.stack.trace.SomeClass.methodC(SomeClass.java:18)
    at test.stack.trace.SomeClass.methodB(SomeClass.java:13)
    at test.stack.trace.SomeClass.methodA(SomeClass.java:9)
    at test.stack.trace.SomeClass.main(SomeClass.java:27)

Manipulated stack trace:

package test.stack.trace;

public class SomeClass {

    ...

    public void methodC() {
        RuntimeException e = new RuntimeException();
        e.setStackTrace(new StackTraceElement[]{
                new StackTraceElement("OtherClass", "methodX", "String.java", 99),
                new StackTraceElement("OtherClass", "methodY", "String.java", 55)
        });
        throw e;
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

Stack trace:

Exception in thread "main" java.lang.RuntimeException
    at OtherClass.methodX(String.java:99)
    at OtherClass.methodY(String.java:55)

To understand the name: A stack trace is a a list of Exceptions( or you can say a list of "Cause by"), from the most surface Exception(e.g. Service Layer Exception) to the deepest one (e.g. Database Exception). Just like the reason we call it 'stack' is because stack is First in Last out (FILO), the deepest exception was happened in the very beginning, then a chain of exception was generated a series of consequences, the surface Exception was the last one happened in time, but we see it in the first place.

Key 1:A tricky and important thing here need to be understand is : the deepest cause may not be the "root cause", because if you write some "bad code", it may cause some exception underneath which is deeper than its layer. For example, a bad sql query may cause SQLServerException connection reset in the bottem instead of syndax error, which may just in the middle of the stack.

-> Locate the root cause in the middle is your job. enter image description here

Key 2:Another tricky but important thing is inside each "Cause by" block, the first line was the deepest layer and happen first place for this block. For instance,

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
           at com.example.myproject.Author.getBookTitles(Author.java:25)
               at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Book.java:16 was called by Auther.java:25 which was called by Bootstrap.java:14, Book.java:16 was the root cause. Here attach a diagram sort the trace stack in chronological order. enter image description here


Just to add to the other examples, there are inner(nested) classes that appear with the $ sign. For example:

public class Test {

    private static void privateMethod() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws Exception {
        Runnable runnable = new Runnable() {
            @Override public void run() {
                privateMethod();
            }
        };
        runnable.run();
    }
}

Will result in this stack trace:

Exception in thread "main" java.lang.RuntimeException
        at Test.privateMethod(Test.java:4)
        at Test.access$000(Test.java:1)
        at Test$1.run(Test.java:10)
        at Test.main(Test.java:13)

The other posts describe what a stack trace is, but it can still be hard to work with.

If you get a stack trace and want to trace the cause of the exception, a good start point in understanding it is to use the Java Stack Trace Console in Eclipse. If you use another IDE there may be a similar feature, but this answer is about Eclipse.

First, ensure that you have all of your Java sources accessible in an Eclipse project.

Then in the Java perspective, click on the Console tab (usually at the bottom). If the Console view is not visible, go to the menu option Window -> Show View and select Console.

Then in the console window, click on the following button (on the right)

Consoles button

and then select Java Stack Trace Console from the drop-down list.

Paste your stack trace into the console. It will then provide a list of links into your source code and any other source code available.

This is what you might see (image from the Eclipse documentation):

Diagram from Eclipse documentation

The most recent method call made will be the top of the stack, which is the top line (excluding the message text). Going down the stack goes back in time. The second line is the method that calls the first line, etc.

If you are using open-source software, you might need to download and attach to your project the sources if you want to examine. Download the source jars, in your project, open the Referenced Libraries folder to find your jar for your open-source module (the one with the class files) then right click, select Properties and attach the source jar.

참고URL : https://stackoverflow.com/questions/3988788/what-is-a-stack-trace-and-how-can-i-use-it-to-debug-my-application-errors

반응형