Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] 요청 로그에 사용자 정보를 추가 #298

Merged
merged 11 commits into from
Aug 23, 2024
13 changes: 13 additions & 0 deletions backend/src/main/java/kr/momo/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
package kr.momo.config;

import java.util.List;
import kr.momo.config.interceptor.UserInfoInterceptor;
import kr.momo.config.interceptor.LoggingInterceptor;
import kr.momo.controller.auth.AuthArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

private static final String BASE_URL = "/api/v1/**";

private final AuthArgumentResolver authArgumentResolver;
private final UserInfoInterceptor userInfoInterceptor;
private final LoggingInterceptor loggingInterceptor;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(authArgumentResolver);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInfoInterceptor).addPathPatterns(BASE_URL);
registry.addInterceptor(loggingInterceptor).addPathPatterns(BASE_URL);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명시적으로 인터셉터 호출 순서를 지정해주었으면 좋겠어요 😊

}
29 changes: 0 additions & 29 deletions backend/src/main/java/kr/momo/config/filter/FilterConfig.java

This file was deleted.

42 changes: 0 additions & 42 deletions backend/src/main/java/kr/momo/config/filter/LogFilter.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package kr.momo.config.filter;
package kr.momo.config.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class LogGenerator {

public void logRequest(String traceId, HttpServletRequest request) {
String httpMethod = request.getMethod();
String requestURI = request.getRequestURI();
String remoteAddr = request.getRemoteAddr();
String userInfo = request.getAttribute(UserInfoInterceptor.USER_INFO).toString();

log.info("REQUEST [{}][{} {}][{}]", traceId, httpMethod, requestURI, remoteAddr);
log.info("REQUEST [{}][USERID:{}][{} {}]", traceId, userInfo, httpMethod, requestURI);
}

public void logResponse(String traceId, long duration, HttpServletRequest request, HttpServletResponse response) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package kr.momo.config.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Slf4j
@RequiredArgsConstructor
@Component
public class LoggingInterceptor implements HandlerInterceptor {

public static final String TRACE_ID = "traceId";
private static final String START_TIME = "startTime";

private final TraceIdGenerator traceIdGenerator;
private final LogGenerator logGenerator;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = traceIdGenerator.generateShortUuid();
MDC.put(TRACE_ID, traceId);
logGenerator.logRequest(traceId, request);
request.setAttribute(START_TIME, System.currentTimeMillis());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 로깅에 필요한 정보를 MDCHttpServletRequest에 나누어 저장하고 있는데 정보 관리를 일관되게 하면 더 좋을 것 같다는 생각이 들어요.

제가 생각했을 땐 로그와 관련된 정보(TraceId, userInfo)는 MDC , 애플리케이션 로직에 필요한 정보(userInfo)를 각각 분리해서 저장하면될 것 같아요. 그럼 사용하는 입장에서 실수하지 않고 목적에 따라 일관되게 사용할 수 있겠죠?

MDC에 관리해야 할 정보가 늘어난다면 MDC.clear()로 모든 키-값 페어를 삭제할 수 있습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 그렇네요! 반영하겠습니다 👍

return true;
}

@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex
) {
long startTime = (Long) request.getAttribute(START_TIME);
long duration = System.currentTimeMillis() - startTime;
String traceId = MDC.get(TRACE_ID);
logGenerator.logResponse(traceId, duration, request, response);
MDC.remove(TRACE_ID);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package kr.momo.config.filter;
package kr.momo.config.interceptor;

import java.util.UUID;
import org.springframework.stereotype.Component;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package kr.momo.config.interceptor;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Optional;
import kr.momo.service.auth.JwtManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
@RequiredArgsConstructor
public class UserInfoInterceptor implements HandlerInterceptor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토큰 인증에 대한 책임을 가지고 있으니 더 명확한 이름으로 JwtInterceptor 는 어떤가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 좋습니다~ 제안해 주신 이름이 확실히 명확한 것 같아요!


public static final String USER_INFO = "userInfo";

private static final String ANONYMOUS = "ANONYMOUS";
private static final String ACCESS_TOKEN = "ACCESS_TOKEN";

private final JwtManager jwtManager;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Cookie[] cookies = request.getCookies();
String userInfo = getCookieValue(cookies).orElse(ANONYMOUS);
if (isLoginUser(userInfo)) {
userInfo = String.valueOf(jwtManager.extract(userInfo));
}

request.setAttribute(USER_INFO, userInfo);
return true;
}

private Optional<String> getCookieValue(Cookie[] cookies) {
if (cookies == null) {
return Optional.empty();
}

return Arrays.stream(cookies)
.filter(cookie -> ACCESS_TOKEN.equals(cookie.getName()))
.map(Cookie::getValue)
.findFirst();
}

private boolean isLoginUser(String token) {
return !ANONYMOUS.equals(token);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package kr.momo.exception;

import kr.momo.config.filter.LogFilter;
import kr.momo.config.interceptor.LoggingInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
Expand All @@ -20,7 +20,7 @@ public class GlobalExceptionHandler {

@ExceptionHandler
public ProblemDetail handleMomoException(MomoException ex) {
String traceId = MDC.get(LogFilter.TRACE_ID);
String traceId = MDC.get(LoggingInterceptor.TRACE_ID);
log.warn(EXCEPTION_LOG_FORMAT, traceId, ex);

ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(ex.httpStatus(), ex.message());
Expand All @@ -29,7 +29,7 @@ public ProblemDetail handleMomoException(MomoException ex) {

@ExceptionHandler
public ProblemDetail handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
String traceId = MDC.get(LogFilter.TRACE_ID);
String traceId = MDC.get(LoggingInterceptor.TRACE_ID);
log.warn(EXCEPTION_LOG_FORMAT, traceId, ex);

ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(
Expand All @@ -40,7 +40,7 @@ public ProblemDetail handleMethodArgumentNotValidException(MethodArgumentNotVali

@ExceptionHandler
public ProblemDetail handleInternalException(Exception ex) {
String traceId = MDC.get(LogFilter.TRACE_ID);
String traceId = MDC.get(LoggingInterceptor.TRACE_ID);
log.error(EXCEPTION_LOG_FORMAT, traceId, ex);

return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR_MESSAGE);
Expand Down