좋은 예외 메시지를 작성하는 방법


101

현재 코드 검토를 수행 중이며 주목해야 할 사항 중 하나는 예외 메시지 가 예외가 발생한 위치 를 반복 하는 것처럼 보이는 예외의 수입니다 . 예 :

throw new Exception("BulletListControl: CreateChildControls failed.");

이 메시지의 세 가지 항목 모두 나머지 예외에서 해결할 수 있습니다. 스택 추적에서 클래스와 메서드를 알고 실패했습니다 (예외가 있기 때문에).

예외 메시지에 어떤 메시지를 넣었는지 생각하게했습니다. (예를 들어이 아직 존재하지 않는 경우 우선 일반 이유로, 예외 클래스를 생성 PropertyNotFoundException- 이유 ), 그리고 내가 던질 때 메시지는 "예를 들어 (잘못된 노드 1234에 속성을 'IDontExist'을 (를) 찾을 수 없습니다 것을 나타냅니다 "- 무엇을 ). 어디에 StackTrace있습니다. 는 경우 (해당하는 경우) 로그에 끝낼 수 있습니다. 방법 입니다 개발자가 해결 (및 수정)하기 위해

예외를 던지기위한 다른 팁이 있습니까? 특히 새 유형 작성 및 예외 메시지와 관련하여.


4
로그 파일 용이거나 사용자에게 제공 할 수 있습니까?
Jon Hopkins

5
디버깅 전용. 그들은 로그에 끝날 수 있습니다. 사용자에게 표시되지 않습니다. 나는 사용자에게 예외 메시지를 제시하는 팬이 아닙니다.
Colin Mackay

답변:


70

나는 예외 이후에 오는 것에 대해 더 많은 대답을 할 것입니다 : 무엇이 좋으며 소프트웨어가 어떻게 행동 해야하는지, 사용자는 예외로 무엇을해야합니까? 경력 초기에 알게 된 훌륭한 기술은 항상 문제와 오류를 컨텍스트, 문제 및 솔루션의 세 부분으로보고하는 것입니다. 이 원칙을 사용하면 오류 처리가 엄청나게 바뀌고 운영자가 소프트웨어를 훨씬 더 잘 사용할 수 있습니다.

몇 가지 예가 있습니다.

Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.

이 경우 운영자는 수행 할 작업과 영향을받는 파일을 정확히 알고 있습니다. 또한 연결 풀링 변경이 수행되지 않았으며 반복되어야한다는 것을 알고 있습니다.

Context: Sending email to 'abc@xyz.com' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem.  The email will be sent later. You may want to tell 'abc@xyz.com' about this problem.

서버 측 시스템을 작성하고 운영자는 일반적으로 기술에 정통한 1 차 지원입니다. 대상은 다르지만 동일한 정보를 포함하는 데스크탑 소프트웨어에 대해 다르게 메시지를 작성합니다.

이 기술을 사용하면 몇 가지 놀라운 일이 발생합니다. 소프트웨어 개발자는 종종 자신의 코드로 문제를 해결하는 방법을 아는 것이 가장 좋습니다. 따라서 코드를 작성할 때 이러한 방식으로 솔루션을 인코딩하면 솔루션을 찾는 데 어려움을 겪는 최종 사용자에게 정보가 누락되기 때문에 최종 사용자에게 큰 이점이됩니다 소프트웨어가 정확히 무엇을하고 있는지. Oracle 오류 메시지를 읽은 사람은 내가 무엇을 의미하는지 알게 될 것입니다.

두 번째로 놀라운 점은 예외에서 해결책을 설명하려고 시도하고 "X를 확인하고 A와 B가 다른 경우 C"라고 쓰는 경우입니다. 이것은 귀하의 예외가 잘못된 장소에서 점검되고 있다는 매우 분명하고 명백한 신호입니다. 프로그래머는 코드에서 사물을 비교할 수있는 능력이 있으므로 "if" 문을 코드에서 실행해야하는 이유는 무엇입니까? 코드가 더 깊어서 누군가가 게으른 일을하고 많은 수의 메소드에서 IOException을 발생시키고 호출 코드 블록에서 모든 메소드에서 잠재적 인 오류를 발견하여 어떤 일이 잘못되었는지, 구체적으로 무엇인지 설명 할 수 없습니다.상황과 해결 방법. 따라서 작업자가 취해야하는 단계를 올바르게 표현할 수 있도록 미세한 결점 오류를 작성하고 코드의 올바른 위치에서 오류를 포착하여 처리 할 수 ​​있습니다.

