Skip to content

Commit

Permalink
Merge pull request #422 from Kernel360/refactor/#412/logging
Browse files Browse the repository at this point in the history
[REFACTOR] 로깅 고도화 1차
  • Loading branch information
mooncw authored Nov 21, 2024
2 parents e1fbc57 + ecf2ab1 commit c5ab4b3
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public ResponseEntity<Object> handleCommonException(CommonException e) {
public ResponseEntity<Object> handleRequestException(MethodArgumentNotValidException e) {
RequestErrorCode errorCode = RequestErrorCode.BAD_REQUEST;
errorCode.setMessage(e);
log.error(errorCode.getMessage());
return ResponseEntityFactory.toResponseEntity(errorCode);
}

Expand All @@ -44,14 +43,12 @@ public ResponseEntity<Object> RequestParamException(ConstraintViolationException
public ResponseEntity<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
RequestErrorCode errorCode = RequestErrorCode.BAD_REQUEST;
errorCode.setMessage(e);
log.error(e.getMessage());
return ResponseEntityFactory.toResponseEntity(errorCode);
}

// 핸들링하지 않는 Exception 처리 - 실제 Exception 메시지는 로그에만 남도록 함
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleOtherException(Exception e) {
log.error(e.getMessage());
return ResponseEntityFactory.toResponseEntity(SERVER_ERROR);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.biengual.core.util;

import com.biengual.core.response.ApiCustomResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Builder;

@Builder
public record GlobalExceptionLogSchema(
String server,
String ip,
String contentType,
String userAgent,
String user,
String httpMethod,
String uri,
String params,
Object requestBody,
String code,
String message
) {
public static GlobalExceptionLogSchema of(
String server, String user, HttpServletRequest request, ApiCustomResponse response
) {
Object optionalRequestBody = request.getAttribute("requestBody");
boolean existsRequestBody = optionalRequestBody.toString().isEmpty();

return GlobalExceptionLogSchema.builder()
.server(server)
.ip(request.getRemoteAddr())
.contentType(request.getContentType())
.userAgent(request.getHeader("User-Agent"))
.user(user)
.httpMethod(request.getMethod())
.uri(request.getRequestURI())
.params(request.getQueryString())
.requestBody(existsRequestBody ? null : optionalRequestBody)
.code(response.code())
.message(response.message())
.build();
}

@Override
public String toString() {
return """
[GLOBAL EXCEPTION]
server: %s,
ip: %s,
contentType: %s,
userAgent: %s,
user: %s,
httpMethod: %s,
uri: %s,
params: %s,
requestBody: %s,
code: %s,
message: %s
"""
.formatted(
this.server,
this.ip,
this.contentType,
this.userAgent,
this.user,
this.httpMethod,
this.uri,
this.params,
this.requestBody,
this.code,
this.message
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.biengual.core.util;

import com.biengual.core.response.ApiCustomResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Builder;

@Builder
public record RestControllerSuccessLogSchema(
String server,
String ip,
String contentType,
String userAgent,
String user,
String httpMethod,
String uri,
String params,
Object requestBody,
String code,
String message,
Long responseTime
) {
public static RestControllerSuccessLogSchema of(
String server, String user, Long responseTime, HttpServletRequest request, ApiCustomResponse response
) {
Object optionalRequestBody = request.getAttribute("requestBody");
boolean existsRequestBody = optionalRequestBody.toString().isEmpty();

return RestControllerSuccessLogSchema.builder()
.server(server)
.ip(request.getRemoteAddr())
.contentType(request.getContentType())
.userAgent(request.getHeader("User-Agent"))
.user(user)
.httpMethod(request.getMethod())
.uri(request.getRequestURI())
.params(request.getQueryString())
.requestBody(existsRequestBody ? null : optionalRequestBody)
.code(response.code())
.message(response.message())
.responseTime(responseTime)
.build();
}

@Override
public String toString() {
return """
[REST API INFO]
server: %s,
ip: %s,
contentType: %s,
userAgent: %s,
user: %s,
httpMethod: %s,
uri: %s,
params: %s,
requestBody: %s,
code: %s,
message: %s,
responseTime: %sms
"""
.formatted(
this.server,
this.ip,
this.contentType,
this.userAgent,
this.user,
this.httpMethod,
this.uri,
this.params,
this.requestBody,
this.code,
this.message,
this.responseTime
);
}
}
81 changes: 49 additions & 32 deletions user-api/src/main/java/com/biengual/userapi/aop/LoggingAspect.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package com.biengual.userapi.aop;

import com.biengual.core.response.ApiCustomResponse;
import com.biengual.core.util.GlobalExceptionLogSchema;
import com.biengual.core.util.RestControllerSuccessLogSchema;
import com.biengual.userapi.oauth2.info.OAuth2UserPrincipal;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import static com.biengual.core.response.status.UserServiceStatus.USER_LOGIN_SUCCESS;

Expand All @@ -37,8 +39,8 @@ public class LoggingAspect {
@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
private void restController() {}

@Pointcut("@within(org.springframework.stereotype.Service)")
private void service() {}
@Pointcut("execution(* com.biengual.core.response.error.GlobalExceptionHandler.*(..))")
public void globalException() {}

@Pointcut("@annotation(com.biengual.core.annotation.LoginLogging)")
private void login() {}
Expand All @@ -47,25 +49,60 @@ private void login() {}
@Around("restController()")
public Object logControllerAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().toShortString();
Object result = joinPoint.proceed(joinPoint.getArgs());
long executionTime = System.currentTimeMillis() - startTime;
long responseTime = System.currentTimeMillis() - startTime;

if (result instanceof ResponseEntity<?> responseEntity) {
Object body = responseEntity.getBody();
Object responseBody = responseEntity.getBody();

if (responseBody instanceof ApiCustomResponse apiResponse) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

if (body instanceof ApiCustomResponse apiResponse) {
String customCode = apiResponse.code();
String user = this.getUserIdentifier();

String user = getUserIdentifier();
RestControllerSuccessLogSchema logSchema =
RestControllerSuccessLogSchema.of(activeProfile, user, responseTime, request, apiResponse);

log.info("server: {}, user: {}, controller: {}, responseTime: {}ms, code: {}",
activeProfile, user, methodName, executionTime, customCode);
log.info(logSchema.toString());
}
}
return result;
}


// 소셜 로그인 로그를 남기는 메서드
@After(value = "login() && args(request, response, authentication)", argNames = "request, response, authentication")
public void logLoginAfter(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
OAuth2UserPrincipal principal = (OAuth2UserPrincipal) authentication.getPrincipal();
String email = principal.getEmail();
String code = USER_LOGIN_SUCCESS.getServiceStatus();

log.info("server: {}, user: {}, code: {}", activeProfile, email, code);
}

// GlobalException에 대한 로그를 남기는 메서드
@AfterReturning(pointcut = "globalException()", returning = "result")
public void globalExceptionAfterReturning(Object result) {
if (result instanceof ResponseEntity<?> responseEntity) {
Object responseBody = responseEntity.getBody();

if (responseBody instanceof ApiCustomResponse apiResponse) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

String user = this.getUserIdentifier();

GlobalExceptionLogSchema logSchema =
GlobalExceptionLogSchema.of(activeProfile, user, request, apiResponse);

log.error(logSchema.toString());
}
}
}

// Internal Method =================================================================================================

// RestController에 대한 사용자 식별
private String getUserIdentifier() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Expand All @@ -80,24 +117,4 @@ private String getUserIdentifier() {

return "guest";
}

// 소셜 로그인 로그를 남기는 메서드
@After(value = "login() && args(request, response, authentication)", argNames = "request, response, authentication")
public void logLoginAfter(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
OAuth2UserPrincipal principal = (OAuth2UserPrincipal) authentication.getPrincipal();
String email = principal.getEmail();
String code = USER_LOGIN_SUCCESS.getServiceStatus();

log.info("server: {}, user: {}, code: {}", activeProfile, email, code);
}

// RestController 동작에서 발생하는 에러 로그를 남기는 메서드
@AfterThrowing(pointcut = "restController()", throwing = "e")
public void logException(JoinPoint joinPoint, Exception e) {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
String errorMessage = e.getMessage();

log.error("server: {}, class: {}, method: {}, message: {}", activeProfile, className, methodName, errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.biengual.userapi.config;

import com.biengual.userapi.filter.ReadableRequestWrapperFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpRequestFilterConfig {

@Bean
public FilterRegistrationBean<ReadableRequestWrapperFilter> readableRequestWrapperFilter() {
FilterRegistrationBean<ReadableRequestWrapperFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new ReadableRequestWrapperFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("readableRequestWrapperFilter");
return registrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
package com.biengual.userapi.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
@EnableScheduling
public class ScheduleConfig {
}

@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("scheduled-task-");
return scheduler;
}
}
Loading

0 comments on commit c5ab4b3

Please sign in to comment.