Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server-config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.swyp8team2.common.config;

import com.swyp8team2.auth.application.oauth.KakaoOAuthClient;
import com.swyp8team2.common.exception.DiscordClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
Expand All @@ -17,4 +18,9 @@ public KakaoOAuthClient kakaoAuthClient() {
.builderFor(adapter).build();
return build.createClient(KakaoOAuthClient.class);
}

@Bean
public RestClient restClient() {
return RestClient.create();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.swyp8team2.common.exception;

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.http.converter.HttpMessageNotReadableException;
Expand All @@ -9,17 +11,23 @@
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import javax.naming.AuthenticationException;
import java.nio.file.AccessDeniedException;
import java.util.Arrays;

@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class ApplicationControllerAdvice {

private final DiscordMessageSender discordMessageSender;
private final Environment environment;

@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> handle(BadRequestException e) {
ErrorResponse response = new ErrorResponse(e.getErrorCode());
Expand Down Expand Up @@ -72,8 +80,11 @@ public ResponseEntity<ErrorResponse> handle(AccessDeniedException e) {
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handle(Exception e) {
public ResponseEntity<ErrorResponse> handle(Exception e, WebRequest webRequest) {
log.error("Exception", e);
if (!Arrays.asList(environment.getActiveProfiles()).contains("local")) {
discordMessageSender.sendDiscordAlarm(e, webRequest);
}
return ResponseEntity.internalServerError()
.body(new ErrorResponse(ErrorCode.INTERNAL_SERVER_ERROR));
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/swyp8team2/common/exception/DiscordClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.swyp8team2.common.exception;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

@Component
public class DiscordClient {

private final RestClient restClient;
private final String discordWebhookUrl;

public DiscordClient(
RestClient restClient,
@Value("${discord.webhook.url}") String discordWebhookUrl) {
this.restClient = restClient;
this.discordWebhookUrl = discordWebhookUrl;
}

public void sendAlarm(DiscordMessage request) {
restClient.post()
.uri(discordWebhookUrl)
.body(request)
.retrieve()
.toBodilessEntity();
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/swyp8team2/common/exception/DiscordMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.swyp8team2.common.exception;

import lombok.Builder;

import java.util.List;

@Builder
public record DiscordMessage(
String content,
List<Embed> embeds
) {

@Builder
record Embed(
String title,
String description
) { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.swyp8team2.common.exception;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Clock;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;

@Component
@RequiredArgsConstructor
public class DiscordMessageSender {

private final DiscordClient discordClient;
private final Clock clock;
private final Environment environment;

public void sendDiscordAlarm(Exception e, WebRequest request) {
discordClient.sendAlarm(createMessage(e, request));
}

private DiscordMessage createMessage(Exception e, WebRequest request) {
List<String> profiles = Arrays.asList(environment.getActiveProfiles());
return DiscordMessage.builder()
.content("# 🚨 μ—λŸ¬ λ°œμƒ")
.embeds(
List.of(
DiscordMessage.Embed.builder()
.title("ℹ️ μ—λŸ¬ 정보")
.description(
"""
### πŸ“ ν™˜κ²½ 정보
%s
### πŸ•– λ°œμƒ μ‹œκ°„
%s
### πŸ”— μš”μ²­ URL
%s
### πŸ“„ Stack Trace
```
%s
```
""".formatted(
String.join("", profiles),
Instant.now(clock),
createRequestFullPath(request),
getStackTrace(e).substring(0, 1000)
))
.build()
)
)
.build();
}

private String createRequestFullPath(WebRequest webRequest) {
HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest();
String fullPath = request.getMethod() + " " + request.getRequestURL();

String queryString = request.getQueryString();
if (queryString != null) {
fullPath += "?" + queryString;
}

return fullPath;
}

private String getStackTrace(Exception e) {
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
}
Loading