디스코드로 Spring boot 에러 로그 보내는 방법 with. logback
에러 로그 알림 시스템을 구축한 이유
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