한 회사에는 소프트웨어를 정말 잘 알고 있으며 오류보고 및 제안 된 솔루션을 강화한 자체 "실행 서적"을 유지 한 최고 수준의 운영자가있었습니다. 이를 인식하기 위해 소프트웨어는 예외적으로 런 북에 대한 위키 링크를 포함하여 기본 설명을 사용할 수 있었으며 시간이 지남에 따라 운영자의 고급 토론 및 관찰에 대한 링크를 제공했습니다.

이 기술을 시험해 볼 수있는 훈련을 받았다면 자신의 예외를 만들 때 코드에서 예외 이름을 지정 해야하는 것이 훨씬 더 분명해집니다. NonRecoverableConfigurationReadFailedException 은 연산자에 대해보다 자세하게 설명하려는 내용의 약어입니다. 나는 장황한 것을 좋아하며 내 코드를 만지는 다음 개발자가 해석하기가 더 쉬울 것이라고 생각합니다.


1
+1 좋은 시스템입니다. 어느 것이 더 중요한가 : 정보가 확실하게 전달되는지 또는 짧은 단어를 사용하는지?
Michael K

3
+1 제가 포함 컨텍스트 문제 용액의 용액에 대한 원하는
경우 Webdev

1
이 기술은 매우 유용합니다. 나는 확실히 그것을 사용할 것입니다.
Kid Diamond

컨텍스트 는 불필요한 데이터입니다. 이미 스택 추적에 있습니다. 갖는 솔루션은 유용 / 항상 가능한 것은 바람직하지만입니다. 대부분의 문제는 가지 정상적으로 응용 프로그램을 중지 또는 보류중인 작업을 무시하고 다시 응용 프로그램의 주요 실행 루프 다음에 당신이 그 예외 클래스의 이름은해야한다 ... 성공할 것으로 기대하고있다 솔루션 에 대한 명백한 될 것입니다 FileNotFound또는 ConnectException)) 당신은 무엇을 해야할지
gavenkoa

2
@ThomasFlinkow 예제에서 추적은 스택 추적에 init (), execute () 및 cleanup ()을 갖습니다. 라이브러리의 좋은 명명 스키마와 깨끗하고 이해하기 쉬운 API를 사용하면 문자열 설명이 필요하지 않습니다. 시스템 전체에 고장난 상태를 유지하지 마십시오. 고유 ID의 추적 및 로깅 레코드는 애플리케이션 플로우 / 상태를 설명 할 수 있습니다.
gavenkoa

23

이 최근 질문 나는 예외가 모든 메시지를 포함 할 수 없습니다 지점을했다. 제 생각에는 그들이하는 사실은 큰 오해입니다. 내가 제안하는 것은

예외의 "메시지"는 예외의 (정규화 된) 클래스 이름 입니다.

예외는 발생한 일에 대해 가능한 한 많은 세부 정보를 자체 멤버 변수 내에 포함해야합니다. 예를 들어,는 IndexOutOfRangeException유효하지 않은 것으로 판명 된 색인 값과 예외가 발생했을 때 유효한 상한 및 하한 값을 포함해야합니다. 이런 식으로 리플렉션을 사용하면 다음과 같은 메시지를 자동으로 구성 할 수 있습니다. IndexOutOfRangeException: index = -1; min=0; max=5스택 추적과 함께이 문제는 문제를 해결하는 데 필요한 모든 객관적인 정보 여야합니다. "인덱스 -1이 0과 5 사이가 아니 었습니다"와 같은 예쁜 메시지로 형식을 지정해도 값이 추가되지 않습니다.

특정 예에서 NodePropertyNotFoundException클래스에는 찾을 수없는 속성 이름과 속성이 포함되지 않은 노드에 대한 참조가 포함됩니다. 이것은 중요 합니다. 노드 이름을 포함 해서는 안됩니다 . 실제 노드에 대한 참조를 포함해야합니다. 특별한 경우에는 이것이 필요하지 않을 수도 있지만 원칙과 선호하는 사고의 문제입니다. 예외를 구성 할 때의 주요 관심사는 예외를 포착 할 수있는 코드로 사용할 수 있어야한다는 것입니다. 인간의 유용성은 중요하지만 부차적 인 관심사입니다.

이것은 경력의 어느 시점에서 목격했을지도 모르는 매우 실망스러운 상황을 처리합니다. 여기서 메시지 텍스트 내에서 일어난 일에 대한 중요한 정보가 포함되어 있지만 멤버 변수 내에서는 발생하지 않는 중요한 정보가 포함되어있을 수 있습니다. 메시지 텍스트가 향후 버전의 기본 레이어에서 동일하게 유지되기를 기대하고 프로그램이있을 때 메시지 텍스트가 외국어가 아닌 것을기도하기 위해 무슨 일이 있었는지 파악하기 위해 텍스트의 문자열 구문 분석을 수행해야했습니다. 다른 국가에서 실행됩니다.

