exception.printStackTrace ()가 왜 나쁜 습관으로 간주됩니까?


126

많이재료를 밖으로 예외의 스택 트레이스를 인쇄하는 것은 잘못된 방법입니다 제안합니다.

예를 들어 Checkstyle의 RegexpSingleline 확인에서 :

이 검사는 [...] ex.printStacktrace () 호출과 같은 일반적인 나쁜 습관을 찾는 데 사용할 수 있습니다.

그러나 스택 추적이 예외의 원인을 추적하는 데 매우 유용하기 때문에 유효한 이유를 제공하는 곳을 찾기 위해 고심하고 있습니다. 내가 알고있는 것 :

  1. 최종 사용자에게 스택 추적이 표시되지 않아야합니다 (사용자 경험 및 보안 목적으로)

  2. 스택 추적 생성은 비교적 비싼 프로세스입니다 (대부분의 '예외적'환경에서는 문제가되지는 않지만)

  3. 많은 로깅 프레임 워크가 스택 추적을 인쇄합니다 (우리는 그렇지 않습니다. 쉽게 변경할 수 없습니다).

  4. 스택 추적을 인쇄한다고해서 오류가 처리되지는 않습니다. 다른 정보 로깅 및 예외 처리와 함께 사용해야합니다.

코드에 스택 추적을 인쇄하지 않는 다른 이유는 무엇입니까?


12
정기적으로 문제를 해결 해야하는 사람으로서 무언가 잘못되었을 때 스택 추적 인쇄를 생략 하지 않습니다 . 예, 사용자에게 표시하지 말고 오류 로그에 덤프하십시오.
Paul Grime

4
정말로 다른 이유가 필요합니까? 나는 당신이 자신의 질문에 상당히 잘 대답했다고 생각합니다. 그러나 예외적으로 스택 추적을 인쇄해야합니다. :)
Neil

답변:


125

Throwable.printStackTrace()스택 추적을 System.errPrintStream에 씁니다 . System.errJVM 프로세스 의 스트림 및 기본 표준 "오류"출력 스트림은 다음을 통해 리디렉션 될 수 있습니다.

  • 에서 System.setErr()가리키는 대상을 변경하는 호출 System.err.
  • 또는 프로세스의 오류 출력 스트림을 리디렉션하여. 오류 출력 스트림이 파일 / 장치로 리디렉션 될 수 있습니다
    • 직원이 내용을 무시할 수있는 경우
    • 파일 / 장치는 로그 회전이 불가능할 수 있으며, 파일 / 장치의 기존 내용을 아카이브하기 전에 열린 파일 / 장치 핸들을 닫으려면 프로세스를 다시 시작해야한다고 가정합니다.
    • 또는 파일 / 장치는의 경우와 같이 실제로 기록 된 모든 데이터를 버립니다 /dev/null.

위에서 언급 한 것처럼, 호출 Throwable.printStackTrace()은 유효한 (좋은 / 큰 것은 아닌) 예외 처리 동작을 구성합니다.

  • System.err애플리케이션 수명 기간 동안 재 할당 되지 않은 경우 ,
  • 응용 프로그램이 실행되는 동안 로그 회전이 필요하지 않은 경우
  • 응용 프로그램의 허용 / 설계 로깅 방법은 System.err(및 JVM의 표준 오류 출력 스트림)에 기록하는 것입니다.

대부분의 경우 위의 조건이 충족되지 않습니다. JVM에서 실행되는 다른 코드를 인식하지 못하고 로그 파일의 크기 나 프로세스의 런타임 기간을 예측할 수 없으며 잘 설계된 로깅 방법은 "머신 분석 가능"로그 파일 작성을 중심으로 수행됩니다 (a 알려진 목적지에있는 로거의 선호하지만 선택적인 기능).

마지막으로,의 출력은 Throwable.printStackTrace()다른 컨텐트에 쓰여질 것입니다 System.err( System.out둘 다 동일한 파일 / 장치로 리디렉션 된 경우 에도 ). 이는 예외 관련 데이터를 쉽게 구문 분석 할 수 없기 때문에 처리해야하는 성가심 (단일 스레드 앱의 경우)입니다. 게다가 다중 스레드 응용 프로그램은 Throwable.printStackTrace() 스레드로부터 안전하지 않기 때문에 매우 혼란스러운 로그를 생성 할 가능성이 높습니다 .

