에러 로그 알림 시스템을 구축한 이유
EC2에 API 서버를 올렸다고 가정하자.
만약 아무런 세팅 없이 API 서버를 실행하고 있다면, API 에러가 발생했을 때 터미널로 EC2에 접속하여 nohup.out 등을 확인하여 직접 로그를 확인해야 한다.
터미널이기 때문에 UI가 불편하며, 무엇보다도 에러가 발생했을 때 즉각적으로 파악하기 어렵다는 점이 가장 힘들었다.
그렇기 때문에 팀 내 커뮤니케이션 툴인 디스코드로 API 에러가 발생할 때마다 알림이 올 수 있도록 환경을 구축해보았다!
해당 게시글에서는 로그에 대한 구체적인 내용은 다루지 않을 예정이다.
Logback?
Logback은 log4j 이후에 출시된 Java 기반 Logging 프레임워크 중 하나이다.
Slf4j의 구현체로, 별도의 dependency 추가 없이 기본적으로 포함되어 있다.
Discord Webhook 만들기
먼저 디스코드 웹훅부터 만들어보자.
톱니바퀴 아이콘 > 연동 > 새 웹후크를 눌러 웹훅을 만들 수 있다.
의존성 추가
logback을 디스코드와 연동하기 위해 discord appender을 추가해야 한다.
build.gradle에 아래와 같이 추가해주어야 한다.
repositories {
maven { url 'https://jitpack.io' }
}
implementation 'com.github.napstr:logback-discord-appender:1.0.0'
logback.xml 작성
resources 폴더 아래에 logback.xml을 만들고, 아래와 같이 코드를 작성한다.
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProfile name="local">
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="dev">
<property resource="application-dev.yml"/>
<springProperty name="DISCORD_WEBHOOK_URL" source="logging.discord.webhook-url"/>
<appender name="DISCORD" class="com.github.napstr.logback.DiscordAppender">
<webhookUri>${DISCORD_WEBHOOK_URL}</webhookUri>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss} [%thread] [%-5level] %logger{36} - %msg%n```%ex{full}```</pattern>
</layout>
<username>감자야...에러 났대...</username>
<avatarUrl>https://jjal.today/data/file/gallery/1889155643_NZHvkRLz_e0292b65bb682075bfdb752a4d8f4062f0b7738a.png</avatarUrl>
<tts>false</tts>
</appender>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<charset>utf8</charset>
</encoder>
</appender>
<appender name="ASYNC_DISCORD" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="DISCORD" />
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC_DISCORD"/>
<appender-ref ref="Console"/>
</root>
</springProfile>
<springProfile name="prod">
<property resource="application-prod.yml"/>
<springProperty name="DISCORD_WEBHOOK_URL" source="logging.discord.webhook-url"/>
<appender name="DISCORD" class="com.github.napstr.logback.DiscordAppender">
<webhookUri>${DISCORD_WEBHOOK_URL}</webhookUri>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss} [%thread] [%-5level] %logger{36} - %msg%n```%ex{full}```</pattern>
</layout>
<username>감자야...에러 났대...</username>
<avatarUrl>https://jjal.today/data/file/gallery/1889155643_NZHvkRLz_e0292b65bb682075bfdb752a4d8f4062f0b7738a.png</avatarUrl>
<tts>false</tts>
</appender>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<charset>utf8</charset>
</encoder>
</appender>
<appender name="ASYNC_DISCORD" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="DISCORD" />
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC_DISCORD"/>
<appender-ref ref="Console"/>
</root>
</springProfile>
</configuration>
springProfile
프로파일별로 다르게 동작하도록 설정해줄 수 있다.
현재 나는 local, dev, prod 3가지 프로파일로 프로젝트를 관리하고 있다.
<appender-ref ref="CONSOLE"/>
local인 경우에는 디스코드 알림이 오지 않고, 콘솔에만 로그가 출력되도록 한다.
dev, prod일 경우에는 디스코드 알림 전송과 콘솔 출력 둘 다 되도록 한다.
<appender-ref ref="ASYNC_DISCORD"/>
<appender-ref ref="Console"/>
참고로, dev와 prod일 때는 내용이 똑같다.
application.yml에서 환경 변수 가져오기
<springProperty name="DISCORD_WEBHOOK_URL" source="logging.discord.webhook-url"/>
application.yml에 있는 logging.discord.webhook-uri를 DISCORD_WEBHOOK_URL이라는 환경 변수로 저장한다.
디스코드 출력 형식 지정
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss} [%thread] [%-5level] %logger{36} - %msg%n```%ex{full}```</pattern>
</layout>
<username>감자야...에러 났대...</username>
<avatarUrl>https://jjal.today/data/file/gallery/1889155643_NZHvkRLz_e0292b65bb682075bfdb752a4d8f4062f0b7738a.png</avatar>
pattern 태그 안에 있는 부분을 수정하여 자신이 원하는대로 로그를 출력할 수 있다.
username은 디스코드 봇의 이름, avatarUrl은 디스코드 봇의 프로필 사진을 의미한다.
application.yml에 디스코드 웹훅 URL 추가
3번에서 만든 웹훅 URL을 application.yml에 다음과 같이 적어준다.
나는 프로파일별로 yml 파일을 관리하기 때문에 application-dev.yml과 application-prod.yml 두 개의 파일에 각각의 웹훅 주소를 적어주었다.
logging:
discord:
webhook-url: (디스코드 웹훅 URL)
config: classpath:logback.xml
결과
해당 방법을 도입하기 전에는 ExceptionHandler에 디스코드 메시지 출력 코드를 그대로 넣어서 계속 에러가 났었다 😢
logback.xml을 사용하니 프로덕션 코드를 건들이지 않고 로그를 바로 디스코드로 보낼 수 있어 편리하다 !!
🔬 Reference
https://junyharang.tistory.com/458
https://velog.io/@qwe916/Discord%EB%A1%9C-Spring-Logback-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0