From b9b79faad05e9a99f05eea1819ca92ab5b02e453 Mon Sep 17 00:00:00 2001 From: gahee99 Date: Sun, 9 Jun 2024 17:19:44 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20global=20Error=20Handler=EC=9E=AC?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gam/api/common/ApiResponse.java | 5 ++ .../java/com/gam/api/common/ErrorHandler.java | 29 ++++++++- .../api/common/InternalServerErrorDTO.java | 59 +++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/gam/api/common/InternalServerErrorDTO.java diff --git a/src/main/java/com/gam/api/common/ApiResponse.java b/src/main/java/com/gam/api/common/ApiResponse.java index 77b984ca..1d1c7e76 100644 --- a/src/main/java/com/gam/api/common/ApiResponse.java +++ b/src/main/java/com/gam/api/common/ApiResponse.java @@ -27,4 +27,9 @@ public static ApiResponse fail(String message){ .build(); } + public static ApiResponse serverError(Object object){ + return ApiResponse.builder() + .data(object) + .build(); + } } diff --git a/src/main/java/com/gam/api/common/ErrorHandler.java b/src/main/java/com/gam/api/common/ErrorHandler.java index e276fc1b..01bfaf13 100644 --- a/src/main/java/com/gam/api/common/ErrorHandler.java +++ b/src/main/java/com/gam/api/common/ErrorHandler.java @@ -7,6 +7,9 @@ import com.gam.api.common.exception.ReportException; import com.gam.api.common.exception.ScrapException; import com.gam.api.common.exception.WorkException; +import java.time.LocalDateTime; +import javax.servlet.http.HttpServletRequest; +import lombok.val; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -15,11 +18,32 @@ import javax.persistence.EntityNotFoundException; -import static com.gam.api.common.message.ExceptionMessage.EMPTY_METHOD_ARGUMENT; - @RestControllerAdvice public class ErrorHandler { + /** Internal Server Error + Slack Alert **/ + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception exception, HttpServletRequest request) { + val header = InternalServerErrorDTO.extractHeaders(request); + + val errorDTO = InternalServerErrorDTO.of(header, request.getMethod(), request.getRequestURL().toString(), + exception.getMessage(), exception.getClass().getName(), LocalDateTime.now()); + + return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity handleRuntimeException(RuntimeException exception, HttpServletRequest request) { + val header = InternalServerErrorDTO.extractHeaders(request); + + val errorDTO = InternalServerErrorDTO.of(header, request.getMethod(), request.getRequestURL().toString(), + exception.getMessage(), exception.getClass().getName(), LocalDateTime.now()); + + return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); + } + + + /** Custom Error + 4__ Error Handler **/ @ExceptionHandler(EntityNotFoundException.class) public ResponseEntity handleEntityNotFoundException(EntityNotFoundException exception) { ApiResponse response = ApiResponse.fail(exception.getMessage()); @@ -73,4 +97,5 @@ public ResponseEntity ReportException(ReportException exception){ ApiResponse response = ApiResponse.fail(exception.getMessage()); return new ResponseEntity<>(response, exception.getStatusCode()); } + } \ No newline at end of file diff --git a/src/main/java/com/gam/api/common/InternalServerErrorDTO.java b/src/main/java/com/gam/api/common/InternalServerErrorDTO.java new file mode 100644 index 00000000..c3840b60 --- /dev/null +++ b/src/main/java/com/gam/api/common/InternalServerErrorDTO.java @@ -0,0 +1,59 @@ +package com.gam.api.common; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Enumeration; +import javax.servlet.http.HttpServletRequest; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class InternalServerErrorDTO { + private String header; + private String httpMethod; + private String URL; + private String message; + private String errorClass; + private String dateTime; + + @Builder + private InternalServerErrorDTO(String header, String httpMethod, String URL, String message, + String errorClass, String dateTime) { + this.header = header; + this.httpMethod = httpMethod; + this.URL = URL; + this.message = message; + this.errorClass = errorClass; + this.dateTime = dateTime; + } + + public static InternalServerErrorDTO of(String header, String httpMethod, String URL, String message, + String errorClass, LocalDateTime nowDateTime) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String now = nowDateTime.format(formatter); + + return InternalServerErrorDTO.builder() + .header(header) + .httpMethod(httpMethod) + .URL(URL) + .message(message) + .errorClass(errorClass) + .dateTime(now) + .build(); + } + + public static String extractHeaders(HttpServletRequest request) { + StringBuilder headers = new StringBuilder(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + String headerValue = request.getHeader(headerName); + headers.append(headerName).append(": ").append(headerValue).append(" "); + } + return headers.toString(); + } +} From c69e17978d29b1f2c7fd4c3fe047132d4c01b6ac Mon Sep 17 00:00:00 2001 From: gahee99 Date: Sun, 9 Jun 2024 18:46:56 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=8A=AC=EB=9E=99,json-formatt=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 3378ed41..ab0f60da 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,12 @@ dependencies { implementation 'io.springfox:springfox-boot-starter:3.0.0' implementation 'io.springfox:springfox-swagger-ui:3.0.0' + + // slack 알림 + implementation 'com.slack.api:slack-api-client:1.27.2' + + // slack DTO json Formatting + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' } tasks.named('test') { From 043e0e867fe64bacf47914d8828dd1c169a758e8 Mon Sep 17 00:00:00 2001 From: gahee99 Date: Sun, 9 Jun 2024 18:47:20 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=8A=AC=EB=9E=99=20config,=20Payl?= =?UTF-8?q?oad=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gam/api/common/SlackErrorPayload.java | 35 +++++++++++++++++++ .../java/com/gam/api/config/SlackConfig.java | 20 +++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/main/java/com/gam/api/common/SlackErrorPayload.java create mode 100644 src/main/java/com/gam/api/config/SlackConfig.java diff --git a/src/main/java/com/gam/api/common/SlackErrorPayload.java b/src/main/java/com/gam/api/common/SlackErrorPayload.java new file mode 100644 index 00000000..d04137f3 --- /dev/null +++ b/src/main/java/com/gam/api/common/SlackErrorPayload.java @@ -0,0 +1,35 @@ +package com.gam.api.common; + +import static com.slack.api.model.block.Blocks.section; +import static com.slack.api.model.block.composition.BlockCompositions.markdownText; +import static java.util.Arrays.asList; + +import com.slack.api.webhook.Payload; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class SlackErrorPayload { + + private Payload payload; + + @Builder + private SlackErrorPayload(Payload payload) { + this.payload = payload; + } + + public static Payload of(String errorDto) { + // Create Payload with a code block + String codeBlock = "```\n" +errorDto + "```"; + + return Payload.builder() + .blocks(asList( + section(s -> s.text(markdownText(mt -> mt.text(codeBlock)))) + )) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/gam/api/config/SlackConfig.java b/src/main/java/com/gam/api/config/SlackConfig.java new file mode 100644 index 00000000..17eb4708 --- /dev/null +++ b/src/main/java/com/gam/api/config/SlackConfig.java @@ -0,0 +1,20 @@ +package com.gam.api.config; + +import com.slack.api.Slack; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Getter +@Configuration +public class SlackConfig { + + @Value("${slack.url}") + private String url; + + @Bean + public Slack slack() { + return Slack.getInstance(); + } +} From 8c1289265ba4f41bcbf8cce3534e47fb74f8576d Mon Sep 17 00:00:00 2001 From: gahee99 Date: Sun, 9 Jun 2024 18:47:48 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=8A=AC=EB=9E=99=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gam/api/common/message/ExceptionMessage.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gam/api/common/message/ExceptionMessage.java b/src/main/java/com/gam/api/common/message/ExceptionMessage.java index 71be522a..b61d90f0 100644 --- a/src/main/java/com/gam/api/common/message/ExceptionMessage.java +++ b/src/main/java/com/gam/api/common/message/ExceptionMessage.java @@ -54,7 +54,10 @@ public enum ExceptionMessage { /** Report **/ ALREADY_REPORTED_USER("이미 신고 한 유저입니다. 신고 처리 진행중"), - NOT_MATCH_DB_BLOCK_STATUS("DB의 userScrap 상태와, 보낸 userScrap 상태가 같지 않습니다."); + NOT_MATCH_DB_BLOCK_STATUS("DB의 userScrap 상태와, 보낸 userScrap 상태가 같지 않습니다."), + + /** slack **/ + NOT_POST_SLACK_ALARM("슬랙 알림이 전송되지 않았습니다."); private final String message; } From dee4963866371f86f9afc00069cc233ac6aa5ccf Mon Sep 17 00:00:00 2001 From: gahee99 Date: Sun, 9 Jun 2024 18:48:02 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=9F=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20=EB=B0=8F=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gam/api/common/ErrorHandler.java | 39 ++++++++++++++----- .../api/common/InternalServerErrorDTO.java | 37 ++++++++++++++---- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/gam/api/common/ErrorHandler.java b/src/main/java/com/gam/api/common/ErrorHandler.java index 01bfaf13..de3c4245 100644 --- a/src/main/java/com/gam/api/common/ErrorHandler.java +++ b/src/main/java/com/gam/api/common/ErrorHandler.java @@ -7,8 +7,12 @@ import com.gam.api.common.exception.ReportException; import com.gam.api.common.exception.ScrapException; import com.gam.api.common.exception.WorkException; +import com.gam.api.common.message.ExceptionMessage; +import com.gam.api.config.SlackConfig; +import com.slack.api.Slack; import java.time.LocalDateTime; import javax.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; import lombok.val; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -19,26 +23,25 @@ import javax.persistence.EntityNotFoundException; @RestControllerAdvice +@RequiredArgsConstructor public class ErrorHandler { + private final Slack slack; + private final SlackConfig slackConfig; + /** Internal Server Error + Slack Alert **/ @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception exception, HttpServletRequest request) { - val header = InternalServerErrorDTO.extractHeaders(request); - - val errorDTO = InternalServerErrorDTO.of(header, request.getMethod(), request.getRequestURL().toString(), - exception.getMessage(), exception.getClass().getName(), LocalDateTime.now()); + val errorDTO = requestToDTO(exception, request); + sendSlackAlarm(errorDTO.toString()); return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(RuntimeException.class) public ResponseEntity handleRuntimeException(RuntimeException exception, HttpServletRequest request) { - val header = InternalServerErrorDTO.extractHeaders(request); - - val errorDTO = InternalServerErrorDTO.of(header, request.getMethod(), request.getRequestURL().toString(), - exception.getMessage(), exception.getClass().getName(), LocalDateTime.now()); - + val errorDTO = requestToDTO(exception, request); + sendSlackAlarm(errorDTO.toString()); return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); } @@ -98,4 +101,22 @@ public ResponseEntity ReportException(ReportException exception){ return new ResponseEntity<>(response, exception.getStatusCode()); } + private InternalServerErrorDTO requestToDTO(Exception exception, HttpServletRequest request) { + val header = InternalServerErrorDTO.extractHeaders(request); + return InternalServerErrorDTO.of(header, request.getMethod(), request.getRequestURL().toString(), + exception.getMessage(), exception.getClass().getName(), LocalDateTime.now()); + } + + private void sendSlackAlarm(String dto) { + try{ + val slackResponse = slack.send(slackConfig.getUrl(), SlackErrorPayload.of(dto)).getBody(); + + if(!slackResponse.equals("ok")) { + throw new Exception(ExceptionMessage.NOT_POST_SLACK_ALARM.getMessage());// todo log -> 슬랙알림 안감 + } + }catch (Exception exception) { + System.out.println(exception.toString()); // todo log + } + } + } \ No newline at end of file diff --git a/src/main/java/com/gam/api/common/InternalServerErrorDTO.java b/src/main/java/com/gam/api/common/InternalServerErrorDTO.java index c3840b60..9ca484c3 100644 --- a/src/main/java/com/gam/api/common/InternalServerErrorDTO.java +++ b/src/main/java/com/gam/api/common/InternalServerErrorDTO.java @@ -1,8 +1,12 @@ package com.gam.api.common; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Enumeration; +import java.util.Objects; import javax.servlet.http.HttpServletRequest; import lombok.Builder; import lombok.Getter; @@ -13,12 +17,14 @@ @Setter @NoArgsConstructor public class InternalServerErrorDTO { - private String header; - private String httpMethod; - private String URL; - private String message; - private String errorClass; - private String dateTime; + private String header = null; + private String httpMethod = null; + private String URL = null ; + private String message = null; + + private String errorClass = null; + + private String dateTime = ""; @Builder private InternalServerErrorDTO(String header, String httpMethod, String URL, String message, @@ -33,6 +39,9 @@ private InternalServerErrorDTO(String header, String httpMethod, String URL, Str public static InternalServerErrorDTO of(String header, String httpMethod, String URL, String message, String errorClass, LocalDateTime nowDateTime) { + if(Objects.isNull(nowDateTime)) { + nowDateTime = LocalDateTime.now(); + } DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String now = nowDateTime.format(formatter); @@ -52,8 +61,22 @@ public static String extractHeaders(HttpServletRequest request) { while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); String headerValue = request.getHeader(headerName); - headers.append(headerName).append(": ").append(headerValue).append(" "); + headers.append(headerName).append(": ").append(headerValue); } return headers.toString(); } + + @Override + public String toString() { + ObjectMapper objectMapper = new ObjectMapper(); + ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter(); + String jsonString = null; + try { + jsonString = writer.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); //todo Log + } + return jsonString; + } + } From 52e2d486dbb76062b3518a73b34344026cc146da Mon Sep 17 00:00:00 2001 From: gahee99 Date: Sun, 9 Jun 2024 18:56:53 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20Internal=20Server=EC=97=90=EB=9F=AC?= =?UTF-8?q?=20client-response=20=ED=98=95=ED=83=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/gam/api/common/ErrorHandler.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gam/api/common/ErrorHandler.java b/src/main/java/com/gam/api/common/ErrorHandler.java index de3c4245..eca7e2e0 100644 --- a/src/main/java/com/gam/api/common/ErrorHandler.java +++ b/src/main/java/com/gam/api/common/ErrorHandler.java @@ -35,14 +35,17 @@ public ResponseEntity handleException(Exception exception, HttpServletRe val errorDTO = requestToDTO(exception, request); sendSlackAlarm(errorDTO.toString()); - return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); + ApiResponse response = ApiResponse.serverError(errorDTO); + return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(RuntimeException.class) public ResponseEntity handleRuntimeException(RuntimeException exception, HttpServletRequest request) { val errorDTO = requestToDTO(exception, request); sendSlackAlarm(errorDTO.toString()); - return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR); + + ApiResponse response = ApiResponse.serverError(errorDTO); + return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); }