Successfully reported this slideshow.
Your SlideShare is downloading. ×

Exception log practical_coding_guide, 예외와 로그 코딩 실용 가이드

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
예외처리가이드
예외처리가이드
Loading in …3
×

Check these out next

1 of 63 Ad

Exception log practical_coding_guide, 예외와 로그 코딩 실용 가이드

Download to read offline

예외 처리 가이드(http://www.slideshare.net/dhrim/ss-2804901)
의 후편입니다.

실제적인 예를 가지고 예외와 로그를 어떻게 처리해 하는지 설명합니다.

예외 처리 가이드(http://www.slideshare.net/dhrim/ss-2804901)
의 후편입니다.

실제적인 예를 가지고 예외와 로그를 어떻게 처리해 하는지 설명합니다.

Advertisement
Advertisement

More Related Content

Slideshows for you (20)

Viewers also liked (20)

Advertisement

Similar to Exception log practical_coding_guide, 예외와 로그 코딩 실용 가이드 (20)

More from 도형 임 (20)

Advertisement

Recently uploaded (20)

Exception log practical_coding_guide, 예외와 로그 코딩 실용 가이드

  1. 1. 예외를 잡고 무언가 했는데, 로그를 남겨야 하나? 로그 레벨은? 어떤 예외 클래스로? 예외 메시지에는 어떤 값을?
  2. 2. 예외 발생
  3. 3. 걍 아무데서나? 그랬더니 중복 여기 여기 여기 예외 발생
  4. 4. 어라 이번엔 누락 예외 발생
  5. 5. 여기서? 예외 발생
  6. 6. 여기서? 예외 발생
  7. 7. 여기서? 예외 발생
  8. 8. 여기서? 예외 발생
  9. 9. 여기서? 예외 발생
  10. 10. 여기서? 예외 발생
  11. 11. 예외의 로그 위치가 고민되면 안된다. 이미 정해져 있어야 한다.
  12. 12. 시스템을 단순화 하면, 호출받은 이쪽과 호출한 저쪽 예외 발생
  13. 13. 이쪽과 저쪽의 구분은 제어 가능 여부 예외 발생 손을 떠났다. 로그를 남기고 싶어도 못남긴다.
  14. 14. 저쪽으로 보내기 전에 로그를 남기자. 중복되지도 누락되지도 않는다. 여기 예외 발생
  15. 15. 혹은 정상상황으로 처리한 곳에서 로그를 남기자 역시 중복되지도 누락되지도 않는다. 여기 예외 발생
  16. 16. 오직 딱 2곳에서만. • 호출한 저쪽으로 넘기기 직전 • 정상상황으로 처리한 곳 그렇지 않을 경우 로그가 중복되거나 누락될 수 있다.
  17. 17. 외부 호출이 아닌 스케쥴링 테스크의 경우는?
  18. 18. 외부 호출이 아닌 스케쥴링 테스크의 경우는? 동일하다. JVM, OS 또는 Framework이 호출한 그 지점이 그 경계이다. 여기 예외 발생 JVM, OS, Framework
  19. 19. • REST 타입의 외부 인터페이스 • 내부 스케쥴링 테스크 Exception Mapper가 예외를 잡 아 적절한 Http Status의 응답을 보낸다. 이 때 로그를 남긴다. 그리고 스케쥴링 테스크에서 로그를 남긴다. Exception Mapper Scheduling Task 여기 여기 REST type interface
  20. 20. exception mapper를 설정하여 HTTP 응답과 로그를 처리. Exception Class HTTP Response Status UserNotFoundException BAD_REQUEST(400) InvalidParameterException BAD_REQUEST(400) WebApplicatoinExceptoin(by Jersey) NOT_FOUND(404) Throwable INTERNAL_SERVER_ERROR(500)
  21. 21. @Provider public class InvalidParameterExceptionMapper implements ExceptionMapper<InvalidParameterException> { @Override public Response toResponse(InvalidParameterException exception) { logger.debug(“request of invalid parameter.”, exception); return Response.status(Status.BAD_REQUEST).build(); } } InvalidParameterException이 던져지면 BAD_REQUEST(400)의 응답 을 보낸다.
  22. 22. @Provider public class DefaultExceptionMapper implements ExceptionMapper<Throwable> { @Override public Response toResponse(Throwable exception) { logger.error(“unhandled exception.”, exception); return Response.status(Status.INTERNAL_SYSTEM_ERROR).build(); } } 다른 exception mapper에 의해 처리되지 못한 모든 Throwable에 대 하여 INTERNAL_SYSTEM_ERROR(500)의 응답을 보낸다.
  23. 23. @Service public class SomeScheduledTask { @Scheduled public void process() { try { … } catch(SomeException e) { logger.warn(...); } catch(Throwable e) { logger.error(...); } } } process() 메소드가 저어쪽 Spring에 호출되는 지점이다. 이 경우 일반 예외뿐 아니라 Throwable로 다 잡고 있다.
  24. 24. • Info : 운영자에게 보이기 위한 정보. • debug : 디버깅을 위한 내용. • warn : 예외는 아니나, 추후 문제가 될 수 있는. • error : 예외가 발생했으나, 정상 처리. • fatal : 예외가 발생했고, 정상처리 못함.
  25. 25. 예 • 8080 포트로 서버가 시작됨. • 백업 스케쥴러가 완료됨. • DB와 특정 url로 연결됨. • 설정파일 some.properties를 /some/path에서 읽음.
  26. 26. 오직 개발자의 디버깅을 위한. 내용에 제약 없다. 많아도 괜찮다.
  27. 27. 예 • 파일 여유 공간이 10% 이하. • 값은 있으나 사용하지 않는 설정.
  28. 28. 예 • 디비 연결이 끊겼으나 재연결하여 처리 • 설정 값이 없어서 default 값으로 처리
  29. 29. 예 • 디비 연결이 끊어지고 재연결 안되어 처리 못함. • null이 될 수 없는 값이 null이어서 처리 못함.
  30. 30. • 사용자의 입력이 잘못되어 던져진 예외. • NullPointerException. • RuntimeException. • 예외가 던져졌는데 정말 예외 상황은 아니다.
  31. 31. 사용자의 입력이 잘못되어 던져진 예외 • 예외라 하더라도 warn의 수준도 아니다. • 하지만 debug 정도는 괜찮겠다. • 대신 로그 설정이 가능하도록 별개의 로거가 있어야.
  32. 32. NullPointerException • 무조건 bug다. • JVM은 NPE가 발생하면 해당 thread를 종료시킨다. • 그렇다면 warn이상이고, fatal 혹은 error이다.
  33. 33. RuntimeException • 역시 JVM은 해당 thread를 종료시킨다. • fatal이다.
  34. 34. 예외가 던져졌는데 정말 예외 상황은 아니다. • 메소드 설계가 잘못된 경우. • 예외를 던지면 안된다.
  35. 35. 외부 인증 서버로 요청하여 인증을 처리한다. • 외부 인증 서버의 접속 정보를 설정 파일에서 얻는다. • 외부 인증 서버로 REST 요청을 하여 응답을 받는다. • id와 password를 입력으로 받는다. • id 존재 여부, password 틀림 여부를 알려주어야 한다. • 인증된 경우 인증 토큰을 반환한다.
  36. 36. • 외부 인증 서버의 접속 정보를 읽지 못한다. • 외부 인증 서버로 접속이 되지 않는다. • 외부 인증 서버로 접속은 되었으나 응답이 없다. • 외부 인증 서버로 응답을 받았으나 5초 지나서 받았다. • timeout 이후 재시도 하여 응답을 받았다. • 외부 인증 서버로 요청되어 응답을 받았으나 잘못된 형식. • id가 존재 하지 않는다. • password가 틀리다.
  37. 37. 상황 던지는 예외 예외 처리 주체 로그 레벨 처리 인증서버의 접속 정보 를 읽지 못한다. SystemFailException (extends RuntimeException) Exception Handler FATAL 시스템 종료 인증서버로 접속이 되 지 않는다. AuthSystemFailException (extends RuntimeException) Exception Handler ERROR 500 응답 인증서버로 접속은 되 었으나 응답이 없다. AuthSystemFailException (extends RuntimeException) Exception Handler ERROR 500 응답 응답을 받았으나 5초 지나서 받았다. 던지지 않는다. 모듈 내부 WARN 정상 처리 timeout 이후 재시도 하여 응답을 받았다. 던지지 않는다. 모듈 내부 WARN 정상 처리 응답을 받았으나 잘못 된 형식. AuthSystemFailException (extends RuntimeException) Exception Handler ERROR 500 응답 id가 존재 하지 않는 다는 응답. AuthFailException 호출한 모듈. InvalidInputException으로 다시 던진다. DEBUG 400 응답 password가 틀리다는 응답. AuthFailException 위와 동일 DEBUG 400 응답
  38. 38. 상황 예외 메시지 or 로그 메시지 or 처리 상세 인증서버의 접속 정보를 읽지 못한다. “loading auth server info failed. properties file=/some/path/…”. 인증서버로 접속이 되지 않는다. “connecting auth server failed. connection info=http://some.url” 인증서버로 접속은 되었으나 응답이 없다. “getting response from auth server failed. connection info=….” 응답을 받았으나 5초 지나서 받았다. “got response from auth server, but too long response time. time=10.3 sec.” timeout 이후 재시도 하여 응답을 받았다. “get response after 1 timeout. connection info=…” 응답을 받았으나 잘못된 형식. “parsing message failed. message=…” id가 존재 하지 않는다는 응답. AuthFailException를 던질 때 NOT_EXIST_ID를 설정한다. 400로 반환할 때 NOT_EXIST_ID를 설정. password가 틀리다는 응답. AuthFailException를 던질 때 INCORRECT_PASSWORD를 설정한다. 400로 반환할 때 INCORRECT_PASSWORD를 설정.
  39. 39. 모두 다음의 3가지 예외를 던진다. • SystemFailException – 구동 불가 • AuthSystemFailException – 장애 발생 상황. 하지만 계속 구동. • AuthFailException – 호출한 곳에서 잡아서 처리한다.
  40. 40. 예외 상황을 처리하기 위해서는 시스템 전체적으로 다음이 전제되어야 한다. • SystemFailException이 발생하면 FATAL 로그를 남기고 시스템 구동 중지 • AuthSystemFailException이 던져지면 ERROR 로그를 남기고 500 응답 • 이외 모든 RuntimeException이 던져지면 ERROR 로그를 남기고 500응답. 즉 시스템 전체적인 정책 혹은 아키텍쳐가 마련되어 있어야 한다. 모듈 설계 시에 포함되어야 한다.
  41. 41. try { authToken = authManager.getAuthToken(id, password); } catch(AuthFailException e) { throw new InvalidInputException("invalid auth info", e.getErrorCode(), e); } 오직 AuthFailException만 잡으면 된다. 다른 두 예외는 RuntimeException을 상속받았다. 잘못된 입력값을 전달하기 위한 getErrorCode() 메소드가 선 언되어 있다.
  42. 42. 받는 쪽에서 처리하기에 불편하지 않게 던진다. • 시스템 관련된 예외는 따로 잡지 않도록 RuntimeException으로 던졌다. • FATAL과 ERROR를 구분하기 위해 SystemFailException과 AuthSystemFailException으로 구분해서 던졌다. • ERROR 상황이지만 극복된 경우 로그만 남기고 예외를 던지지 않았다. • 잘못된 입력을 구분하기 위해 getErrorCode() 메소드를 AuthFailException에 선언하였다.
  43. 43. RuntimeException을 상속한 예외 클래스로 던지면 매 호출 stack에서 명시적으로 잡지 않아도 된다. 최종 exception handler에서만 잡아서 처리하면 된다. 단 최종 exception handler가 반드시 있어야 한다.
  44. 44. 문제가 발생하면 DEBUG 수준의 로그를 봐야 한다. 로거 설정 변경을 위해 시스템을 재구동하기는 어렵다. logback의 경우 파일의 변경을 감지하여 반영한다. (기존 log4j 등은 그러하지 못했다.) 혹은 JMX를 사용하여 로거 레벨을 동적으로 변경가능하도 록 하자.
  45. 45. debug 레벨의 로깅은 무척이나 많다. 전부 같은 이름이면, debug 레벨로 변경시 어마어마한 양의 로그가 쌓인다. 각 기능별로 로거를 분리해서 필요 부분만 레벨을 조정할 수 있도록.
  46. 46. 요즘은 클래스의 이름을 로거 이름으로 하는 경우가 대세. Logger logger = LogManager.getLogger(SomeDao.class); 클래스의 패키지까지 로거의 이름이 된다. 이 경우 계층적으로 로거를 설정할 수 있다. <logger name=“some” level=“warn”/> <logger name=“some.component” level=“debug”/> <logger name=“some.component.dao” level=“warn”/>
  47. 47. • 다음 2가지 이유로 권장. • 불필요한 String 연산 제거(by SLF4J) • 동적 설정 변경(by LogBack)
  48. 48. • 불필요한 String 연산 방지 logger.debug(“userId=“+userId); logger.debug(“userId={}”, userId); • SLF4J는 다양한 로거에 관계없이 동일한 사용방법을 제공. 단지 진짜 사용하는 로거를 호출해 준다.
  49. 49. • 가장 많이 사용하는 Log4J의 다음 버전(개발자가 동일) • 설정 파일이 동일 • 성능 훨씬 우월
  50. 50. • 로거 설정 파일의 default는 logback.xml 혹은 log4j.xml • 그런데 가져다 사용하는 라이브러리 jar안에 해당 파일이 있으면, 그 설정 파일이 사용될 수 있다. • 설정 파일 이름을 달리하고, 그 이름을 명시하자.
  51. 51. Java의 JUL(Java Util Logging) java.util.logging.LogManager.getLogManager().reset(); org.slf4j.bridge.SLF4JBridgeHandler.install(); java.util.logging.Logger.getLogger("").setLevel(java.util.logging.Level.FINEST); refer http://www.slf4j.org/legacy.html#jul-to-slf4j
  52. 52. Apache의 JCL(Java Commons Logging) 단지 commons-logging.jar를 jcl-over-slf4j.jar로 대체 refer http://www.slf4j.org/legacy.html#jcl-over-slf4j
  53. 53. System.out.println() 처럼 사용하지 말자. 로깅 코드는 삭제하지 않는다. 설치 이후에도 사용된다. 코딩 시에는 찍히는 값이 뭔지 알 수 있지만, 수 많은 로그에 서는 그 값이 무엇인지 모른다. 무언지 알 수 있어야 한다. logger.debug(userId); // 추후에 값 만으로는 파악이 안된다. logger.debug(“userId={}”, userId);
  54. 54. 엔티티 전체를 로그로 찍자. logger.debug(“userId={}”, request.getUserId()); // 이러지 말고 속성 이름은 변경되고 추가되고 삭제된다. 하지만 로깅 코드 는 업데이트 안된다. logger.debug(“request={}”, request); 이를 위해서는 toString()이 구현되어야 한다.
  55. 55. Apache commons의 ToStringBuilder @Override public String toString() { return ToStringBuilder.reflectionToString(this, …); }
  56. 56. 로그의 목적은 문제 파악이다. 예외의 stacktrace가 잔뜩 찍히는 것은 문제 해결의 축복이 다. debug 로그가 많은 것은 단지 레벨 설정으로 처리된다.
  57. 57. logger.debug("request={}", request); logger.debug("context={}", context); logger.debug("binder={}", binder); 여러 개의 로그가 한번에 모여서 찍힌다는 보장은 없다. multi thread 상황에서는 더더욱 그렇다. logger.debug("request={}, context={}, binder={}", request, context, binder);

×