Spring

logback과 Sentry로 에러 모니터링하기

짱정연 2023. 10. 24. 23:49
반응형

들어가며

 현재 진행중인 프로젝트에서는 logback과 팀 내 협업 툴인 디스코드를 연동하여 애플리케이션 단에서 에러 로그가 발생하면 디스코드로 알림이 오도록 환경을 구축하였다.

 빠르게 에러를 파악할 수 있다는 장점이 있지만, 디스코드는 메신저 툴이기 때문에 로그를 관리하기 어렵다는 단점이 있다. 그렇기 때문에 에러 트래킹 및 성능 모니터링 도구로 유명한 Sentry를 프로젝트에 도입하여 로그를 보다 체계적으로 관리하고자 한다.

 

Sentry 요금

참고로 Sentry는 완전 무료 서비스가 아니다. 무료 Plan을 제공하긴 하나 사용할 수 있는 기능이 한정적이다.

  • 멤버 수 제한 - 유료 사용의 경우 무제한
  • 에러 수 제한(월 5,000개) - 유료 사용의 경우 100,000개 이상
  • 히스토리 제한(30일) - 유료 사용의 경우 90일
주의 !!!
Developer Plan 아래 있는 GET STARTED를 눌러 프로젝트를 생성해주자.
처음에 신경 쓰지 않고 바로 프로젝트를 만드니 Business Free Trial 플랜으로 등록되어 있었고, Plan 중도 변경이 되지 않아 프로젝트를 삭제하고 다시 만들어주었다 ;ㅅ;

 

Sentry로 에러 로그를 수집할 때 아래 코드처럼 원하는 에러에 대해서만 전용 예외를 던지는 방법도 있지만, 이번 포스트에서는 전용 예외 코드를 사용하지 않고 logback을 활용한 방법을 다뤄보겠다.

import io.sentry.Sentry;

try {
  aMethodThatMightFail();
} catch(Exception e) {
  Sentry.captureException(e);
}

 

Sentry 프로젝트 생성

플랫폼, 알람 설정, 이름을 설정하여 프로젝트를 생성해주자.

나는 logback을 활용해줄 것이기 때문에 Server 탭에서 logback을 선택하였다.

나는 디스코드로 에러 알림을 따로 받기 때문에 알림 설정은 하지 않았다.

 

의존성 추가

build.gradle에 아래와 같이 의존성을 추가해주자.

// sentry
implementation 'io.sentry:sentry-logback:6.19.0'

 

Sentry 설정 정보 작성

공식 문서에 따르면

  1. properties 파일 작성
  2. 자바 시스템 프로퍼티에 작성
  3. 환경 변수에 작성
  4. 코드로 작성

이렇게 4가지 방법을 제공하고 있는데, 나는 그 중 설정 파일에 작성하는 방식을 사용해보겠다.

자바 시스템 프로퍼티 vs 환경 변수
자바 시스템 프로퍼티는 자바 커맨드 라인에 의해 설정된다 (ex. -DpropertyName=value)
환경 변수는 OS에 의해 설정된다.

 

resources 디렉토리에 sentry.properties를 만들고 DSN 주소를 작성해준다.

dsn={dsn 주소}

다른 옵션들도 추가할 수 있는데, 나는 기본 정보인 DSN 주소만 작성하였다.

 

logback.xml

resources 디렉토리 아래에 logback.xml을 만들어준다.

나는 디스코드와 연동할 때 만들어두었기 때문에 생략한다.

 

logback.xml 설정을 어떻게 구성했는지는 아래 포스트에 기록해두었다.

 

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

에러 로그 알림 시스템을 구축한 이유 EC2에 API 서버를 올렸다고 가정하자. 만약 아무런 세팅 없이 API 서버를 실행하고 있다면, API 에러가 발생했을 때 터미널로 EC2에 접속하여 nohup.out 등을 확인

leeeeeyeon-dev.tistory.com

 

기존에 있던 logback.xml에 Sentry Appender를 추가해주었다.

