일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- GitHub Actions
- 모임 장소 추천
- Spring
- Spring Batch
- JPA
- kusitms
- 이펙티브자바
- Domain Driven Design
- cicd
- redis
- 약속 장소 추천
- 백엔드
- 자바 ORM 표준 JPA 프로그래밍
- ddd
- springboot
- 도메인 주도 개발 시작하기
- 객체지향 쿼리 언어
- 한국대학생it경영학회
- 중간 장소 추천
- java
- 30기
- RESTClient
- K3S
- JPQL
- Container Registry
- 쿠버네티스
- 큐시즘
- 중간 지점 추천
- 영속성
- 최범균
- Today
- Total
코딩은 마라톤
[Spring] Discord Webhook을 활용하여 에러 대응하기 본문
중간 지점 추천 서비스인 SPOT(스팟)은 에러 발생 시, 팀에서 어떤 에러인지 문의가 들어오고 이때 터미널에 들어가 로그를 확인했다.
다만, 아직 미흡한 로깅 처리와 에러 추적에 대한 시스템을 마련해두지 않아서 매번 예외 로그만 확인하는데 시간이 꽤 걸렸다.
팀 SPOT은 디스코드를 협업 툴로 사용하고 있고, 디스코드 웹훅을 이용해 에러 발생 시 신속하게 대응할 수 있을 것으로 판단하여 스팟-에러 알림 채널을 만들었다.
웹훅을 어떻게 연동하고 처리하는지 알아보자!
1. 디스코드 웹훅 생성하기
디스코드 알림 채널을 만들고, 서버 설정 -> 연동을 통해 웹훅을 만들 수 있다.
웹훅을 만들고 연결할 알림 채널과 이름을 설정하고 웹후크 URL을 복사한다. 웹훅 URL에 요청을 보내 디스코드에 알림을 보내는 방식이다.
2. 디스코드 웹훅 요청 객체 만들기
웹훅 요청 객체는 위 사진과 같이 여러 필드가 존재한다.
참고) https://discord.com/developers/docs/resources/webhook
public record DiscordRequest(
String content,
List<Embed> embeds
) {
public static DiscordRequest of(String content, List<Embed> embeds) {
return new DiscordRequest(content, embeds);
}
public record Embed(String title, String description) {
public static Embed of(String title, String description) {
return new Embed(title, description);
}
}
}
스팟에서는 content와 embeds 배열을 사용했고, embeds에서는 title과 description을 사용했다.
- content : 🚨 [PROD] 서버 예외 발생
- title : Exception / Message
- description : EventException / 출발지는 최소 2개 이상이어야 합니다.
3. 디스코드 웹훅 요청 처리하기
@Slf4j
@Component
@RequiredArgsConstructor
public class DiscordAlarmSender {
@Value("${discord.error-alert-url}")
private String errorAlertUrl;
private static final RestClient restClient = RestClient.create();
public void sendErrorAlert(Exception exception) {
String content = ":rotating_light: [" + getEnvironment() + "] 서버 예외 발생";
List<DiscordRequest.Embed> embeds = List.of(
DiscordRequest.Embed.of("Exception", exception.getClass().getSimpleName()),
DiscordRequest.Embed.of("Message", exception.getMessage()),
DiscordRequest.Embed.of("Timestamp", TimeUtil.formatAsDateTime(LocalDateTime.now())),
DiscordRequest.Embed.of("StackTrace", parseStackTrace(exception))
);
DiscordRequest discordRequest = DiscordRequest.of(content, embeds);
try {
restClient.post()
.uri(errorAlertUrl)
.body(discordRequest)
.retrieve()
.toBodilessEntity();
} catch (Exception e) {
log.warn("[DiscordAlarmSender] Discord 메시지 전송 실패: {}", e.getMessage());
}
}
private String parseStackTrace(Exception exception) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : exception.getStackTrace()) {
sb.append(element.toString()).append("\n");
if (sb.length() > 2000) break;
}
return sb.toString();
}
}
1번 과정에서 생성된 웹훅 URL을 application.yml에 사용한다.
그리고 디스코드에 웹훅 요청을 보내기 위해 RestClient를 사용했고, 웹훅 URL로 요청 객체를 담아 호출한다.
웹훅 요청 객체의 값은 최대 6000자를 초과할 수 없고, description은 4096자까지만 가능하다.
하지만 4096자까지 보여주기에는 디스코드에 가독성이 떨어지기 때문에 2000자까지만 담아서 보이게 했다.
4. 배포 환경에 따라 제목으로 구분하기 (선택)
https://developer-anxi.tistory.com/79
https://developer-anxi.tistory.com/80
스팟은 운영, 스테이징 환경으로 분리했기 때문에 운영 환경과 스테이징 환경에 따라 에러를 구분했다.
@Slf4j
@Component
@RequiredArgsConstructor
public class DiscordAlarmSender {
...
private final Environment environment;
public void sendErrorAlert(Exception exception) {
String env = getEnvironment();
if (!List.of("PROD", "STG").contains(env)) {
return;
}
String content = ":rotating_light: [" + getEnvironment() + "] 서버 예외 발생";
...
}
private String getEnvironment() {
return Optional.ofNullable(environment.getActiveProfiles()[0]).map(String::toUpperCase).orElse("-");
}
private String parseStackTrace(Exception exception) {
...
}
}
Environment 객체에서 활성 프로필을 가지고 PROD(운영), STG(스테이징)이 아닐 경우, 에러를 전송하지 않도록 했다.
운영·스테이징 환경일 경우, content에 환경을 동적으로 주입해서 보이도록 했다.
🎬 마무리
이렇게 해서 디스코드 웹훅을 활용해서 에러 알리미를 만들어봤다.
에러 알리미를 통해 예외명, 예외 메시지, 시간대, 예외 로그를 바로 확인할 수 있어서 특정 시간대의 에러도 기존의 로그를 샅샅이 찾지 않아도 된다.
다만, 에러 정보가 간소화돼서 보이기 때문에 추후 Sentry를 활용해서 신속 대응은 디스코드를, Sentry는 자세하게 살펴볼 때 사용하면 좋을 것 같다.
References
https://unan.palms.blog/discord-alarm
[Spring] Discord로 project 운영
나만의 네컷 프로젝트에서 Discord Webhook을 활용하여 서비스 운영하기.
unan.palms.blog
https://discord.com/developers/docs/resources/webhook
Discord for Developers
Build games, experiences, and integrations for millions of users on Discord.
discord.com
'Backend > Spring Boot' 카테고리의 다른 글
[Reactive Spring] 리액티브 프로그래밍과 오퍼레이션 (Flux, Mono) (0) | 2025.03.24 |
---|---|
[SpringBoot + OpenAI(ChatGPT)] SpringBoot에서 OpenAI API를 이용해 연동하기 (4) | 2024.11.10 |
[SpringBoot] 서블릿과 서블릿 컨테이너 (1) | 2024.06.28 |
[Springboot] Filter와 Interceptor (1) | 2024.05.08 |
Request DTO에서 @Getter를 쓰는 이유 (바인딩) (1) | 2024.04.08 |