Skip to content

Commit

Permalink
Merge pull request #33 from it-s-time-goat/dev/ldy/logging
Browse files Browse the repository at this point in the history
[FEAT] 로그백을 이용한 로깅 기능 적용
  • Loading branch information
leedy3838 authored Jul 10, 2024
2 parents 8cba5ff + c1734a7 commit 5280d9e
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum GlobalErrorCode implements ErrorCode {
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "Invalid parameter included"),
RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "Resource not exists"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error"),
INVALID_METHOD(HttpStatus.METHOD_NOT_ALLOWED, "Invalid Method"),
;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,90 +3,116 @@
import com.goat.server.directory.exception.DirectoryCanNotDeleteException;
import com.goat.server.directory.exception.DirectoryNotFoundException;
import com.goat.server.mypage.exception.UserNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;

import com.goat.server.global.exception.CustomFeignException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
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.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import com.goat.server.global.exception.errorcode.ErrorCode;
import com.goat.server.global.exception.errorcode.GlobalErrorCode;
import com.goat.server.global.exception.response.ErrorResponse;
import com.goat.server.global.exception.response.ErrorResponse.ValidationError;
import com.goat.server.global.exception.response.ErrorResponse.ValidationErrors;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

private static final Logger log = LoggerFactory.getLogger("ErrorLogger");
private static final String LOG_FORMAT_INFO = "\n[🔵INFO] - ({} {})\n(id: {}, role: {})\n{}\n {}: {}";
private static final String LOG_FORMAT_WARN = "\n[🟠WARN] - ({} {})\n(id: {}, role: {})";
private static final String LOG_FORMAT_ERROR = "\n[🔴ERROR] - ({} {})\n(id: {}, role: {})";

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Object> handleIllegalArgument(final IllegalArgumentException e) {
final ErrorCode errorCode = GlobalErrorCode.INVALID_PARAMETER;
public ResponseEntity<Object> handleIllegalArgument(IllegalArgumentException e, HttpServletRequest request) {
logInfo(GlobalErrorCode.INVALID_PARAMETER, e, request);
ErrorCode errorCode = GlobalErrorCode.INVALID_PARAMETER;
return handleExceptionInternal(errorCode, e.getMessage());
}

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<Object> handleUserNotFound(final UserNotFoundException e) {
final ErrorCode errorCode = e.getErrorCode();
public ResponseEntity<Object> handleUserNotFound(UserNotFoundException e, HttpServletRequest request) {
logInfo(e.getErrorCode(), e, request);
ErrorCode errorCode = e.getErrorCode();
return handleExceptionInternal(errorCode);
}

@ExceptionHandler(DirectoryNotFoundException.class)
public ResponseEntity<Object> handleDirectoryNotFound(final DirectoryNotFoundException e) {
final ErrorCode errorCode = e.getErrorCode();
public ResponseEntity<Object> handleDirectoryNotFound(DirectoryNotFoundException e, HttpServletRequest request) {
logInfo(e.getErrorCode(), e, request);
ErrorCode errorCode = e.getErrorCode();
return handleExceptionInternal(errorCode);
}

@ExceptionHandler(DirectoryCanNotDeleteException.class)
public ResponseEntity<Object> handleDirectoryCanNotDelete(final DirectoryCanNotDeleteException e) {
final ErrorCode errorCode = e.getErrorCode();
public ResponseEntity<Object> handleDirectoryCanNotDelete(DirectoryCanNotDeleteException e,
HttpServletRequest request) {
logInfo(e.getErrorCode(), e, request);
ErrorCode errorCode = e.getErrorCode();
return handleExceptionInternal(errorCode);
}

/**
* Feign 관련 exception 처리
*/
@ExceptionHandler(CustomFeignException.class)
public ErrorResponse handleTokenNotExist(final CustomFeignException e) {
final ErrorCode errorCode = e.getErrorCode();
public ErrorResponse handleTokenNotExist(CustomFeignException e, HttpServletRequest request) {
logInfo(e.getErrorCode(), e, request);
ErrorCode errorCode = e.getErrorCode();
return makeErrorResponse(errorCode, e.getMessage());
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllException(Exception e, HttpServletRequest request) {
logError(e, request);
ErrorCode errorCode = GlobalErrorCode.INTERNAL_SERVER_ERROR;
return handleExceptionInternal(errorCode);
}

/**
* DTO @Valid 관련 exception 처리
*/
@Override
public ResponseEntity<Object> handleMethodArgumentNotValid(
final MethodArgumentNotValidException e,
final HttpHeaders headers,
final HttpStatusCode status,
final WebRequest request) {
log.warn("handleIllegalArgument", e);
final ErrorCode errorCode = GlobalErrorCode.INVALID_PARAMETER;
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException e,
HttpHeaders headers,
HttpStatusCode status,
WebRequest request) {
ErrorCode errorCode = GlobalErrorCode.INVALID_PARAMETER;
return handleExceptionInternal(e, errorCode);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllException(final Exception ex) {
log.warn("handleAllException", ex);
final ErrorCode errorCode = GlobalErrorCode.INTERNAL_SERVER_ERROR;
/**
* HTTP Method 관련 exception 처리
*/
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
HttpRequestMethodNotSupportedException e,
HttpHeaders headers,
HttpStatusCode status,
WebRequest request) {
ErrorCode errorCode = GlobalErrorCode.INVALID_METHOD;
return handleExceptionInternal(errorCode);
}

private ResponseEntity<Object> handleExceptionInternal(final ErrorCode errorCode) {
private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode) {
return ResponseEntity.status(errorCode.getHttpStatus())
.body(makeErrorResponse(errorCode));
}

private ErrorResponse makeErrorResponse(final ErrorCode errorCode) {
private ErrorResponse makeErrorResponse(ErrorCode errorCode) {
return ErrorResponse.builder()
.isSuccess(false)
.code(errorCode.name())
Expand All @@ -95,12 +121,12 @@ private ErrorResponse makeErrorResponse(final ErrorCode errorCode) {
.build();
}

private ResponseEntity<Object> handleExceptionInternal(final ErrorCode errorCode, final String message) {
private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode, String message) {
return ResponseEntity.status(errorCode.getHttpStatus())
.body(makeErrorResponse(errorCode, message));
}

private ErrorResponse makeErrorResponse(final ErrorCode errorCode, final String message) {
private ErrorResponse makeErrorResponse(ErrorCode errorCode, String message) {
return ErrorResponse.builder()
.isSuccess(false)
.code(errorCode.name())
Expand All @@ -109,12 +135,12 @@ private ErrorResponse makeErrorResponse(final ErrorCode errorCode, final String
.build();
}

private ResponseEntity<Object> handleExceptionInternal(final BindException e, final ErrorCode errorCode) {
private ResponseEntity<Object> handleExceptionInternal(BindException e, ErrorCode errorCode) {
return ResponseEntity.status(errorCode.getHttpStatus())
.body(makeErrorResponse(e, errorCode));
}

private ErrorResponse makeErrorResponse(final BindException e, final ErrorCode errorCode) {
private ErrorResponse makeErrorResponse(BindException e, ErrorCode errorCode) {
final List<ValidationError> validationErrorList = e.getBindingResult()
.getFieldErrors()
.stream()
Expand All @@ -128,4 +154,37 @@ private ErrorResponse makeErrorResponse(final BindException e, final ErrorCode e
.results(new ValidationErrors(validationErrorList))
.build();
}

private void logInfo(ErrorCode ec, Exception e, HttpServletRequest request) {
log.info(LOG_FORMAT_INFO, request.getMethod(), request.getRequestURI(), getUserId(),
getRole(), ec.getHttpStatus(), e.getClass().getName(), e.getMessage());
}

private void logWarn(Exception e, HttpServletRequest request) {
log.warn(LOG_FORMAT_WARN, request.getMethod(), request.getRequestURI(), getUserId(), getRole(), e);
}

private void logError(Exception e, HttpServletRequest request) {
log.error(LOG_FORMAT_ERROR, request.getMethod(), request.getRequestURI(), getUserId(), getRole(), e);
}

private String getUserId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication != null && authentication.isAuthenticated()) {
return authentication.getName(); // 사용자의 id
} else {
return "anonymous";
}
}

private String getRole() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication != null && authentication.isAuthenticated()) {
return authentication.getAuthorities().toString(); // 사용자의 role
} else {
return "anonymous";
}
}
}
7 changes: 7 additions & 0 deletions src/main/resources/console-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
</included>
58 changes: 58 additions & 0 deletions src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>

<configuration>
<property name="FILE_LOG_PATTERN"
value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] [%thread] %-5level [%C.%M:%L] - %msg %ex{5}%n"/>
<property name="CONSOLE_LOG_PATTERN"
value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg %ex{5}%n"/>

<springProfile name="local">
<include resource="console-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>

<springProfile name="dev">
<include resource="logs/common/file-info-appender.xml"/>
<include resource="logs/common/file-warn-appender.xml"/>
<include resource="logs/common/file-error-appender.xml"/>
<include resource="logs/error/file-info-appender.xml"/>
<include resource="logs/error/file-warn-appender.xml"/>
<include resource="logs/error/file-error-appender.xml"/>

<logger additivity="false" level="INFO" name="ErrorLogger">
<appender-ref ref="ERROR_ERROR"/>
<appender-ref ref="ERROR_WARN"/>
<appender-ref ref="ERROR_INFO"/>
</logger>

<root level="INFO">
<appender-ref ref="FILE-INFO"/>
<appender-ref ref="FILE-WARN"/>
<appender-ref ref="FILE-ERROR"/>
</root>
</springProfile>

<springProfile name="prod">
<include resource="logs/common/file-info-appender.xml"/>
<include resource="logs/common/file-warn-appender.xml"/>
<include resource="logs/common/file-error-appender.xml"/>
<include resource="logs/error/file-info-appender.xml"/>
<include resource="logs/error/file-warn-appender.xml"/>
<include resource="logs/error/file-error-appender.xml"/>

<logger additivity="false" level="INFO" name="ErrorLogger">
<appender-ref ref="ERROR_ERROR"/>
<appender-ref ref="ERROR_WARN"/>
<appender-ref ref="ERROR_INFO"/>
</logger>

<root level="INFO">
<appender-ref ref="FILE-INFO"/>
<appender-ref ref="FILE-WARN"/>
<appender-ref ref="FILE-ERROR"/>
</root>
</springProfile>

</configuration>
19 changes: 19 additions & 0 deletions src/main/resources/logs/common/file-error-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<included>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE-ERROR">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/log/common/error/error-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
</included>
19 changes: 19 additions & 0 deletions src/main/resources/logs/common/file-info-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<included>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE-INFO">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/log/common/info/info-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
</included>
19 changes: 19 additions & 0 deletions src/main/resources/logs/common/file-warn-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<included>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE-WARN">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/log/common/warn/warn-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
</included>
19 changes: 19 additions & 0 deletions src/main/resources/logs/error/file-error-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<included>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="ERROR_ERROR">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/log/error/error/error-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>100</maxHistory>
<totalSizeCap>4GB</totalSizeCap>
</rollingPolicy>
</appender>
</included>
19 changes: 19 additions & 0 deletions src/main/resources/logs/error/file-info-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<included>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="ERROR_INFO">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/log/error/info/info-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>100</maxHistory>
<totalSizeCap>4GB</totalSizeCap>
</rollingPolicy>
</appender>
</included>
Loading

0 comments on commit 5280d9e

Please sign in to comment.