System.err여러 스레드 Throwable.printStackTrace()가 동시에 호출 될 때 스택 추적 쓰기를 동기화하는 동기화 메커니즘이 없습니다 . 이 문제를 해결하려면 실제로 코드와 관련된 모니터에서 System.err(및 System.out대상 파일 / 장치가 동일한 경우) 코드를 동기화 해야하므로 로그 파일 상태를 유지하는 데 비용이 많이 듭니다. 예를 들어, ConsoleHandlerStreamHandler클래스는로 제공되는 로깅 기능에서 콘솔에 로그 레코드를 추가해야합니다 java.util.logging. 로그 레코드 공개의 실제 조작이 동기화됩니다. 로그 레코드 공개를 시도하는 모든 스레드는 또한StreamHandler예. System.out/를 사용하여 인터리브되지 않은 로그 레코드를 System.err보장하려면 동일한 메시지를 직렬화 가능한 방식으로 이러한 스트림에 게시해야합니다.

위의 모든 사항과 Throwable.printStackTrace()실제로 유용한 매우 제한된 시나리오를 고려할 때 종종 호출하는 것이 나쁜 습관임을 알 수 있습니다.


이전 단락 중 하나에서 인수를 확장하면 Throwable.printStackTrace콘솔에 쓰는 로거와 함께 사용하는 것도 좋지 않습니다. 이는 로거가 다른 모니터에서 동기화하는 반면 애플리케이션이 인터리브 된 로그 레코드를 원하지 않는 경우 다른 모니터에서 동기화하는 이유 때문입니다. 응용 프로그램에서 동일한 대상에 쓰는 두 개의 서로 다른 로거를 사용할 때도 인수가 유효합니다.


좋은 답변, 감사합니다. 그러나 대부분의 경우 소음이 필요하거나 필요하지 않다는 데 동의하지만 절대적으로 중요 할 때가 있습니다.
Chris Knight

1
@ 크리스 네, 당신이 있지만 사용할 수없는 시간은 System.out.println그리고 Throwable.printStackTrace물론, 개발자 판단이 필요합니다. 스레드 안전성에 대한 부분이 누락되었다는 것이 약간 걱정되었습니다. 대부분의 로거 구현을 살펴보면 System.err또는에서 모니터를 얻지 않더라도 로그 레코드가 작성된 부분 (콘솔에조차도)이 동기화됨을 알 수 System.out있습니다.
Vineet Reynolds

2
PrintStream 또는 PrintWriter 객체를 매개 변수로 사용하는 printStackTrace 재정의에는 이러한 이유 중 어느 것도 적용되지 않습니다.
ESRogs

1
@Geek, 후자는 값이 2 인 프로세스 파일 디스크립터입니다. 전자는 단지 추상화입니다.
Vineet Reynolds

1
printStackTrace ()의 JDK 소스 코드에서 System.err PrintStream을 잠그기 위해 동기화를 사용하므로 스레드 안전 메소드 여야합니다.
EyouGo

33

여러 문제를 만지고 있습니다.

1) 스택 추적은 최종 사용자에게 보이지 않아야합니다 (사용자 경험 및 보안 목적으로)

예, 최종 사용자의 문제를 진단 할 수 있어야하지만 최종 사용자는 다음 두 가지 이유로이를 볼 수 없습니다.

  • 그것들은 매우 모호하고 읽을 수 없으며 응용 프로그램은 사용자에게 친숙하지 않습니다.
  • 최종 사용자에게 스택 추적을 표시하면 잠재적 인 보안 위험이 발생할 수 있습니다. 내가 틀렸다면 PHP를 수정하십시오 .PHP는 실제로 스택 추적에 함수 매개 변수를 인쇄합니다.

2) 스택 추적을 생성하는 것은 상대적으로 비용이 많이 드는 프로세스입니다 (그러나 대부분의 '예외'상황에서는 문제가되지는 않습니다)

스택 추적 생성은 예외가 생성 / 발생 될 때 발생합니다 (따라서 예외가 발생하면 가격이 함께 제공됨). 인쇄 비용이 그렇게 많이 들지 않습니다. 실제로 Throwable#fillInStackTrace()사용자 정의 예외를 무시 하여 간단한 GOTO 문만큼 저렴하게 예외를 던질 수 있습니다 .

3) 많은 로깅 프레임 워크가 스택 추적을 인쇄합니다 (우리는 그렇지 않습니다. 쉽게 변경할 수는 없습니다)

