-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 어플리케이션 단위 500 에러 디스코드로 알림 전송기능 개발 #320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0fa3c7d
42bca35
c99d269
3460092
1b7a147
591306a
c6cd0a5
e8a5dfc
20f3483
47f2874
b89820d
648f8db
a5b8f7b
6bd3994
a42a813
d3cb409
4d07e52
c263dec
ce14b9b
3ee0084
573d268
b1995db
9cab189
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package konkuk.thip.book.adapter.out.api.naver; | ||
|
|
||
| import konkuk.thip.common.exception.BusinessException; | ||
| import konkuk.thip.common.exception.ExternalApiException; | ||
| import konkuk.thip.common.exception.InternalServerException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.stereotype.Component; | ||
|
|
@@ -63,7 +64,7 @@ private String keywordToEncoding(String keyword) { | |
| try { | ||
| text = URLEncoder.encode(keyword, "UTF-8"); | ||
| } catch (UnsupportedEncodingException e) { | ||
| throw new BusinessException(BOOK_KEYWORD_ENCODING_FAILED); | ||
| throw new InternalServerException(BOOK_KEYWORD_ENCODING_FAILED); | ||
|
Comment on lines
-66
to
+67
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
| } | ||
| return text; | ||
| } | ||
|
|
@@ -84,7 +85,7 @@ String get(String apiUrl, Map<String, String> requestHeaders){ | |
| return readBody(con.getErrorStream()); | ||
| } | ||
| } catch (IOException e) { | ||
| throw new BusinessException(BOOK_NAVER_API_REQUEST_ERROR); | ||
| throw new ExternalApiException(BOOK_NAVER_API_REQUEST_ERROR); | ||
| } finally { | ||
| con.disconnect(); | ||
| } | ||
|
|
@@ -96,9 +97,9 @@ private HttpURLConnection connect(String apiUrl){ | |
| URL url = new URL(apiUrl); | ||
| return (HttpURLConnection)url.openConnection(); | ||
| } catch (MalformedURLException e) { | ||
| throw new BusinessException(BOOK_NAVER_API_URL_ERROR); | ||
| throw new InternalServerException(BOOK_NAVER_API_URL_ERROR); | ||
| } catch (IOException e) { | ||
| throw new BusinessException(BOOK_NAVER_API_URL_HTTP_CONNECT_FAILED); | ||
| throw new InternalServerException(BOOK_NAVER_API_URL_HTTP_CONNECT_FAILED); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -116,7 +117,7 @@ private String readBody(InputStream body){ | |
|
|
||
| return responseBody.toString(); | ||
| } catch (IOException e) { | ||
| throw new BusinessException(BOOK_NAVER_API_RESPONSE_ERROR); | ||
| throw new ExternalApiException(BOOK_NAVER_API_RESPONSE_ERROR); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
|
|
||
| import jakarta.persistence.EntityManager; | ||
| import konkuk.thip.common.entity.StatusType; | ||
| import konkuk.thip.common.exception.InvalidStateException; | ||
| import konkuk.thip.common.exception.InternalServerException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.aspectj.lang.ProceedingJoinPoint; | ||
|
|
@@ -30,7 +30,7 @@ public class StatusFilterAspect { | |
| */ | ||
| private Session currentTxSession() { | ||
| if (!TransactionSynchronizationManager.isActualTransactionActive()) { | ||
| throw new InvalidStateException(PERSISTENCE_TRANSACTION_REQUIRED); | ||
| throw new InternalServerException(PERSISTENCE_TRANSACTION_REQUIRED); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
| } | ||
| return session(); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package konkuk.thip.common.discord; | ||
|
|
||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.reactive.function.client.WebClient; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @Component | ||
| public class DiscordClient { | ||
|
|
||
| @Value("${discord.env}") | ||
| private String env; | ||
|
|
||
| @Value("${discord.webhook-url}") | ||
| private String webhookUrl; | ||
|
|
||
| public void sendErrorMessage(String message, String stackTrace, String requestId, String userId) { | ||
| if("test".equals(env)) return; | ||
|
|
||
| WebClient webClient = WebClient.create(); | ||
|
|
||
| Map<String, Object> embedData = new HashMap<>(); | ||
| embedData.put("title", "THIP 서버 500 에러 발생"); | ||
|
|
||
| Map<String, String> field1 = new HashMap<>(); | ||
| field1.put("name", "발생시각"); | ||
| field1.put("value", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); | ||
|
|
||
| Map<String, String> field2 = new HashMap<>(); | ||
| field2.put("name", "에러 명"); | ||
| field2.put("value", message); | ||
|
|
||
| Map<String, String> field3 = new HashMap<>(); | ||
| field3.put("name", "스택 트레이스"); | ||
| field3.put("value", stackTrace); | ||
|
hd0rable marked this conversation as resolved.
|
||
|
|
||
| Map<String, String> field4 = new HashMap<>(); | ||
| field4.put("name", "Request ID"); | ||
| field4.put("value", requestId != null ? requestId : "N/A"); | ||
|
|
||
| Map<String, String> field5 = new HashMap<>(); | ||
| field5.put("name", "User ID"); | ||
| field5.put("value", userId != null ? userId : "N/A"); | ||
|
|
||
| embedData.put("fields", List.of(field1, field2, field3, field4, field5)); | ||
|
|
||
| Map<String, Object> payload = new HashMap<>(); | ||
| payload.put("embeds", new Object[]{embedData}); | ||
|
|
||
| webClient.post() | ||
| .uri(webhookUrl) | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .bodyValue(payload) | ||
| .retrieve() | ||
| .bodyToMono(Void.class) | ||
| .block(); | ||
|
Comment on lines
+56
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major 웹훅 전송 실패 처리 추가 필요 Discord 웹훅 호출 실패 시 에러 처리가 없습니다. 네트워크 문제나 Discord 서비스 장애 시 로그를 남기는 것이 좋습니다. 에러 핸들링을 추가하세요: webClient.post()
.uri(webhookUrl)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(payload)
.retrieve()
+ .onStatus(HttpStatusCode::isError, response ->
+ response.bodyToMono(String.class)
+ .map(body -> new RuntimeException("Discord webhook failed: " + body))
+ )
.bodyToMono(Void.class)
- .block();
+ .block();
+
+// 또는 subscribe 사용 시:
+ .subscribe(
+ result -> log.debug("Discord notification sent"),
+ error -> log.error("Failed to send Discord notification", error)
+ );
🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package konkuk.thip.common.exception; | ||
|
|
||
| import konkuk.thip.common.exception.code.ErrorCode; | ||
|
|
||
| public class InternalServerException extends RuntimeException { | ||
|
|
||
| private final ErrorCode errorCode; | ||
|
|
||
| public InternalServerException(ErrorCode errorCode) { | ||
| super(errorCode.getMessage()); | ||
| this.errorCode = errorCode; | ||
| } | ||
| public InternalServerException(ErrorCode errorCode, Exception e) { | ||
| super(errorCode.getMessage(), e); | ||
| this.errorCode = errorCode; | ||
| } | ||
| } | ||
|
hd0rable marked this conversation as resolved.
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM