From 71fd65ab1716bfca88d9874563028a4695cd0470 Mon Sep 17 00:00:00 2001 From: SSung023 <50323157+SSung023@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:27:43 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20slack=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 169588d1..b8c91f7f 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ dependencies { // AWS implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.1.RELEASE' - + // json implementation 'com.googlecode.json-simple:json-simple:1.1.1' @@ -53,16 +53,16 @@ dependencies { // MongoDB implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' -// testImplementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo' // H2 - //implementation 'com.h2database:h2' - //runtimeOnly 'com.h2database:h2:2.2.222' testRuntimeOnly 'com.h2database:h2:2.2.222' // Github API for Java implementation 'org.kohsuke:github-api:1.318' + // Slack + implementation 'com.slack.api:slack-api-client:1.25.1' + compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' From 5d26de74248f56d2274a13762f856d20cd323eb0 Mon Sep 17 00:00:00 2001 From: SSung023 <50323157+SSung023@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:36:17 +0900 Subject: [PATCH 2/6] =?UTF-8?q?chore:=20favicon=20error=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=EC=9D=84=20=EC=9C=84=ED=95=B4=20favicon.ico=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/favicon.ico | Bin 0 -> 153626 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/static/favicon.ico diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7ccb7f4dddd25195eb55224d0b009fa0699fdde4 GIT binary patch literal 153626 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 literal 0 HcmV?d00001 From 02635c22b144bc01655960f026d9cc1716c4fb0e Mon Sep 17 00:00:00 2001 From: SSung023 <50323157+SSung023@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:48:16 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20BusinessException=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EC=8B=9C,=20Slack=20=EC=B1=84=EB=84=90=EC=97=90=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=B3=B4=EB=82=B4=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BusinessException 발생 시, Slack의 #exception-log 채널에 메세지를 보내는 기능 추가 - GlobalException에 대한 추가 처리도 필요 - .yml 파일에 slack 정보 추가 필요 - 스택 트레이스 가독성 개선 필요 --- .../exception/BusinessExceptionHandler.java | 4 ++ .../slack/service/SlackMessageUtil.java | 55 ++++++++++++++++ .../gitget/slack/service/SlackService.java | 7 +++ .../slack/service/SlackServiceImpl.java | 62 +++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java create mode 100644 src/main/java/com/genius/gitget/slack/service/SlackService.java create mode 100644 src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java diff --git a/src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java b/src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java index c9151504..0908e481 100644 --- a/src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java +++ b/src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java @@ -1,6 +1,7 @@ package com.genius.gitget.global.util.exception; import com.genius.gitget.global.util.response.dto.CommonResponse; +import com.genius.gitget.slack.service.SlackService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; @@ -11,9 +12,12 @@ @RestControllerAdvice @RequiredArgsConstructor public class BusinessExceptionHandler { + private final SlackService slackService; + @ExceptionHandler(BusinessException.class) protected ResponseEntity globalBusinessExceptionHandler(BusinessException e) { log.error("[ERROR]" + e.getMessage(), e); + slackService.sendMessage(e); return ResponseEntity.badRequest().body( new CommonResponse(e.getStatus(), e.getMessage()) diff --git a/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java b/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java new file mode 100644 index 00000000..1b421170 --- /dev/null +++ b/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java @@ -0,0 +1,55 @@ +package com.genius.gitget.slack.service; + +import static com.slack.api.model.block.Blocks.divider; +import static com.slack.api.model.block.Blocks.section; +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; + +import com.slack.api.model.Attachment; +import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.TextObject; +import java.util.ArrayList; +import java.util.List; + +public class SlackMessageUtil { + + private static final String ERROR_MESSAGE = "*Error Message:*\n"; + private static final String ERROR_STACK = "*Error Stack:*\n"; + private static final String FILTER_STRING = "gitget"; + + public static List createAttachments(String color, List data) { + List attachments = new ArrayList<>(); + Attachment attachment = new Attachment(); + attachment.setColor(color); + attachment.setBlocks(data); + attachments.add(attachment); + return attachments; + } + + public static List createProdErrorMessage(Exception exception) { + StackTraceElement[] stacks = exception.getStackTrace(); + + List layoutBlockList = new ArrayList<>(); + + List sectionInFields = new ArrayList<>(); + sectionInFields.add(markdownText(ERROR_MESSAGE + exception.getMessage())); + sectionInFields.add(markdownText(ERROR_STACK + exception)); + layoutBlockList.add(section(section -> section.fields(sectionInFields))); + + layoutBlockList.add(divider()); + layoutBlockList.add(section(section -> section.text(markdownText(filterErrorStack(stacks))))); + return layoutBlockList; + } + + private static String filterErrorStack(StackTraceElement[] stacks) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("```"); + for (StackTraceElement stack : stacks) { +// stringBuilder.append(stack.toString()).append("\n"); + if (stack.toString().contains(FILTER_STRING)) { + stringBuilder.append(stack).append("\n"); + } + } + stringBuilder.append("```"); + return stringBuilder.toString(); + } +} diff --git a/src/main/java/com/genius/gitget/slack/service/SlackService.java b/src/main/java/com/genius/gitget/slack/service/SlackService.java new file mode 100644 index 00000000..d191ea20 --- /dev/null +++ b/src/main/java/com/genius/gitget/slack/service/SlackService.java @@ -0,0 +1,7 @@ +package com.genius.gitget.slack.service; + +public interface SlackService { + void sendMessage(String message); + + void sendMessage(Exception exception); +} diff --git a/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java b/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java new file mode 100644 index 00000000..f1b8c48d --- /dev/null +++ b/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java @@ -0,0 +1,62 @@ +package com.genius.gitget.slack.service; + +import com.slack.api.Slack; +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.Attachment; +import com.slack.api.model.block.LayoutBlock; +import java.io.IOException; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class SlackServiceImpl implements SlackService { + private static final String PROD_ERROR_MESSAGE_TITLE = "*Exception 발생*"; + private static final String ATTACHMENTS_ERROR_COLOR = "#eb4034"; + @Value("${slack.token}") + private String token; + @Value("${slack.channel}") + private String channel; + + @Override + public void sendMessage(String message) { + try { + MethodsClient methods = Slack.getInstance().methods(token); + ChatPostMessageRequest request = ChatPostMessageRequest.builder() + .channel(channel) + .text(message) + .build(); + + methods.chatPostMessage(request); + + } catch (SlackApiException | IOException e) { + log.error(e.getMessage()); + } + } + + @Override + public void sendMessage(Exception exception) { + try { + List layoutBlocks = SlackMessageUtil.createProdErrorMessage(exception); + List attachments = SlackMessageUtil.createAttachments(ATTACHMENTS_ERROR_COLOR, + layoutBlocks); + + MethodsClient methods = Slack.getInstance().methods(token); + ChatPostMessageRequest request = ChatPostMessageRequest.builder() + .channel(channel) + .attachments(attachments) + .text(PROD_ERROR_MESSAGE_TITLE) + .build(); + + methods.chatPostMessage(request); + log.info("slack 메세지 전송 성공"); + + } catch (SlackApiException | IOException e) { + log.error(e.getMessage()); + } + } +} From 7ab98a4046f7e5408334c1db5a3a22ea5aaef372 Mon Sep 17 00:00:00 2001 From: SSung023 <50323157+SSung023@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:44:20 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20Exception=20=EB=B0=9C=EC=83=9D=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B3=B4=EB=82=B4=EB=8A=94=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=ED=8F=AC=EB=A9=A7=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Exception 발생 시 전송하는 Slack 메세지에 예외 발생 시각 데이터 추가 --- .../certification/util/DateUtil.java | 21 ++++++++++++++++++- .../slack/service/SlackMessageUtil.java | 12 ++++++++--- .../slack/service/SlackServiceImpl.java | 4 ++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/genius/gitget/challenge/certification/util/DateUtil.java b/src/main/java/com/genius/gitget/challenge/certification/util/DateUtil.java index 2130c390..a112346e 100644 --- a/src/main/java/com/genius/gitget/challenge/certification/util/DateUtil.java +++ b/src/main/java/com/genius/gitget/challenge/certification/util/DateUtil.java @@ -1,7 +1,9 @@ package com.genius.gitget.challenge.certification.util; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -44,10 +46,27 @@ public static LocalDate convertToLocalDate(Date date) { ); } + public static LocalDateTime getKstLocalTime() { + ZoneId systemZone = ZoneId.systemDefault(); + ZoneId koreaZone = ZoneId.of("Asia/Seoul"); + + // LocalDate.now()를 호출하여 LocalDateTime을 생성 + LocalDateTime nowLocal = LocalDateTime.now(); + + // 현재 시스템의 ZoneId를 이용하여 ZonedDateTime을 생성 + ZonedDateTime nowZone = ZonedDateTime.of(nowLocal, systemZone); + + // KST(Asia/Seoul)로 변환 + ZonedDateTime koreaTime = nowZone.withZoneSameInstant(koreaZone); + + // LocalDateTime으로 변환하여 반환 + return koreaTime.toLocalDateTime(); + } + private static boolean isFirstWeek(LocalDate challengeStartDate, LocalDate currentDate) { LocalDate mondayOfWeek = challengeStartDate.minusDays(challengeStartDate.getDayOfWeek().ordinal()); LocalDate sundayOfWeek = mondayOfWeek.plusDays(6); - + if (currentDate.isAfter(mondayOfWeek.minusDays(1)) && currentDate.isBefore(sundayOfWeek.plusDays(1))) { return true; diff --git a/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java b/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java index 1b421170..e845129b 100644 --- a/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java +++ b/src/main/java/com/genius/gitget/slack/service/SlackMessageUtil.java @@ -4,6 +4,7 @@ import static com.slack.api.model.block.Blocks.section; import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import com.genius.gitget.challenge.certification.util.DateUtil; import com.slack.api.model.Attachment; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.TextObject; @@ -12,10 +13,16 @@ public class SlackMessageUtil { - private static final String ERROR_MESSAGE = "*Error Message:*\n"; - private static final String ERROR_STACK = "*Error Stack:*\n"; + private static final String ERROR_TITLE = "*Exception 발생 시각:* "; + private static final String ERROR_MESSAGE = "*Exception Message:*\n"; + private static final String ERROR_STACK = "*Exception Stack:*\n"; private static final String FILTER_STRING = "gitget"; + + public static String createErrorTitle() { + return ERROR_TITLE + DateUtil.getKstLocalTime(); + } + public static List createAttachments(String color, List data) { List attachments = new ArrayList<>(); Attachment attachment = new Attachment(); @@ -44,7 +51,6 @@ private static String filterErrorStack(StackTraceElement[] stacks) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("```"); for (StackTraceElement stack : stacks) { -// stringBuilder.append(stack.toString()).append("\n"); if (stack.toString().contains(FILTER_STRING)) { stringBuilder.append(stack).append("\n"); } diff --git a/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java b/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java index f1b8c48d..a82d3bed 100644 --- a/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java +++ b/src/main/java/com/genius/gitget/slack/service/SlackServiceImpl.java @@ -15,7 +15,6 @@ @Slf4j @Service public class SlackServiceImpl implements SlackService { - private static final String PROD_ERROR_MESSAGE_TITLE = "*Exception 발생*"; private static final String ATTACHMENTS_ERROR_COLOR = "#eb4034"; @Value("${slack.token}") private String token; @@ -41,6 +40,7 @@ public void sendMessage(String message) { @Override public void sendMessage(Exception exception) { try { + String errorTitle = SlackMessageUtil.createErrorTitle(); List layoutBlocks = SlackMessageUtil.createProdErrorMessage(exception); List attachments = SlackMessageUtil.createAttachments(ATTACHMENTS_ERROR_COLOR, layoutBlocks); @@ -49,7 +49,7 @@ public void sendMessage(Exception exception) { ChatPostMessageRequest request = ChatPostMessageRequest.builder() .channel(channel) .attachments(attachments) - .text(PROD_ERROR_MESSAGE_TITLE) + .text(errorTitle) .build(); methods.chatPostMessage(request); From d6acf5463618110cc80295286346c9a3c988ca7f Mon Sep 17 00:00:00 2001 From: SSung023 <50323157+SSung023@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:05:15 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EC=B2=98=EB=A6=AC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=98=88=EC=99=B8=20=EC=99=B8?= =?UTF-8?q?=EC=97=90=20=EB=8B=A4=EB=A5=B8=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EC=8B=9C=EC=97=90=EB=8F=84=20Slack=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EB=B3=B4=EB=82=B4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitget/global/util/exception/GlobalExceptionHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java b/src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java index 189caa53..0adacb21 100644 --- a/src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java @@ -1,6 +1,7 @@ package com.genius.gitget.global.util.exception; import com.genius.gitget.global.util.response.dto.CommonResponse; +import com.genius.gitget.slack.service.SlackService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -12,11 +13,14 @@ @RestControllerAdvice @RequiredArgsConstructor public class GlobalExceptionHandler { + private final SlackService slackService; @ExceptionHandler(Exception.class) protected ResponseEntity globalExceptionHandler(Exception e) { log.error("예외처리 되지 않은 Exception 발생 - 처리 필요"); log.error("[UNHANDLED ERROR] " + e.getMessage(), e); + slackService.sendMessage(e); + return ResponseEntity.badRequest().body( new CommonResponse(HttpStatus.BAD_REQUEST, e.getMessage())); } From 3cffcc0c958a4ebcbb72c5359e41f5f6ad7723c2 Mon Sep 17 00:00:00 2001 From: SSung023 <50323157+SSung023@users.noreply.github.com> Date: Sun, 30 Jun 2024 21:28:53 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20prod=20=ED=94=84=EB=A1=9C=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=20=ED=99=9C=EC=84=B1=ED=99=94=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EC=9D=84=20=EB=95=8C=EC=97=90=EB=A7=8C=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=EB=A5=BC=20=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Environment를 의존성 주입받아 prod(application-prod.yml) 프로파일이 활성화되어 있을 때에만 Slack 메세지를 보내도록 설정 - 각 ExceptionHandler가 MessageSender 인터페이스를 상속받고, sendSlackMessage() 메서드를 구현 - 테스트 및 개발 환경에서 Slack 메세지를 보내지 않는 점 확인 --- .../BusinessExceptionHandler.java | 18 +++++++++++++--- .../{ => handler}/FileExceptionHandler.java | 21 +++++++++++++++++-- .../{ => handler}/GlobalExceptionHandler.java | 16 +++++++++++--- .../util/exception/handler/MessageSender.java | 15 +++++++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) rename src/main/java/com/genius/gitget/global/util/exception/{ => handler}/BusinessExceptionHandler.java (61%) rename src/main/java/com/genius/gitget/global/util/exception/{ => handler}/FileExceptionHandler.java (60%) rename src/main/java/com/genius/gitget/global/util/exception/{ => handler}/GlobalExceptionHandler.java (68%) create mode 100644 src/main/java/com/genius/gitget/global/util/exception/handler/MessageSender.java diff --git a/src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java b/src/main/java/com/genius/gitget/global/util/exception/handler/BusinessExceptionHandler.java similarity index 61% rename from src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java rename to src/main/java/com/genius/gitget/global/util/exception/handler/BusinessExceptionHandler.java index 0908e481..56f98053 100644 --- a/src/main/java/com/genius/gitget/global/util/exception/BusinessExceptionHandler.java +++ b/src/main/java/com/genius/gitget/global/util/exception/handler/BusinessExceptionHandler.java @@ -1,9 +1,11 @@ -package com.genius.gitget.global.util.exception; +package com.genius.gitget.global.util.exception.handler; +import com.genius.gitget.global.util.exception.BusinessException; import com.genius.gitget.global.util.response.dto.CommonResponse; import com.genius.gitget.slack.service.SlackService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -11,16 +13,26 @@ @Slf4j @RestControllerAdvice @RequiredArgsConstructor -public class BusinessExceptionHandler { +public class BusinessExceptionHandler implements MessageSender { + private final Environment env; private final SlackService slackService; @ExceptionHandler(BusinessException.class) protected ResponseEntity globalBusinessExceptionHandler(BusinessException e) { log.error("[ERROR]" + e.getMessage(), e); - slackService.sendMessage(e); + sendSlackMessage(e); return ResponseEntity.badRequest().body( new CommonResponse(e.getStatus(), e.getMessage()) ); } + + @Override + public void sendSlackMessage(Exception exception) { + if (!env.matchesProfiles("prod")) { + return; + } + slackService.sendMessage(exception); + } + } diff --git a/src/main/java/com/genius/gitget/global/util/exception/FileExceptionHandler.java b/src/main/java/com/genius/gitget/global/util/exception/handler/FileExceptionHandler.java similarity index 60% rename from src/main/java/com/genius/gitget/global/util/exception/FileExceptionHandler.java rename to src/main/java/com/genius/gitget/global/util/exception/handler/FileExceptionHandler.java index b23bb3e9..e2a3a83b 100644 --- a/src/main/java/com/genius/gitget/global/util/exception/FileExceptionHandler.java +++ b/src/main/java/com/genius/gitget/global/util/exception/handler/FileExceptionHandler.java @@ -1,9 +1,12 @@ -package com.genius.gitget.global.util.exception; +package com.genius.gitget.global.util.exception.handler; import static com.genius.gitget.global.util.exception.ErrorCode.FILE_MAX_SIZE_EXCEED; import com.genius.gitget.global.util.response.dto.CommonResponse; +import com.genius.gitget.slack.service.SlackService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -12,11 +15,25 @@ @Slf4j @RestControllerAdvice -public class FileExceptionHandler { +@RequiredArgsConstructor +public class FileExceptionHandler implements MessageSender { + private final Environment env; + private final SlackService slackService; + @ExceptionHandler(MaxUploadSizeExceededException.class) protected ResponseEntity globalExceptionHandler(Exception e) { log.error("Multipart 용량이 최대 크기를 초과하여 예외가 발생했습니다."); + sendSlackMessage(e); + return ResponseEntity.badRequest().body( new CommonResponse(HttpStatus.BAD_REQUEST, FILE_MAX_SIZE_EXCEED.getMessage())); } + + @Override + public void sendSlackMessage(Exception exception) { + if (!env.matchesProfiles("prod")) { + return; + } + slackService.sendMessage(exception); + } } diff --git a/src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java b/src/main/java/com/genius/gitget/global/util/exception/handler/GlobalExceptionHandler.java similarity index 68% rename from src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java rename to src/main/java/com/genius/gitget/global/util/exception/handler/GlobalExceptionHandler.java index 0adacb21..ec18ab7d 100644 --- a/src/main/java/com/genius/gitget/global/util/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/genius/gitget/global/util/exception/handler/GlobalExceptionHandler.java @@ -1,9 +1,10 @@ -package com.genius.gitget.global.util.exception; +package com.genius.gitget.global.util.exception.handler; import com.genius.gitget.global.util.response.dto.CommonResponse; import com.genius.gitget.slack.service.SlackService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -12,16 +13,25 @@ @Slf4j @RestControllerAdvice @RequiredArgsConstructor -public class GlobalExceptionHandler { +public class GlobalExceptionHandler implements MessageSender { + private final Environment env; private final SlackService slackService; @ExceptionHandler(Exception.class) protected ResponseEntity globalExceptionHandler(Exception e) { log.error("예외처리 되지 않은 Exception 발생 - 처리 필요"); log.error("[UNHANDLED ERROR] " + e.getMessage(), e); - slackService.sendMessage(e); + sendSlackMessage(e); return ResponseEntity.badRequest().body( new CommonResponse(HttpStatus.BAD_REQUEST, e.getMessage())); } + + @Override + public void sendSlackMessage(Exception exception) { + if (!env.matchesProfiles("prod")) { + return; + } + slackService.sendMessage(exception); + } } diff --git a/src/main/java/com/genius/gitget/global/util/exception/handler/MessageSender.java b/src/main/java/com/genius/gitget/global/util/exception/handler/MessageSender.java new file mode 100644 index 00000000..aeca1ac1 --- /dev/null +++ b/src/main/java/com/genius/gitget/global/util/exception/handler/MessageSender.java @@ -0,0 +1,15 @@ +package com.genius.gitget.global.util.exception.handler; + +public interface MessageSender { + /** + * 예외가 발생했을 때 Slack에 예외 발생에 대한 메세지를 보내는 메서드 + *

+ * 주의 사항!! + * 활성화 된 profile이 "prod"일 때에만 작동하도록 해야 합니다. + * Environment의 matchProfiles()를 통해 특정 프로파일이 활성화되어 있는지 확인 가능 + * if(!environment.matchesProfiles("prod")) return; + * + * @param exception 발생한 예외 + */ + void sendSlackMessage(Exception exception); +}