diff --git a/user-service/src/main/java/kr/mafoo/user/handler/GlobalExceptionHandler.java b/user-service/src/main/java/kr/mafoo/user/handler/GlobalExceptionHandler.java index de20ae4..b9de2b9 100644 --- a/user-service/src/main/java/kr/mafoo/user/handler/GlobalExceptionHandler.java +++ b/user-service/src/main/java/kr/mafoo/user/handler/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ package kr.mafoo.user.handler; -import kr.mafoo.user.slack.SlackNotificationService; +import kr.mafoo.user.service.SlackService; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,10 +17,11 @@ @RequiredArgsConstructor public class GlobalExceptionHandler { private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); - private final SlackNotificationService slackNotificationService; + private final SlackService slackService; @ExceptionHandler(Exception.class) public Mono> handleException(ServerWebExchange exchange, Exception ex) { + String method = exchange.getRequest().getMethod().toString(); String userAgent = exchange.getRequest().getHeaders().getFirst("User-Agent"); String proxyIp = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For"); InetSocketAddress address = exchange.getRequest().getRemoteAddress(); @@ -28,17 +29,16 @@ public Mono> handleException(ServerWebExchange exchange, String fullPath = exchange.getRequest().getURI().getPath() + (exchange.getRequest().getURI().getQuery() != null ? "?" + exchange.getRequest().getURI().getQuery() : ""); - logger.error("Exception occurred: {} {} {} ERROR {} {}", exchange.getRequest().getMethod(), fullPath, originIp, ex.getMessage(), userAgent); + logger.error("Exception occurred: {} {} {} ERROR {} {}", method, fullPath, originIp, ex.getMessage(), userAgent); - slackNotificationService.sendErrorNotification( - ex, - exchange.getRequest().getMethod().toString(), - exchange.getRequest().getURI().toString(), - HttpStatus.INTERNAL_SERVER_ERROR.toString(), - System.currentTimeMillis(), // or appropriate execution time - exchange.getRequest().getHeaders().getFirst("User-Agent") + return slackService.sendErrorNotification( + method, + fullPath, + originIp, + userAgent, + ex.getMessage() + ).then( + Mono.just(new ResponseEntity<>("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR)) ); - - return Mono.just(new ResponseEntity<>("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR)); } } diff --git a/user-service/src/main/java/kr/mafoo/user/service/SlackService.java b/user-service/src/main/java/kr/mafoo/user/service/SlackService.java index 81f10d4..4b533c8 100644 --- a/user-service/src/main/java/kr/mafoo/user/service/SlackService.java +++ b/user-service/src/main/java/kr/mafoo/user/service/SlackService.java @@ -1,23 +1,19 @@ package kr.mafoo.user.service; import com.slack.api.methods.MethodsClient; -import com.slack.api.methods.SlackApiException; import com.slack.api.methods.request.chat.ChatPostMessageRequest; import com.slack.api.model.block.Blocks; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.MarkdownTextObject; -import com.slack.api.model.block.composition.TextObject; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import static com.slack.api.model.block.Blocks.*; -import static com.slack.api.model.block.composition.BlockCompositions.markdownText; import static com.slack.api.model.block.composition.BlockCompositions.plainText; @Service @@ -32,86 +28,126 @@ public class SlackService { private final MethodsClient methodsClient; - public void sendErrorNotification(Throwable throwable, String method, String uri, String statusCode, long executionTime, String userAgent) { - try { - List textObjects = new ArrayList<>(); - - textObjects.add(markdownText(">*예상하지 못한 에러가 발생했습니다!*\n")); - textObjects.add(markdownText("\n")); - - textObjects.add(markdownText("*메소드:* \n`" + method + "`\n")); - textObjects.add(markdownText("*URI:* \n`" + uri + "`\n")); - textObjects.add(markdownText("*상태코드:* \n`" + statusCode + "`\n")); - textObjects.add(markdownText("*메세지:* \n`" + throwable.getMessage() + "`\n")); - textObjects.add(markdownText("*소요시간:* \n`" + executionTime + " ms`\n")); - textObjects.add(markdownText("*사용자:* \n`" + userAgent + "`\n")); - - ChatPostMessageRequest request = ChatPostMessageRequest - .builder() - .channel(errorChannel) - .blocks( - asBlocks( - divider(), - section( - section -> section.fields(textObjects) - ) - )) - .build(); - - methodsClient.chatPostMessage(request); - } catch (SlackApiException | IOException e) { - throw new RuntimeException("Can't send Slack Message.", e); - } + public Mono sendErrorNotification(String method, String uri, String originIp, String userAgent, String message) { + return Mono.fromCallable(() -> { + List layoutBlocks = new ArrayList<>(); + + // Header 삽입 + layoutBlocks.add( + Blocks.header( + headerBlockBuilder -> + headerBlockBuilder.text(plainText("🚨 예상하지 못한 에러 발생")) + ) + ); + + layoutBlocks.add(divider()); + + // Content 삽입 + MarkdownTextObject errorMethodMarkdown = + MarkdownTextObject.builder().text("`METHOD`\n" + method).build(); + + MarkdownTextObject errorUriMarkdown = + MarkdownTextObject.builder().text("`URI`\n" + uri).build(); + + layoutBlocks.add( + section( + section -> section.fields(List.of(errorMethodMarkdown, errorUriMarkdown)) + ) + ); + + MarkdownTextObject errorOriginIpMarkdown = + MarkdownTextObject.builder().text("`에러 발생 IP`\n" + originIp).build(); + + MarkdownTextObject errorUserAgentMarkdown = + MarkdownTextObject.builder().text("`에러 발생 환경`\n" + userAgent).build(); + + layoutBlocks.add( + section( + section -> section.fields(List.of(errorOriginIpMarkdown, errorUserAgentMarkdown)) + ) + ); + + MarkdownTextObject errorMessageMarkdown = + MarkdownTextObject.builder().text("`메세지`\n" + message).build(); + + layoutBlocks.add( + section( + section -> section.fields(List.of(errorMessageMarkdown)) + ) + ); + + ChatPostMessageRequest chatPostMessageRequest = + ChatPostMessageRequest + .builder() + .text("예상하지 못한 에러 발생 알림") + .channel(errorChannel) + .blocks(layoutBlocks) + .build(); + + return methodsClient.chatPostMessage(chatPostMessageRequest); + + }).then(); } public Mono sendNewMemberNotification(String memberId, String memberName, String memberProfileImageUrl, String memberCreatedAt, String userAgent) { return Mono.fromCallable(() -> { - List layoutBlocks = new ArrayList<>(); - - layoutBlocks.add( - Blocks.header( - headerBlockBuilder -> - headerBlockBuilder.text(plainText("🎉 신규 사용자 가입")))); - layoutBlocks.add(divider()); - - MarkdownTextObject userIdMarkdown = - MarkdownTextObject.builder().text("`사용자 ID`\n" + memberId).build(); - - MarkdownTextObject userNameMarkdown = - MarkdownTextObject.builder().text("`사용자 닉네임`\n" + memberName).build(); - - layoutBlocks.add( - section( - section -> section.fields(List.of(userIdMarkdown, userNameMarkdown)))); - - MarkdownTextObject userProfileImageMarkdown = - MarkdownTextObject.builder().text("`프로필 이미지`\n" + memberProfileImageUrl).build(); - - MarkdownTextObject userCreatedAtMarkdown = - MarkdownTextObject.builder().text("`가입 일자`\n" + memberCreatedAt).build(); - - layoutBlocks.add( - section( - section -> section.fields(List.of(userProfileImageMarkdown, userCreatedAtMarkdown)))); - - MarkdownTextObject userUserAgentMarkdown = - MarkdownTextObject.builder().text("`가입 환경`\n" + userAgent).build(); - - layoutBlocks.add( - section( - section -> section.fields(List.of(userUserAgentMarkdown)))); - - ChatPostMessageRequest chatPostMessageRequest = - ChatPostMessageRequest - .builder() - .text("신규 사용자 가입 알림") - .channel(memberChannel) - .blocks(layoutBlocks) - .build(); - - return methodsClient.chatPostMessage(chatPostMessageRequest); - }) - .then(); + List layoutBlocks = new ArrayList<>(); + + // Header 삽입 + layoutBlocks.add( + Blocks.header( + headerBlockBuilder -> + headerBlockBuilder.text(plainText("🎉 신규 사용자 가입")) + ) + ); + + layoutBlocks.add(divider()); + + // Content 삽입 + MarkdownTextObject userIdMarkdown = + MarkdownTextObject.builder().text("`사용자 ID`\n" + memberId).build(); + + MarkdownTextObject userNameMarkdown = + MarkdownTextObject.builder().text("`사용자 닉네임`\n" + memberName).build(); + + layoutBlocks.add( + section( + section -> section.fields(List.of(userIdMarkdown, userNameMarkdown)) + ) + ); + + MarkdownTextObject userProfileImageMarkdown = + MarkdownTextObject.builder().text("`프로필 이미지`\n" + memberProfileImageUrl).build(); + + MarkdownTextObject userCreatedAtMarkdown = + MarkdownTextObject.builder().text("`가입 일자`\n" + memberCreatedAt).build(); + + layoutBlocks.add( + section( + section -> section.fields(List.of(userProfileImageMarkdown, userCreatedAtMarkdown)) + ) + ); + + MarkdownTextObject userUserAgentMarkdown = + MarkdownTextObject.builder().text("`가입 환경`\n" + userAgent).build(); + + layoutBlocks.add( + section( + section -> section.fields(List.of(userUserAgentMarkdown)) + ) + ); + + ChatPostMessageRequest chatPostMessageRequest = + ChatPostMessageRequest + .builder() + .text("신규 사용자 가입 알림") + .channel(memberChannel) + .blocks(layoutBlocks) + .build(); + + return methodsClient.chatPostMessage(chatPostMessageRequest); + + }).then(); } }