아주 좋은 지적입니다. 여기서 주요 문제는 프레임 워크가 예외를 기록하는 경우 아무것도하지 않지만 (그렇지 않으면 확인하십시오!) 예외를 직접 로그하려면 Logback 또는 Log4J 와 같은 로깅 프레임 워크를 사용 하여 원시 콘솔에 두지 마십시오. 제어하기가 매우 어렵 기 때문입니다.

로깅 프레임 워크를 사용하면 스택 추적을 파일, 콘솔로 쉽게 리디렉션하거나 지정된 전자 메일 주소로 보낼 수도 있습니다. 하드 코드 printStackTrace()를 사용하면와 함께 살아야합니다 sysout.

4) 스택 추적 인쇄는 오류 처리를 구성하지 않습니다. 다른 정보 로깅 및 예외 처리와 함께 사용해야합니다.

다시 한 번 : SQLException로깅 프레임 워크를 사용하여 전체 스택 추적으로 올바르게 기록하고 " 미안합니다. 현재 요청을 처리 할 수 ​​없습니다 "메시지를 표시하십시오. 사용자가 그 이유에 관심이 있다고 생각하십니까? StackOverflow 오류 화면을 보셨습니까? 매우 유머러스하지만 세부 사항을 밝히지 않습니다 . 그러나 사용자에게 문제를 조사 할 수 있습니다.

그러나 그는 합니다 즉시 전화하고 문제를 진단 할 수 있어야합니다. 따라서 적절한 예외 로깅과 사용자 친화적 인 메시지가 모두 필요합니다.


정리 : 항상 예외를 로깅 (바람직하게는 로깅 프레임 워크 사용 )하지만 최종 사용자에게 노출시키지 마십시오. GUI의 오류 메시지에 대해 신중하게 생각하고 개발 모드에서만 스택 추적을 표시하십시오.


당신의 대답은 내가 얻은 것입니다.
Blasanka '

"대부분의 '예외'상황 '. 좋은.
awwsmm

19

우선 printStackTrace () 는 예외가 발생할 때 스택 추적이 채워 지므로 상태가 비싸지 않습니다.

아이디어는 로거 프레임 워크를 통해 로그에 전달되는 모든 것을 전달하여 로깅을 제어 할 수 있도록하는 것입니다. 따라서 printStackTrace를 사용하는 대신 다음과 같은 것을 사용하십시오.Logger.log(msg, exception);


11

나쁜 관행을 구성하지 않습니다 자체의 예외의 스택 트레이스를 인쇄하지만, 유일한 예외가 발생했을 때 스테이시 추적을 인쇄하는 것은 아마 여기에 문제 - 종종 그냥 스택 추적을 인쇄하는 것만으로는 충분하지 않습니다.

또한 catch블록 에서 수행되는 모든 항목이 인 경우 적절한 예외 처리가 수행되지 않는 것으로 의심되는 경향 이 있습니다 e.printStackTrace. 부적절한 처리는 문제가 무시되고 최악의 경우 정의되지 않거나 예기치 않은 상태로 계속 실행되는 프로그램을 의미 할 수 있습니다.

다음 예제를 보자.

try {
  initializeState();

} catch (TheSkyIsFallingEndOfTheWorldException e) {
  e.printStackTrace();
}

continueProcessingAssumingThatTheStateIsCorrect();

여기서는 초기화가 필요한 일부 처리를 계속하기 전에 초기화 처리를 수행하려고합니다.

위의 코드에서 프로그램 continueProcessingAssumingThatTheStateIsCorrect이 문제를 일으킬 수 있는 방법으로 진행하지 못하도록 예외를 포착하고 올바르게 처리해야 합니다.

많은 경우에, e.printStackTrace()어떤 예외가 삼켜지고 있으며 문제가 발생하지 않은 것처럼 처리가 진행될 수 있다는 표시입니다.

이것이 왜 문제가 되었습니까?

잘못된 예외 처리가 널리 보급 된 가장 큰 이유 중 하나는 Eclipse와 같은 IDE e.printStackTrace가 예외 처리를 수행하는 코드를 자동 생성하는 방법 때문일 수 있습니다 .

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

(상기 실제 인 try-catch자동 생성 된 처리 이클립스로 InterruptedException하여 발생 Thread.sleep).