물론 예외의 클래스 이름은 예외의 메시지이므로 예외의 멤버 변수는 특정 세부 사항이므로 모든 다른 메시지를 전달하려면 많은 예외가 필요합니다. 괜찮습니다.

때때로, 코드를 작성할 때, 우리 throw는 새로운 예외 클래스를 만들기 위해 우리가 할 수있는 일을 방해하지 않고 명령문 을 빠르게 코딩 하고 코드 작성을 계속 하려는 잘못된 상황을 접하게 됩니다. 바로 거기에 던져. 이 경우 GenericException실제로 문자열 메시지를 생성 시간 매개 변수로 받아들이는 클래스가 있지만이 예외 클래스의 생성자에는이 클래스의 FIXME XXX TODO모든 단일 인스턴스화가 소프트웨어 시스템이 출시되기 전에, 바람직하게는 코드가 커밋되기 전에 좀 더 특화된 예외 클래스의 인스턴스화로 대체되었습니다.


8
C ++과 같이 GC가없는 언어를 사용하는 경우 임의의 데이터에 대한 참조를 스택을 보내는 예외에 넣는 데 매우주의해야합니다. 당신이 참조하는 것은 예외가 잡힐 때 파괴되었을 가능성이 있습니다.
Sebastian Redl

4
@SebastianRedl 맞습니다. node객체가 using-disposable (C #) 또는 try-with-resources (Java) 절에 의해 보호되는 경우 C # 및 Java에도 동일하게 적용될 수 있습니다 . 예외와 함께 저장된 객체는 폐기 / 폐쇄되어 불법입니다. 예외가 처리되는 장소에서 유용한 정보를 얻기 위해 액세스합니다. 이 경우 객체 자체에 대한 일종의 요약이 예외 내에 저장되어야한다고 가정합니다. 나는 모든 경우에 이것을 일반적으로 처리하는 바보 같은 방법을 생각할 수 없다.
Mike Nakis

13

일반적으로 예외는 개발자 가 유용한 정보 (예상 값, 실제 값, 가능한 원인 / 해결 방법 등)를 제공 하여 원인찾아내는 데 도움이 됩니다.

내장 유형 중 어느 것도 의미가없는 경우 새 예외 유형을 작성해야합니다 . 특정 유형을 사용하면 다른 개발자가 특정 예외를 잡아서 처리 할 수 ​​있습니다. 개발자가 예외를 처리하는 방법을 알고 있지만 유형이 Exception인 경우 제대로 처리 할 수 ​​없습니다.


+1-예상 값과 실제 값이 매우 유용합니다. 질문에 주어진 예에서, 당신은 단순히 방법이 실패했다고 말하지 말고 그것이 왜 실패했는지 (기본적으로, 실패한 정확한 명령과 실패를 야기한 상황)
Felix Dombek

4

.NET에서는 throw new Exception("...")질문의 저자가 보여준 것처럼 절대로하지 마십시오 . 예외는 근본 예외 유형이며 직접 발생해서는 안됩니다. 대신 파생 된 .NET 예외 유형 중 하나를 발생 시키거나 예외 (또는 다른 예외 유형)에서 파생되는 사용자 정의 예외를 작성하십시오.

왜 예외를 던지지 않습니까? Exception을 던지면 예외를 설명 할 것이없고 호출 코드 catch(Exception ex) { ... }가 일반적으로 좋지 않은 코드를 작성하도록 강요하기 때문에! :-).


2

예외에 "추가"하고자하는 것은 예외 또는 스택 추적에 고유하지 않은 데이터 요소입니다. 그것들이 "메시지"의 일부인지 또는 기록 될 때 첨부되어야하는지 여부는 흥미로운 질문입니다.

이미 언급했듯이, 예외는 아마도 당신에게 무엇을 말해 줄 것입니다. 스택 추적은 아마도 당신에게 어디를 말해 줄지 모르지만 "왜"가 한두 줄을 들여다 보며 " 물론! " 이것은 프로덕션 코드에서 오류를 기록 할 때 더욱 그렇습니다. 테스트 시스템에는없는 실제 시스템으로가는 잘못된 데이터로 인해 너무 자주 물 렸습니다. 오류를 일으키는 (또는 기여하는) 데이터베이스의 ID가 레코드에 무엇인지 아는 것만 큼 간단합니다.

