20. 외부 호출이 아닌 스케쥴링 테스크의 경우는?
동일하다. JVM, OS 또는 Framework이 호출한 그 지점이 그
경계이다.
여기
예외
발생
JVM, OS,
Framework
21. • REST 타입의 외부 인터페이스
• 내부 스케쥴링 테스크
Exception Mapper가 예외를 잡
아 적절한 Http Status의 응답을
보낸다. 이 때 로그를 남긴다.
그리고 스케쥴링 테스크에서
로그를 남긴다.
Exception
Mapper
Scheduling
Task
여기
여기
REST type interface
23. @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)의 응답
을 보낸다.
24. @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)의 응답을 보낸다.
25. @Service
public class SomeScheduledTask {
@Scheduled
public void process() {
try {
…
} catch(SomeException e) {
logger.warn(...);
} catch(Throwable e) {
logger.error(...);
}
}
}
process() 메소드가 저어쪽 Spring에 호출되는 지점이다.
이 경우 일반 예외뿐 아니라 Throwable로 다 잡고 있다.
26.
27. • Info : 운영자에게 보이기 위한 정보.
• debug : 디버깅을 위한 내용.
• warn : 예외는 아니나, 추후 문제가 될 수 있는.
• error : 예외가 발생했으나, 정상 처리.
• fatal : 예외가 발생했고, 정상처리 못함.
37. 예외가 던져졌는데 정말 예외 상황은 아니다.
• 메소드 설계가 잘못된 경우.
• 예외를 던지면 안된다.
38.
39. 외부 인증 서버로 요청하여 인증을 처리한다.
• 외부 인증 서버의 접속 정보를 설정 파일에서 얻는다.
• 외부 인증 서버로 REST 요청을 하여 응답을 받는다.
• id와 password를 입력으로 받는다.
• id 존재 여부, password 틀림 여부를 알려주어야 한다.
• 인증된 경우 인증 토큰을 반환한다.
40. • 외부 인증 서버의 접속 정보를 읽지 못한다.
• 외부 인증 서버로 접속이 되지 않는다.
• 외부 인증 서버로 접속은 되었으나 응답이 없다.
• 외부 인증 서버로 응답을 받았으나 5초 지나서 받았다.
• timeout 이후 재시도 하여 응답을 받았다.
• 외부 인증 서버로 요청되어 응답을 받았으나 잘못된 형식.
• id가 존재 하지 않는다.
• password가 틀리다.
41. 상황 던지는 예외 예외 처리 주체 로그 레벨 처리
인증서버의 접속 정보
를 읽지 못한다.
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 응답
42. 상황 예외 메시지 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를 설정.
43. 모두 다음의 3가지 예외를 던진다.
• SystemFailException – 구동 불가
• AuthSystemFailException – 장애 발생 상황. 하지만 계속 구동.
• AuthFailException – 호출한 곳에서 잡아서 처리한다.
44. 예외 상황을 처리하기 위해서는 시스템 전체적으로 다음이
전제되어야 한다.
• SystemFailException이 발생하면 FATAL 로그를 남기고 시스템 구동 중지
• AuthSystemFailException이 던져지면 ERROR 로그를 남기고 500 응답
• 이외 모든 RuntimeException이 던져지면 ERROR 로그를 남기고 500응답.
즉 시스템 전체적인 정책 혹은 아키텍쳐가 마련되어 있어야
한다.
모듈 설계 시에 포함되어야 한다.
45. try {
authToken = authManager.getAuthToken(id, password);
} catch(AuthFailException e) {
throw new InvalidInputException("invalid auth info", e.getErrorCode(), e);
}
오직 AuthFailException만 잡으면 된다.
다른 두 예외는 RuntimeException을 상속받았다.
잘못된 입력값을 전달하기 위한 getErrorCode() 메소드가 선
언되어 있다.
46. 받는 쪽에서 처리하기에 불편하지 않게 던진다.
• 시스템 관련된 예외는 따로 잡지 않도록 RuntimeException으로 던졌다.
• FATAL과 ERROR를 구분하기 위해 SystemFailException과
AuthSystemFailException으로 구분해서 던졌다.
• ERROR 상황이지만 극복된 경우 로그만 남기고 예외를 던지지 않았다.
• 잘못된 입력을 구분하기 위해 getErrorCode() 메소드를 AuthFailException에
선언하였다.
47.
48. RuntimeException을 상속한 예외 클래스로 던지면 매 호출
stack에서 명시적으로 잡지 않아도 된다.
최종 exception handler에서만 잡아서 처리하면 된다.
단 최종 exception handler가 반드시 있어야 한다.
49. 문제가 발생하면 DEBUG 수준의 로그를 봐야 한다.
로거 설정 변경을 위해 시스템을 재구동하기는 어렵다.
logback의 경우 파일의 변경을 감지하여 반영한다.
(기존 log4j 등은 그러하지 못했다.)
혹은 JMX를 사용하여 로거 레벨을 동적으로 변경가능하도
록 하자.
50. debug 레벨의 로깅은 무척이나 많다.
전부 같은 이름이면, debug 레벨로 변경시 어마어마한 양의
로그가 쌓인다.
각 기능별로 로거를 분리해서 필요 부분만 레벨을 조정할
수 있도록.
51. 요즘은 클래스의 이름을 로거 이름으로 하는 경우가 대세.
Logger logger = LogManager.getLogger(SomeDao.class);
클래스의 패키지까지 로거의 이름이 된다.
이 경우 계층적으로 로거를 설정할 수 있다.
<logger name=“some” level=“warn”/>
<logger name=“some.component” level=“debug”/>
<logger name=“some.component.dao” level=“warn”/>
52. • 다음 2가지 이유로 권장.
• 불필요한 String 연산 제거(by SLF4J)
• 동적 설정 변경(by LogBack)
53. • 불필요한 String 연산 방지
logger.debug(“userId=“+userId);
logger.debug(“userId={}”, userId);
• SLF4J는 다양한 로거에 관계없이 동일한 사용방법을 제공.
단지 진짜 사용하는 로거를 호출해 준다.
54. • 가장 많이 사용하는 Log4J의 다음 버전(개발자가 동일)
• 설정 파일이 동일
• 성능 훨씬 우월
55. • 로거 설정 파일의 default는 logback.xml 혹은 log4j.xml
• 그런데 가져다 사용하는 라이브러리 jar안에 해당 파일이
있으면, 그 설정 파일이 사용될 수 있다.
• 설정 파일 이름을 달리하고, 그 이름을 명시하자.
57. Apache의 JCL(Java Commons Logging)
단지 commons-logging.jar를 jcl-over-slf4j.jar로 대체
refer http://www.slf4j.org/legacy.html#jcl-over-slf4j
58. System.out.println() 처럼 사용하지 말자.
로깅 코드는 삭제하지 않는다. 설치 이후에도 사용된다.
코딩 시에는 찍히는 값이 뭔지 알 수 있지만, 수 많은 로그에
서는 그 값이 무엇인지 모른다. 무언지 알 수 있어야 한다.
logger.debug(userId); // 추후에 값 만으로는 파악이 안된다.
logger.debug(“userId={}”, userId);
59. 엔티티 전체를 로그로 찍자.
logger.debug(“userId={}”, request.getUserId()); // 이러지 말고
속성 이름은 변경되고 추가되고 삭제된다. 하지만 로깅 코드
는 업데이트 안된다.
logger.debug(“request={}”, request);
이를 위해서는 toString()이 구현되어야 한다.