Skip to content

Commit

Permalink
Merge pull request #55 from YAPP-Github/MAFOO-69
Browse files Browse the repository at this point in the history
[MAFOO-69] feat: ์œ ์ € ๋„๋ฉ”์ธ(user-service) ์Šฌ๋ž™ ์•Œ๋ฆผ, ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ
  • Loading branch information
gmkim20713 authored Aug 19, 2024
2 parents b9f96ae + 5028aee commit 9de1612
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package kr.mafoo.user.handler;

import kr.mafoo.user.service.SlackService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;

@ControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private final SlackService slackService;

@ExceptionHandler(ResponseStatusException.class)
public Mono<ResponseEntity<String>> handleResponseStatusException(ServerWebExchange exchange, ResponseStatusException ex) {
return handleExceptionInternal(exchange, ex, (HttpStatus) ex.getStatusCode());
}

@ExceptionHandler(Exception.class)
public Mono<ResponseEntity<String>> handleGenericException(ServerWebExchange exchange, Exception ex) {
return handleExceptionInternal(exchange, ex, HttpStatus.INTERNAL_SERVER_ERROR);
}

private Mono<ResponseEntity<String>> handleExceptionInternal(ServerWebExchange exchange, Exception ex, HttpStatus status) {
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();
String originIp = proxyIp != null ? proxyIp : (address != null ? address.toString() : "UNKNOWN SOURCE");
String fullPath = exchange.getRequest().getURI().getPath() +
(exchange.getRequest().getURI().getQuery() != null ? "?" + exchange.getRequest().getURI().getQuery() : "");

logger.error("Exception occurred: {} {} {} ERROR {} {}", method, fullPath, originIp, ex.getMessage(), userAgent);

if (status == HttpStatus.INTERNAL_SERVER_ERROR) {
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<>(status.getReasonPhrase(), status));
}
}
196 changes: 116 additions & 80 deletions user-service/src/main/java/kr/mafoo/user/service/SlackService.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<TextObject> 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<Void> sendErrorNotification(String method, String uri, String originIp, String userAgent, String message) {
return Mono.fromCallable(() -> {
List<LayoutBlock> 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<Void> sendNewMemberNotification(String memberId, String memberName, String memberProfileImageUrl, String memberCreatedAt, String userAgent) {
return Mono.fromCallable(() -> {
List<LayoutBlock> 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<LayoutBlock> 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();
}

}
2 changes: 1 addition & 1 deletion user-service/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ slack:
token: ${SLACK_TOKEN}
channel:
error: ${SLACK_ERROR_CHANNEL}
member: ${SLACK_MEMBER_CHANNEL}
member: ${SLACK_MEMBER_CHANNEL}

0 comments on commit 9de1612

Please sign in to comment.