So ... 나열된 또는 .NET의 경우 기록 된 예외 데이터 수집에 추가되었습니다 (cf @Plip!).

  • 매개 변수
  • ADO.NET 또는 Linq가 SQL 또는 이와 유사한 것으로 반환 한 추가 데이터 (조금 흥미로울 수도 있습니다).
  • 다른 것이 분명하지 않을 수 있습니다.

물론 초기 오류 보고서 / 로그에 이러한 정보가 없을 때까지 필요한 사항을 알 수 없습니다. 당신이 깨닫지 못하는 것들이 필요할 때까지 얻을 수 있습니다.


0

예외 무엇입니까 ?

(1) 사용자에게 무언가 잘못되었다고 말하는가?

코드가 예외보다 "더 나은"것을 방해하고 보여 주어야하기 때문에 이것은 최후의 수단이어야합니다.

"오류"메시지는 무엇이 잘못되었고 사용자가 오류 상태에서 복구 하기 위해 무엇을 있는지 명확하고 간결하게 표시해야 합니다.

예 : "이 버튼을 다시 누르지 마십시오"

(2) 잘못되었다고 개발자에게 알리는가?

이것은 후속 분석을 위해 파일에 로그인하는 것입니다. 스택 추적은 개발자 말할 것이다 코드가 파산을; 메시지는 다시 무엇이 잘못되었는지 표시해야합니다 .

(3) 예외 처리기 (예 : 코드)에 문제가 있다고 말하고 있습니까?

유형 예외는보고가 표시되고있는 예외 핸들러를 결정합니다 속성 예외 객체에 정의 핸들러가 처리 할 수 있습니다.

예외 메시지는 전적으로 관련이 없습니다 .


-3

도움이 될 수 있으면 새 유형을 만들지 마십시오. 추가적인 혼란과 복잡성을 야기 할 수 있으며 더 많은 코드를 유지 관리 할 수 ​​있습니다. 코드를 확장해야 할 수도 있습니다. 예외 계층을 설계하려면 많은 테스트와 테스트가 필요합니다. 사후 생각이 아닙니다. 내장 언어 예외 계층을 사용하는 것이 가장 좋습니다.

예외 메시지의 내용은 메시지 수신자에 따라 달라 지므로 그 사람의 신발에 몸을 맡겨야합니다.

지원 엔지니어는 가능한 빨리 오류의 원인을 식별 할 수 있어야합니다. 간단한 설명 문자열과 문제 해결에 도움이되는 데이터를 포함 시키십시오. 가능하면 스택 추적을 항상 포함 시키십시오. 이것이 유일한 정보원이 될 것입니다.

시스템의 일반 사용자에게 오류를 표시하는 것은 오류 유형에 따라 다릅니다. 예를 들어 사용자가 다른 입력을 제공하여 문제를 해결할 수 있으면 간결한 설명 메시지가 필요합니다. 사용자가 문제를 해결할 수없는 경우 오류가 발생했음을 알리고 지원할 오류를 기록 / 보내는 것이 가장 좋습니다 (위의 지침 사용).

또한 큰 "HALT ERROR!"를 포기하지 마십시오. 상. 그것은 오류입니다-그것은 세상의 끝이 아닙니다.

요약하자면 시스템의 액터 및 사용 사례에 대해 생각하십시오. 그 사용자의 신발에 자신을 두십시오. 도움이 되길. 착하게 굴 어라. 시스템 디자인에서이 점을 미리 생각해보십시오. 사용자의 관점에서 볼 때, 이러한 예외 사례와 시스템의 예외 처리 방식은 시스템의 일반적인 사례와 마찬가지로 중요합니다.


10
동의하지 않습니다. 언어 API가 정확한 요구를 충족시키지 못하는 경우 자체 예외를 구현해야하는 많은 이유가 있습니다. 한 가지 이유는 여러 문제가 발생할 수있는 방법에서 다른 유형의 예외에 대해 다른 catch 절을 작성하여 정확한 문제에 대응할 수 있기 때문입니다. 다른 예외는 다른 추상화 계층을 나타내는 여러 계층의 예외를 분리 할 수 ​​있다는 것입니다. 여기서 예외가 속하는 정확한 계층은 해당 유형으로 인코딩 될 수 있습니다. "Exception"또는 "IllegalStateException"을 사용하면 메시지 문자열이 그다지 도움이되지 않습니다.
Felix Dombek

1
또한 동의하지 않습니다. 예외 유형은 호출 코드에 적합해야합니다. 예를 들어 프레임 워크를 호출하고 이것이 FileDoesNotExistException을 내부적으로 발생시키는 경우 프레임 워크의 호출자로서 의미가 없습니다. 대신 사용자 정의 예외를 작성하고 던져진 예외를 내부 예외로 전달하는 것이 좋습니다.
bytedev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.