Spring

디스코드로 Spring boot 에러 로그 보내는 방법 with. logback

짱정연 2023. 9. 12. 23:54
반응형

에러 로그 알림 시스템을 구축한 이유

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

 

반응형