<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>

    <appender name="Sentry" class="io.sentry.logback.SentryAppender">
      <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>ERROR</level>
      </filter>
      <encoder>
        <pattern>${CONSOLE_LOG_PATTERN}</pattern>
      </encoder>
    </appender>

    <root level="INFO">
      <appender-ref ref="ASYNC_DISCORD"/>
      <appender-ref ref="Console"/>
      <appender-ref ref="Sentry"/>
    </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>

    <appender name="Sentry" class="io.sentry.logback.SentryAppender">
      <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>ERROR</level>
      </filter>
      <encoder>
        <pattern>${CONSOLE_LOG_PATTERN}</pattern>
      </encoder>
    </appender>

    <root level="INFO">
      <appender-ref ref="ASYNC_DISCORD"/>
      <appender-ref ref="Console"/>
      <appender-ref ref="Sentry"/>
    </root>
  </springProfile>
</configuration>

 

결과 확인

API 서버로 에러가 나는 URL을 보냈을 때, 에러가 잘 트래킹되는 모습을 확인할 수 있다.

Sentry 대시보드

 

Issue를 클릭하면 아래 사진처럼 로그에 대해 자세히 확인할 수 있다.

어느 환경(dev, prod)에서 에러가 발생했는지, 어느 서버(blabla-api-01)에서 에러가 발생했는지도 자세히 확인할 수 있다!

현재 로드밸런싱으로 2개의 서버를 운영하고 있고 server_name으로 각 서버를 구분하고 있어 server_name으로 어느 서버에 에러가 발생했는지 확인할 수 있는 것이다. 

Issue

 

Stack Trace도 아래처럼 깔끔하게 해준다 :)

Stack Trace

 

마치며

 처음에 개발 블로그 글을 참고하며 application.yml에 dsn 주소를 입력하여 Sentry 연동이 되지 않았다.

공식 문서에서 'logback.xml에 dsn 주소가 없다면 sentry.properties에서 dsn 주소를 읽어온다'라는 문구를 보고 application.yml에 기입한 정보를 sentry.properties로 변경해주니 연동이 잘 되었다.

 

 물론 처음부터 공식 문서를 참고하여 하는 것이 베스트이지만 :) 아직은 나의 영어나 개발 실력이 좀 부족하다 ... ㅎㅎ

개발 블로그를 따라하다가 안될 때는 포스트와 나의 개발 환경(버전 정보 등)이 일치하는지 확인해보고, 다른 것 같으면 공식 문서를 참고해보자!

 

 이번 프로젝트에서 버전 차이에 따른 트러블 슈팅을 유독 많이 겪은 것 같은데, 그 덕분에 미약하게나마 공식 문서를 읽는 습관을 키우고 있는 것 같다!

 

Reference

 

Spring Boot

On this page, we get you up and running with Sentry's SDK. Get started using a guide listed in the right sidebar. Don't already have an account and Sentry proje

docs.sentry.io

 

Configuration for Java

Learn more about how to configure the SDK. These options are set when the SDK is first initialized, passed to the init method as an object.

docs.sentry.io

 

sentry.io로 에러 로그 관리하기

개요 다양한 로그 수집을 알아보다가 최근에 많이 사용하는 sentry을 알게되었습니다. sentry가 무엇이고 어떻게 사용하는지 코드로 함께 알아보겠습니다. Sentry란? Sentry란 Application 에러 트래킹, 성

escapefromcoding.tistory.com

 

Sentry 로 프론트엔드 에러 로깅

Sentry

velog.io

 

[Spring Boot] logback과 Sentry를 활용하여 에러 모니터링하기

Sentry는 에러 모니터링을 위한 툴로 굉장히 많이 쓰이고 있는 툴이다. 이번에 회사에서 Sentry 붙이는 작업을 했는데, 기존에는 global하게 적용되어 있지 않고, 선언적 방법으로 적용되어 있었다.

zorba91.tistory.com

 

반응형