Hello World

[펌]SpringBoot, Slack, Logback을 이용한 실시간 에러 모니터링 구현하기 본문

Spring/Boot(4.x)

[펌]SpringBoot, Slack, Logback을 이용한 실시간 에러 모니터링 구현하기

EnterKey 2016. 1. 9. 23:58
반응형

보통에 스타트업은 빠른 프로토타이핑을 통해 MVP(Minimum Viable Product) 로 시장에 진출한다.

기능구현에 집중해서 개발하다보니 제대로된 테스트 코드가 없을 때도 있고, 얼마나 많은 에러가 발생할지 가늠하기 쉽지 않은 상황들이 대부분이다.

에러는 언제든지 날 수 있다고 가정한다면 얼마나 빠르게 감지하고 Hotfix를 내보내느냐가 고객의 불편을 최소화 할 수 있는 방법이 아닐까 싶다.

이번편에서는 Spring 기반의 Application에 간단한 Filter와 Logback을 이용해 초기 시스템에서 간단히 사용할 수 있는 에러 모니터링 기능을 구현해보자.

요구사항은 다음과 같다.

 

1) 협업도구로 쓰고 있는 Slack의 특정 채널에 실시간으로 알람이 되었으면 좋겠다.

2) 급한대로 RDBMS의 테이블에도 에러 로그 데이터를 저장 하고싶다.

3) 에러가 난 시점에 사용자 정보, HttpRequest 파라미터 정보, User-Agent 정보 등을 함께 보고싶다. (에러가 난 것 보다 더 중요한 것은 어떤 환경에서 어떤 사용자가 에러를 겪고있는지 아는 것이 더 중요하기 때문에 꼭 수집해야 할 정보들이다.)

 

Spring Boot 기반의 간단한 Application을 만들어보자. 나는 다음 버전들로 간단한 샘플 프로젝트를 구성했다.

1) SpringBoot 1.3.1

2) Spring Data JPA with H2

3) Jackson 2.6.4,  Slack Webhook API, Apache Commons-IO

 

그리고 에러가 나는 시점에 사용자 정보와 HttpRequest 파라미터 정보, User-Agent 정보 수집을 위해서 몇가지 작업들을 했다.

 

1. MultiReadableHttpServletRequestFilter 적용

POST혹은 PUT 등으로 HttpBody에 데이터를 담아 서버에 전송할 경우, HttpBody 정보는 Java HttpServletRequest의 InputStream으로 변환된다.

그런데 Java의 InputStream 특성상, 스트림을 한번 소비하면 재 사용이 불가능 하다보니 에러난 시점에 HttpBody에 어떤 데이터가 담겨 있는지 확인 할 수가 없다.

그래서 HttpServletRequest InputStream을 여러번 읽을 수 있도록 Cache 하는 Filter를 적용했다.

코드는 대략 다음과 같다.

최초의 inputStream을 ByteArrayOutputStream에 복사해두고, 항상 카피된 ByteArrayOutputStream을 반환 하도록 해주었다.

 

2) MDC(Mapped Diagnostic Context) Filter 적용

MDC는 SLF4J API중 하나다. 자세한 내용은 Logback Documentation에 나와있고, 간단히 설명하자면 ThreadLocal에 데이터를 넣고, 로그를 출력하는 시점에 넣은 데이터들을 맵핑 할 수 있게 해주는 ThreadLocal 유틸리티 정도로 생각하면 되겠다.

즉 MDC에다 어떠한 데이터를 넣으면 해당 HttpRequest(Thread)가 존재하는 동안 데이터가 유지되고, 만약 에러가 발생 한 상황이라면 MDC의 데이터들을 로그에 활용 할 수 있다는 의미이다.

MDC Filter는 다음과 같다.

HttpRequest의 Header, Body(Parameter), 사용자 정보, Agent 상세 정보등을 MDC에 넣도록 했다.

 

3) Filter 등록

2개의 필터가 순서대로 적용 될 수 있도록 FilterRegistrationBean을 생성했다.

 

이제 RDBMS와 Slack으로 로그를 전송할 Custom Logback Appender를 구현해보자.

 

복잡한 코드는 없다, 단지 Slack에 좀 이쁘게 보여주려고 SlackField들을 여러개 만들다보니 코드가 좀 길어졌다.

이제 Slack에 새로운 채널을 추가하고, Incoming Webhook을 등록하자.

나는 test라는 채널을 추가한 다음, Incoming Webhook을 Integration 했다.

Screen Shot 2015-12-23 at 6.17.35 AM

Slack webHookUrl과 channel명을 Property에 설정해주었다.

 

이제 Logback ROOT Logger에 우리가 만든 CustomAppender를 등록해보자.

내가 만든 Appender는 Spring에 의존적인 부분들이 있다보니, logback.xml의 Appender에 바로 설정 하기는 쉽지 않을 것 같아 보인다.

간단한 Bean을 하나 만들고, 의존성을 주입 받아서 ROOT Logger에 등록하도록 했다.

자, 이제 모든 준비가 된 것 같다.

테스트할 Controller를 하나 생성하고 에러를 강제로 발생시켜보자.

자, 4개의 파라미터를 받는 POST API가 있는데, value1의 값이 없으면 에러가 나도록 했다.

value1의 값을 비우고, 나머지 2,3,4의 값을 채운다음 요청을 했을 때, Slack으로 에러가 전송되고 value2,3,4의 값이 표시되는지 확인해보자.

 

Screen Shot 2015-12-23 at 5.27.06 AM

Send를 누르자마자 Slack에 알람이 표시되었고, 다음과 같이 채널에 내용이 나왔다.

Screen Shot 2015-12-23 at 5.40.25 AM

기대했던 대로 Stacktrace, 로그내용, MDC에 저장한 내용들까지 Slack으로 모두 전송되었다.

log.database.enabled=true로 된 상황이라면 JDBC 연결을 통해 ERROR_LOGS 테이블에도 데이터가 쌓였을 것이다.

그리고 SpringBoot를 사용한다면 EmbeddedServletContainerCustomizer Bean을 다음과 같이 등록하면 Http Error 코드들을 원하는 URL로 맵핑할 수 있고,

404 페이지에 대한 에러로그 처리를 위와 같이 할 수도 있다. ( 물론 샘플 코드라 “error” 라는 메시지를 화면에 찍히게 했지만, 실제 서비스에는 잘 디자인된 404 페이지를 사용자에게 보여주도록 하면 되겠다. )

 

정리해보면, 2개의 Servlet Filter (MultiReadableHttpServletRequest, LogbackMdcFilter), Logback Custom AppenderSlackApi 를 구현하면 큰 시간과 비용을 들이지 않고도 간단하게 에러로그를 실시간으로 확인 할 수 있는 시스템을 만들 수 있다.

테스트 소스코드는 Github에 모두 공개되어 있으니, 아직 로그알람이 없는 시스템을 운영중이라면 적극 검토 해보시기 바란다!

소스코드 : https://github.com/brant-hwang/spring-logback-slack-notification-example


출처: http://brantiffy.axisj.com/archives/451


반응형
Comments