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
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ private static String buildGrafanaLogLink() {
}

String application = "eatssu-" + serverEnv;
String logQl = "{application=\"" + application + "\"} |= \"reqId=" + requestId + "\"";
String logQl = "{application=\"" + application + "\"} |= \"" + requestId + "\"";
long now = System.currentTimeMillis();

Map<String, Object> query = new LinkedHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.slack.api.methods.request.chat.ChatPostMessageRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import ssu.eatssu.domain.slack.entity.SlackChannel;

Expand All @@ -17,6 +18,7 @@ public class SlackService {

private final MethodsClient methodsClient;

@Async
public void sendSlackMessage(String message, SlackChannel channel) {

String channelAddress = channel.getKrName();
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/ssu/eatssu/global/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
package ssu.eatssu.global.config;

import org.slf4j.MDC;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.annotation.EnableAsync;

import java.util.Map;

@Configuration
@EnableAsync
public class AsyncConfig {

@Bean
public TaskDecorator mdcTaskDecorator() {
return runnable -> {
Map<String, String> context = MDC.getCopyOfContextMap();
return () -> {
if (context != null) {
MDC.setContextMap(context);
}
try {
runnable.run();
} finally {
MDC.clear();
}
};
};
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package ssu.eatssu.global.handler;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
Expand All @@ -25,22 +22,15 @@
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponse;
import ssu.eatssu.global.handler.response.BaseResponseStatus;

/**
* 전역 Controller 예외처리
*/
@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private final SlackErrorNotifier slackErrorNotifier;
@Value("${server.env:unknown}")
private String serverEnv;

/**
* BaseException 처리
*/
Expand Down
18 changes: 15 additions & 3 deletions src/main/java/ssu/eatssu/global/log/MDCLoggingFilter.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
package ssu.eatssu.global.log;

import jakarta.servlet.*;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.UUID;
import java.util.regex.Pattern;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MDCLoggingFilter implements Filter {
private static final String REQUEST_ID_HEADER = "X-Request-ID";
private static final Pattern VALID_REQUEST_ID = Pattern.compile("[A-Za-z0-9-]{1,64}");

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String requestId = ((HttpServletRequest) request).getHeader("X-RequestID");
if (requestId == null) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestId = httpRequest.getHeader(REQUEST_ID_HEADER);
if (requestId == null || !VALID_REQUEST_ID.matcher(requestId).matches()) {
requestId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("requestId", requestId);
MDC.put("uri", httpRequest.getMethod() + " " + httpRequest.getRequestURI());
((HttpServletResponse) response).setHeader(REQUEST_ID_HEADER, requestId);
try {
chain.doFilter(request, response);
} finally {
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ spring:
max-file-size: 20MB
max-request-size: 20MB

mvc:
log-resolved-exception: true

## Auth
jwt:
secret:
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ spring:
max-file-size: 20MB
max-request-size: 20MB

mvc:
log-resolved-exception: true

## Auth
jwt:
secret:
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- 로그 형식 설정 예시 :2025-09-25 12:34:56.789 [INFO] [http-nio-9000-ex] [reqId=123e4567] [com.eatssu.api.UserController] - 내용
Thread : 동시성 문제 관련 사용, Request : 요청 단위 -->
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %clr([%level]) [%thread] [reqId=%X{requestId}] %cyan(%logger{36}) - %msg%n"/>
value="%d{yyyy-MM-dd HH:mm:ss.SSS, Asia/Seoul} %clr([%level]) [%thread] [reqId=%X{requestId}] %cyan(%logger{36}) - %msg%n"/>
<!-- 콘솔 출력 (stdout) -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import ssu.eatssu.domain.admin.dto.request.RegisterCategoryRequest;
import ssu.eatssu.domain.admin.service.ManageCategoryService;
import ssu.eatssu.domain.restaurant.entity.Restaurant;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;
Expand All @@ -29,15 +28,12 @@ class ManageCategoryControllerTest {
@Mock
private ManageCategoryService manageCategoryService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ManageCategoryController(manageCategoryService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import ssu.eatssu.domain.admin.dto.response.MenuBoards;
import ssu.eatssu.domain.admin.service.ManageFixMenuService;
import ssu.eatssu.domain.restaurant.entity.Restaurant;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;
Expand All @@ -35,15 +34,12 @@ class ManageFixMenuControllerTest {
@Mock
private ManageFixMenuService manageFixMenuService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ManageFixMenuController(manageFixMenuService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import ssu.eatssu.domain.admin.dto.response.PageWrapper;
import ssu.eatssu.domain.admin.service.ManageInquiryService;
import ssu.eatssu.domain.inquiry.entity.InquiryStatus;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;
Expand All @@ -39,15 +38,12 @@ class ManageInquiryControllerTest {
@Mock
private ManageInquiryService manageInquiryService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ManageInquiryController(manageInquiryService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import ssu.eatssu.domain.admin.service.ManageMealService;
import ssu.eatssu.domain.menu.entity.constants.TimePart;
import ssu.eatssu.domain.restaurant.entity.Restaurant;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;
Expand All @@ -41,15 +40,12 @@ class ManageMealControllerTest {
@Mock
private ManageMealService manageMealService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ManageMealController(manageMealService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import ssu.eatssu.domain.admin.dto.response.PageWrapper;
import ssu.eatssu.domain.admin.dto.response.ReportLine;
import ssu.eatssu.domain.admin.service.ManageReportService;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;
Expand All @@ -35,15 +34,12 @@ class ManageReportControllerTest {
@Mock
private ManageReportService manageReportService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ManageReportController(manageReportService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import ssu.eatssu.domain.admin.service.ManageReviewService;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;
Expand All @@ -26,15 +25,12 @@ class ManageReviewControllerTest {
@Mock
private ManageReviewService manageReviewService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ManageReviewController(manageReviewService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import ssu.eatssu.domain.menu.presentation.dto.response.MenusInMealResponse;
import ssu.eatssu.domain.menu.service.MealService;
import ssu.eatssu.domain.restaurant.entity.Restaurant;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.global.handler.GlobalExceptionHandler;

import java.util.List;
Expand All @@ -36,15 +35,12 @@ class MealControllerTest {
@Mock
private MealService mealService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new MealController(mealService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import ssu.eatssu.domain.auth.security.CustomUserDetails;
import ssu.eatssu.domain.partnership.service.PartnershipService;
import ssu.eatssu.domain.slack.service.SlackErrorNotifier;
import ssu.eatssu.domain.user.entity.Role;
import ssu.eatssu.global.handler.GlobalExceptionHandler;
import ssu.eatssu.global.handler.response.BaseException;
Expand All @@ -39,17 +38,14 @@ class PartnershipControllerTest {
@Mock
private PartnershipService partnershipService;

@Mock
private SlackErrorNotifier slackErrorNotifier;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new ParameterNamesModule())
.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
mockMvc = MockMvcBuilders.standaloneSetup(new PartnershipController(partnershipService))
.setControllerAdvice(new GlobalExceptionHandler(slackErrorNotifier))
.setControllerAdvice(new GlobalExceptionHandler())
.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver())
.setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.handler.response.BaseResponseStatus;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -118,6 +120,9 @@ void sendServerErrorIncludesGrafanaLogLinkWhenConfiguredAndRequestIdPresent() {
// then
assertThat(message).contains("https://example.grafana.net/explore?schemaVersion=1&panes=",
"Grafana에서 로그 보기");
assertThat(URLDecoder.decode(message, StandardCharsets.UTF_8))
.contains("|= \\\"test-request-id\\\"")
.doesNotContain("reqId=");
}

@Test
Expand Down
Loading
Loading