Skip to content

Commit

Permalink
Merge pull request #69 from YAPP-Github/feature/MAFOO-121
Browse files Browse the repository at this point in the history
[MAFOO-121] feat: 포토 μ„œλΉ„μŠ€μ˜ μŠ¬λž™ μ—λŸ¬ μ•Œλ¦Όμ— payload 정보λ₯Ό μΆ”κ°€ν–ˆμ–΄μš”
  • Loading branch information
gmkim20713 authored Oct 25, 2024
2 parents 26f8710 + f07d002 commit fc132fd
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,32 @@
import kr.mafoo.photo.controller.dto.response.ErrorResponse;
import kr.mafoo.photo.exception.DomainException;
import kr.mafoo.photo.exception.ErrorCode;
import kr.mafoo.photo.service.SlackService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.support.WebExchangeBindException;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

@RestControllerAdvice
@RequiredArgsConstructor
public class WebExceptionHandler {

private final Logger logger = LoggerFactory.getLogger(WebExceptionHandler.class);
private final SlackService slackService;

@ExceptionHandler(DomainException.class)
public ResponseEntity<ErrorResponse> handleDomainException(DomainException exception) {
return ResponseEntity
Expand Down Expand Up @@ -42,4 +60,79 @@ public ResponseEntity<ErrorResponse> validException(Exception ex) {
.badRequest()
.body(response);
}

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

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

private Mono<ResponseEntity<String>> handleExceptionInternal(ServerWebExchange exchange, Exception exception, HttpStatus status) {
String method = extractMethod(exchange);
String userAgent = extractUserAgent(exchange);
String fullPath = extractURI(exchange);
String originIp = extractOriginIp(exchange);

return extractRequestBody(exchange).flatMap(requestBody -> {

logException(method, fullPath, originIp, userAgent, exception);

if (status == HttpStatus.INTERNAL_SERVER_ERROR) {
return slackService.sendErrorNotification(
method, fullPath, requestBody, originIp, userAgent, exception.getMessage()
).then(Mono.just(new ResponseEntity<>("Internal Server Error", status)));
}

return Mono.just(new ResponseEntity<>(status.getReasonPhrase(), status));
});
}

private String extractMethod(ServerWebExchange exchange) {
return exchange.getRequest().getMethod().toString();
}

private String extractUserAgent(ServerWebExchange exchange) {
return exchange.getRequest().getHeaders().getFirst("User-Agent");
}

private String extractURI(ServerWebExchange exchange) {
var request = exchange.getRequest();

String scheme = request.getURI().getScheme();
String host = request.getURI().getHost();
int port = request.getURI().getPort();
String fullPath = request.getURI().getRawPath();
String query = request.getURI().getQuery();

String baseUrl = (port != -1) ? host + ":" + port : host;
String uriWithHost = scheme + "://" + baseUrl + fullPath;

return (query != null && !query.isEmpty()) ? uriWithHost + "?" + query : uriWithHost;
}

private String extractOriginIp(ServerWebExchange exchange) {
String proxyIp = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
return Optional.ofNullable(proxyIp)
.orElseGet(() -> remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : "UNKNOWN SOURCE");
}

private Mono<String> extractRequestBody(ServerWebExchange exchange) {
return exchange.getRequest().getBody().map(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);

return new String(bytes, StandardCharsets.UTF_8);
}).reduce(new StringBuilder(), StringBuilder::append)
.map(StringBuilder::toString);
}

private void logException(String method, String fullPath, String originIp, String userAgent, Exception exception) {
logger.error("Exception occurred: {} {} {} ERROR {} {}", method, fullPath, originIp, exception.getMessage(), userAgent);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@
@RequiredArgsConstructor
public class SlackService {

@Value(value = "${slack.webhook.token}")
private String token;

@Value(value = "${slack.webhook.channel.error}")
private String errorChannel;

@Value(value = "${slack.webhook.channel.qr}")
private String qrErrorChannel;

private final MethodsClient methodsClient;

public Mono<Void> sendErrorNotification(String method, String uri, String originIp, String userAgent, String message) {
public Mono<Void> sendNotification(String channel, String headerText, String method, String uri, String requestBody, String originIp, String userAgent, String message) {
return Mono.fromCallable(() -> {
List<LayoutBlock> layoutBlocks = new ArrayList<>();

// Header μ‚½μž…
layoutBlocks.add(
Blocks.header(
headerBlockBuilder ->
headerBlockBuilder.text(plainText("🚨 μ˜ˆμƒν•˜μ§€ λͺ»ν•œ μ—λŸ¬ λ°œμƒ"))
headerBlockBuilder.text(plainText(headerText))
)
);

Expand All @@ -55,6 +55,15 @@ public Mono<Void> sendErrorNotification(String method, String uri, String origin
)
);

MarkdownTextObject requestBodyMarkdown =
MarkdownTextObject.builder().text("`μš”μ²­ λ°”λ””`\n" + requestBody).build();

layoutBlocks.add(
section(
section -> section.fields(List.of(requestBodyMarkdown))
)
);

MarkdownTextObject errorOriginIpMarkdown =
MarkdownTextObject.builder().text("`μ—λŸ¬ λ°œμƒ IP`\n" + originIp).build();

Expand All @@ -79,8 +88,8 @@ public Mono<Void> sendErrorNotification(String method, String uri, String origin
ChatPostMessageRequest chatPostMessageRequest =
ChatPostMessageRequest
.builder()
.text("μ˜ˆμƒν•˜μ§€ λͺ»ν•œ μ—λŸ¬ λ°œμƒ μ•Œλ¦Ό")
.channel(errorChannel)
.text("μ—λŸ¬ λ°œμƒ μ•Œλ¦Ό")
.channel(channel) // λ™μ μœΌλ‘œ 채널 선택
.blocks(layoutBlocks)
.build();

Expand All @@ -89,4 +98,11 @@ public Mono<Void> sendErrorNotification(String method, String uri, String origin
}).then();
}

public Mono<Void> sendErrorNotification(String method, String uri, String requestBody, String originIp, String userAgent, String message) {
return sendNotification(errorChannel, "🚨 μ˜ˆμƒν•˜μ§€ λͺ»ν•œ μ—λŸ¬ λ°œμƒ", method, uri, requestBody, originIp, userAgent, message);
}

public Mono<Void> sendQrRelatedErrorNotification(String method, String uri, String requestBody, String originIp, String userAgent, String message) {
return sendNotification(qrErrorChannel, "πŸ“Έ μ§€μ›ν•˜μ§€ μ•ŠλŠ” QR λΈŒλžœλ“œ μ—λŸ¬ λ°œμƒ", method, uri, requestBody, originIp, userAgent, message);
}
}

0 comments on commit fc132fd

Please sign in to comment.