대부분의 응용 프로그램에서는 스택 추적을 표준 오류로 인쇄하는 것만으로는 충분하지 않을 수 있습니다. 부적절한 예외 처리는 많은 경우 응용 프로그램이 예기치 않은 상태로 실행되어 예기치 않은 정의되지 않은 동작을 유발할 수 있습니다.


1
이. 종종 바로 잡기 아니 , 적어도 메소드 레벨에있는 모든에서 제외하는 것은, 더 잡기 스택 추적의 인쇄를하고 문제가 발생하지 것처럼 계속보다.
es

10

이유 목록은 매우 포괄적 인 것 같습니다.

내가 두 번 이상 만난 특히 나쁜 예는 다음과 같습니다.

    try {
      // do stuff
    } catch (Exception e) {
        e.printStackTrace(); // and swallow the exception
    }

위의 코드의 문제점은 처리가 전적으로printStackTrace 호출 로 구성된다는 입니다 . 예외는 실제로 제대로 처리되지 않거나 탈출 할 수 없습니다.

반면에 일반적으로 코드에 예기치 않은 예외가있을 때마다 항상 스택 추적을 기록합니다. 수년 동안이 정책으로 인해 많은 디버깅 시간이 절약되었습니다.

마지막으로, 하나님의 완전한 예외 에 대해 간략하게 설명 합니다.


"예외는 피할 수 없다"-예외가 스택으로 전파되었다는 의미입니까? 로깅은 printStackTrace ()와 어떻게 다릅니 까?
MasterJoe

5

printStackTrace()콘솔에 인쇄합니다. 프로덕션 환경에서는 아무도 그것을보고 있지 않습니다. Suraj가 맞습니다.이 정보를 로거에게 전달해야합니다.


프로덕션 콘솔 출력을주의 깊게 관찰하지만 올바른 요점입니다.
크리스 나이트

주의 깊게보고 의미를 설명 할 수 있습니까? 어떻게 그리고 누구? 어떤 빈도로?
오 친 분

주의 깊게 살펴보면 필요할 때 말을해야했습니다. 앱에 문제가있는 경우 여러 호출 포트 중 하나 인 롤링 로그 파일입니다.
Chris Knight

3

서버 애플리케이션에서 stacktrace는 stdout / stderr 파일을 폭파시킵니다. 일반적으로 컨텍스트와 타임 스탬프 등이 없기 때문에 점점 커지고 쓸모없는 데이터로 채워질 수 있습니다.

예 : 컨테이너로 바람둥이를 사용할 때 catalina.out


좋은 지적. printStackTrace ()를 잘못 사용하면 로깅 파일이 손상되거나 최소한 쓸모없는 정보로 채워질 수 있습니다.
Chris Knight

@ChrisKnight-로깅 파일이 쓸모없는 정보로 어떻게 날아갈 수 있는지 설명하는 기사를 제안 해 주시겠습니까? 감사.
MasterJoe

3

PrintStackTrace ()에 대해 '잘못된'것이기 때문에 '코드 냄새'이기 때문에 나쁜 습관이 아닙니다. 누군가가 예외를 제대로 처리하지 못했기 때문에 PrintStackTrace () 호출이 대부분 발생합니다. 적절한 방식으로 예외를 처리하면 일반적으로 더 이상 StackTrace에 관심이 없습니다.

또한 stderr에 스택 추적을 표시하는 것은 일반적으로 stderr이 아무데도 가지 않기 때문에 프로덕션 환경이 아닌 디버깅 할 때만 유용합니다. 로깅하는 것이 더 합리적입니다. 그러나 PrintStackTrace ()를 예외 로깅으로 바꾸는 것만으로도 여전히 실패했지만 아무 일도 일어나지 않는 것처럼 계속 실행되는 응용 프로그램이 남습니다.


0

일부 사람들은 이미 여기에 언급 한 바와 같이이 문제는 그냥 전화의 경우에 예외 삼키는 함께 e.printStackTrace()catch블록. 스레드 실행을 중지하지 않으며 try 블록 후에 정상 상태에서와 같이 계속됩니다.

그 대신, 예외 ( RuntimeException로커 구성 가능)로 인한 자동 충돌을 피하기 위해 예외에서 복구 (예 : 복구 가능한 경우)하거나 throw 또는 호출자에게 예외를 버블 링해야합니다.


0

@Vineet Reynolds가 얽힌 출력 스트림 문제를 피하려면

stdout에 인쇄 할 수 있습니다. e.printStackTrace(System.out);

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.