Skip to content

Commit

Permalink
feat: 예외 발생 시 슬랙 연동 구현 (#215)
Browse files Browse the repository at this point in the history
* chore: 기본 상점 상품 쿼리 수정

* chore: slack api client 의존성 추가

* feat: 예외 발생 시 슬랙 연동 구현

* chore: slack webhook url config 추가

* fix: build 오류 해결
  • Loading branch information
kmebin authored Dec 1, 2023
1 parent ee34964 commit ae751fb
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 15 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ dependencies {
// webflux
implementation 'org.springframework.boot:spring-boot-starter-webflux'

// Slack Webhook
implementation "net.gpedro.integrations.slack:slack-webhook:1.4.0"

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
}
Expand Down
12 changes: 6 additions & 6 deletions infra/mysql/initdb.d/item-data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ insert into item (type, category, name, awake_image, sleep_image, bug_price, gol
values ('NIGHT', 'SKIN', '산타 부엉이', 'https://image.moabam.com/moabam/skins/owl/santa/eyes-opened.png',
'https://image.moabam.com/moabam/skins/owl/santa/eyes-closed.png', 30, 15, 15, current_time());

insert into product (id, type, name, price, quantity, created_at, updated_at)
values (null, 'BUG', '황금벌레x5', 3000, 5, current_time(), null);
insert into product (type, name, price, quantity, created_at)
values ('BUG', '황금벌레 5', 3000, 5, current_time());

insert into product (id, type, name, price, quantity, created_at, updated_at)
values (null, 'BUG', '황금벌레x10', 7000, 10, current_time(), null);
insert into product (type, name, price, quantity, created_at)
values ('BUG', '황금벌레 15', 7000, 15, current_time());

insert into product (id, type, name, price, quantity, created_at, updated_at)
values (null, 'BUG', '황금벌레x25', 9900, 25, current_time(), null);
insert into product (type, name, price, quantity, created_at)
values ('BUG', '황금벌레 25', 9900, 25, current_time());
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.moabam.api.infrastructure.slack;

import static java.util.stream.Collectors.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Component;

import net.gpedro.integrations.slack.SlackAttachment;
import net.gpedro.integrations.slack.SlackField;
import net.gpedro.integrations.slack.SlackMessage;

import com.moabam.global.common.util.DateUtils;

import jakarta.servlet.http.HttpServletRequest;

@Component
public class SlackMessageFactory {

private static final String ERROR_TITLE = "에러가 발생했습니다 🚨";

public SlackMessage generateErrorMessage(HttpServletRequest request, Exception exception) throws IOException {
return new SlackMessage()
.setAttachments(generateAttachments(request, exception))
.setText(ERROR_TITLE);
}

private List<SlackAttachment> generateAttachments(HttpServletRequest request, Exception exception) throws
IOException {
return List.of(new SlackAttachment()
.setFallback("Error")
.setColor("danger")
.setTitleLink(request.getContextPath())
.setText(formatException(exception))
.setColor("danger")
.setFields(generateFields(request)));
}

private String formatException(Exception exception) {
return String.format("📍 Exception Class%n%s%n📍 Exception Message%n%s%n%s",
exception.getClass().getName(),
exception.getMessage(),
Arrays.toString(exception.getStackTrace()));
}

private List<SlackField> generateFields(HttpServletRequest request) throws IOException {
return List.of(
new SlackField().setTitle("✅ Request Method").setValue(request.getMethod()),
new SlackField().setTitle("✅ Request URL").setValue(request.getRequestURL().toString()),
new SlackField().setTitle("✅ Request Time").setValue(DateUtils.format(LocalDateTime.now())),
new SlackField().setTitle("✅ Request IP").setValue(request.getRemoteAddr()),
new SlackField().setTitle("✅ Request Headers").setValue(request.toString()),
new SlackField().setTitle("✅ Request Body").setValue(getRequestBody(request))
);
}

private String getRequestBody(HttpServletRequest request) throws IOException {
String body;

try (
InputStream inputStream = request.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))
) {
body = bufferedReader.lines().collect(joining(System.lineSeparator()));
}
return body;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.moabam.api.infrastructure.slack;

import java.io.IOException;

import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

import net.gpedro.integrations.slack.SlackApi;
import net.gpedro.integrations.slack.SlackMessage;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class SlackService {

private final SlackApi slackApi;
private final SlackMessageFactory slackMessageFactory;
private final TaskExecutor taskExecutor;

public void send(HttpServletRequest request, Exception exception) throws IOException {
SlackMessage slackMessage = slackMessageFactory.generateErrorMessage(request, exception);
taskExecutor.execute(() -> slackApi.call(slackMessage));
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/moabam/global/common/util/DateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DateUtils {

private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

public static String format(LocalDateTime dateTime) {
return dateTime.format(formatter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
public class GlobalConstant {

public static final String BLANK = "";
public static final String COMMA = ",";
public static final String UNDER_BAR = "_";
public static final String DELIMITER = "/";
public static final String CHARSET_UTF_8 = ";charset=UTF-8";
public static final String SPACE = " ";
Expand All @@ -19,6 +17,5 @@ public class GlobalConstant {

public static final int ROOM_FIXED_SEARCH_SIZE = 10;
public static final int LEVEL_DIVISOR = 10;
public static final int DEFAULT_SKIN_SIZE = 2;
public static final String IMAGE_EXTENSION = ".png";
}
19 changes: 19 additions & 0 deletions src/main/java/com/moabam/global/config/SlackConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.moabam.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import net.gpedro.integrations.slack.SlackApi;

@Configuration
public class SlackConfig {

@Value("${webhook.slack.url}")
private String webhookUrl;

@Bean
public SlackApi slackApi() {
return new SlackApi(webhookUrl);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.moabam.global.error.handler;

import static com.moabam.global.error.model.ErrorMessage.INVALID_REQUEST_FIELD;
import static com.moabam.global.error.model.ErrorMessage.INVALID_REQUEST_VALUE_TYPE_FORMAT;
import static com.moabam.global.error.model.ErrorMessage.S3_INVALID_IMAGE_SIZE;
import static com.moabam.global.error.model.ErrorMessage.*;

import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -62,7 +60,10 @@ protected ErrorResponse handleBadRequestException(MoabamException moabamExceptio
}

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({FcmException.class, TossPaymentException.class})
@ExceptionHandler({
FcmException.class,
TossPaymentException.class
})
protected ErrorResponse handleFcmException(MoabamException moabamException) {
return new ErrorResponse(moabamException.getMessage(), null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.moabam.global.error.handler;

import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.moabam.api.infrastructure.slack.SlackService;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;

@RestControllerAdvice
@Profile({"dev", "prod"})
@RequiredArgsConstructor
public class SlackExceptionHandler {

private final SlackService slackService;

@ExceptionHandler(Exception.class)
void handleException(HttpServletRequest request, Exception exception) throws Exception {
slackService.send(request, exception);
throw exception;
}
}
2 changes: 1 addition & 1 deletion src/main/resources/config
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -38,6 +39,11 @@ public class RankingServiceTest {
@Autowired
RankingService rankingService;

@BeforeEach
void init() {
redisTemplate.delete("Ranking");
}

@DisplayName("redis에 추가")
@Nested
class Add {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -40,6 +41,11 @@ class RankingControllerTest extends WithoutFilterSupporter {
@Autowired
RedisTemplate<String, Object> redisTemplate;

@BeforeEach
void init() {
redisTemplate.delete("Ranking");
}

@DisplayName("")
@WithMember
@Test
Expand Down
5 changes: 5 additions & 0 deletions src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,8 @@ payment:
toss:
base-url: "https://api.tosspayments.com"
secret-key: "test_sk_4yKeq5bgrpWk4XYdDoBxVGX0lzW6:"

# Webhook
webhook:
slack:
url: test

0 comments on commit ae751fb

Please sign in to comment.