카테고리 없음

logback으로 로깅

bbangduck 2023. 5. 8. 18:49

error, warn, info 레벨로 나눠서 기록

error파일에 출력되는 로그 레벨 error,  warn, info 

warn파일에 출력되는 로그 레벨 warn, info 

info파일에 출력되는 로그 레벨  info 

 

1. 

spring-logback.xml

<configuration>
    <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>NEUTRAL</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <file>./log/error.log</file>
        <encoder>
            <pattern>[%X{request_id:-startup}] %d{HH:mm:ss.SSS} [%t] %-5level [%logger{0}:%line] - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>error.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>1</maxHistory>
            <totalSizeCap>100MB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <appender name="warn" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level> error</level>
            <onMatch>DENY</onMatch>
            <onMismatch>NEUTRAL</onMismatch>
        </filter>
        <file>./log/warn.log</file>
        <encoder>
            <pattern>[%X{request_id:-startup}] %d{HH:mm:ss.SSS} [%t] %-5level [%logger{0}:%line] - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>warn.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>1</maxHistory>
            <totalSizeCap>100MB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>./log/info.log</file>
        <encoder>
            <pattern>[%X{request_id:-startup}] %d{HH:mm:ss.SSS} [%t] %-5level [%logger{0}:%line] - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>info.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>1</maxHistory>
            <totalSizeCap>100MB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%X{request_id:-startup}] %d{HH:mm:ss.SSS} [%t] %-5level [%logger{0}:%line] - %msg%n</pattern>
        </layout>
    </appender>

    <root level="info">
        <appender-ref ref="error" />
        <appender-ref ref="warn" />
        <appender-ref ref="info" />
        <appender-ref ref="STDOUT"/>
    </root>

</configuration>

 

2. graphQL 로깅도 하고 싶은 경우

@Component
public class CustomServletWrappingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper wrappingResponse = new ContentCachingResponseWrapper(response);
        filterChain.doFilter(wrappingRequest, wrappingResponse);
        wrappingResponse.copyBodyToResponse();
    }
}
@Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);

    private final ObjectMapper objectMapper;

    public LoggingInterceptor(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getClass().getName().contains("SecurityContextHolderAwareRequestWrapper")) return true;
        String request_id = UUID.randomUUID().toString();
        MDC.put("request_id", request_id);

        final ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request);

        logger.info("Request URL : {}", ServletUriComponentsBuilder.fromRequest(cachingRequest).toUriString());
        logger.info("Request Method : {}", cachingRequest.getMethod());
        logger.info("Request Header : {}", objectMapper.writeValueAsString(cachingRequest.getHeaderNames()));

        // GraphQL 요청인 경우
        if (cachingRequest.getRequestURI().startsWith("/graphql")) {
            logger.info("GraphQL Request Body : {}", new String(cachingRequest.getContentAsByteArray()));
        }
        // REST API 요청인 경우
        else {
            logger.info("REST API Request Body : {}", new String(cachingRequest.getContentAsByteArray()));
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (request.getClass().getName().contains("SecurityContextHolderAwareRequestWrapper")) return;
        final ContentCachingResponseWrapper cachingResponse = (ContentCachingResponseWrapper) response;

        logger.info("Response Status : {}", cachingResponse.getStatus());
        logger.info("Response Header : {}", objectMapper.writeValueAsString(cachingResponse.getHeaderNames()));

        // GraphQL 요청인 경우
        if (request.getRequestURI().startsWith("/graphql")) {
            logger.info("GraphQL Response Body : {}", new String(cachingResponse.getContentAsByteArray()));
        }
        // REST API 요청인 경우
        else {
            logger.info("REST API Response Body : {}", new String(cachingResponse.getContentAsByteArray()));
        }

        cachingResponse.copyBodyToResponse();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        MDC.remove("request_id");
    }
}

 

 



`LoggingInterceptor` 클래스는 Spring의 `HandlerInterceptorAdapter` 인터페이스를 구현합니다.

이 클래스는 요청 처리 전, 후 및 완료 후 로깅을 수행합니다.

`ObjectMapper` 클래스를 사용하여 요청 및 응답의 JSON 데이터를 파싱하고, `ContentCachingRequestWrapper` 및 `ContentCachingResponseWrapper` 클래스를 사용하여 요청 및 응답을 버퍼링합니다.

또한, Mapped Diagnostic Context(MDC)를 사용하여 각 로깅 이벤트에 대한 고유 식별자를 생성합니다.

`CustomServletWrappingFilter` 클래스는 Spring의 `OncePerRequestFilter` 인터페이스를 구현합니다. 

이 클래스는 `ContentCachingRequestWrapper` 및 `ContentCachingResponseWrapper` 클래스를 사용하여 모든 요청과 응답을 버퍼링합니다. 

또한, `doFilterInternal()` 메소드에서 요청을 처리한 후, 버퍼링된 응답 내용을 클라이언트에 전송하기 전에 복사(copy)합니다.