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

스프링단 로그 추가 #245

Merged
merged 5 commits into from
Aug 5, 2024
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 @@ -11,11 +11,15 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler
public ResponseEntity<ProblemDetail> handleCodeZapException(CodeZapException codeZapException) {
log.info("[CodeZapException] {}가 발생했습니다.", codeZapException.getClass().getName(), codeZapException);
Copy link
Contributor

Choose a reason for hiding this comment

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

우리가 충분히 예상하고, 예외 처리를 해 준 상황이 INFO 레벨의 로그에 기록되는 것이 조금은 어색하게 느껴져요.
혹시 아직 로그 레벨에 대한 논의가 진행되지 않았다면, 같이 이야기를 해 보아도 좋을 것 같아요!
꼭 수정해야 하는 것은 아니기에 우선 comment 만 남겨두고 Approve 할게요

Copy link
Contributor Author

@jminkkk jminkkk Aug 5, 2024

Choose a reason for hiding this comment

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

예상했기 때문에 ERROR나 WARN가 아닌 INFO로 설정해두긴 했었어요...!
크리티컬하지는 않지만 사용자에게 400대 응답이 간 것이기 때문에 정보로 남길 필요가 있다고 생각했기 때문에요~

혹시 짱수는 400대 에러에는 어느 레벨이 적절한지 생각하는지 궁금해요!
+) 디스커션으로 얘기 해보면 좋을 것 같아요!

return ResponseEntity.status(codeZapException.getHttpStatusCode())
.body(ProblemDetail.forStatusAndDetail(
codeZapException.getHttpStatusCode(),
Expand All @@ -32,6 +36,7 @@ public ResponseEntity<ProblemDetail> handleMethodArgumentNotValidException(
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.toList();

log.info("[MethodArgumentNotValidException] {}가 발생했습니다. \n", exception.getClass().getName(), exception);
return ResponseEntity.badRequest()
.body(ProblemDetail.forStatusAndDetail(
HttpStatus.BAD_REQUEST,
Expand All @@ -41,6 +46,7 @@ public ResponseEntity<ProblemDetail> handleMethodArgumentNotValidException(

@ExceptionHandler
public ResponseEntity<ProblemDetail> handleException(Exception exception) {
log.error("[Exception] 예상치 못한 오류 {} 가 발생했습니다.", exception.getClass().getName(), exception);
return ResponseEntity.internalServerError()
.body(ProblemDetail.forStatusAndDetail(
HttpStatus.INTERNAL_SERVER_ERROR,
Expand Down
35 changes: 35 additions & 0 deletions backend/src/main/java/codezap/global/logger/MDCFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package codezap.global.logger;

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

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class MDCFilter implements Filter {
private final String CORRELATION_ID = "correlationId";

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
MDC.put(CORRELATION_ID, generateCorrelationId());
chain.doFilter(request, response);
MDC.clear();
}

private String generateCorrelationId() {
return UUID.randomUUID()
.toString()
.substring(0, 8);
jminkkk marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package codezap.global.logger;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class MethodExecutionTimeAspect {

@Around("execution(* codezap..*(..)) && " +
"!execution(* codezap.global.logger.MethodExecutionTimeAspect(..))" +
"!execution(* codezap.global.exception.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
if (!log.isInfoEnabled()) {
return joinPoint.proceed();
}

long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();

long executionTimeMillis = endTime - startTime;

String className = joinPoint.getSignature()
.getDeclaringType()
.getSimpleName();
String methodName = joinPoint.getSignature()
.getName();

log.info("{}.{} 실행 {}ms", className, methodName, executionTimeMillis);

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package codezap.global.logger;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class RequestResponseLogger extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

long startTime = System.currentTimeMillis();
filterChain.doFilter(requestWrapper, responseWrapper);
long duration = System.currentTimeMillis() - startTime;

String requestBody = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
String responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);

log.info("[Request] {}, {}, 요청 바디: {}", request.getMethod(), request.getRequestURI(), requestBody);
log.info("[Response] Status: {}, Duration: {}ms, 응답 바디: {}", response.getStatus(), duration, responseBody);

responseWrapper.copyBodyToResponse();
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return path.contains("/swagger") || path.contains("/v3/api-docs");
}
}