aop를 활용하여 request, response 로그 출력

운영 업무를 하다보면 카탈리나 로그를 분석해야 하는 일들이 많은데 로그가 너무 많이 쌓이다보니 분석하는데 어려움이 있어 어떻게 하면 좋을지 고민하고 찾아보던중 aop를 이용하여 request, response 로그를 따로 출력 할 수 있는 방법을 찾을 수 있었다.

 

aop 코드 작성

@Aspect
@Component
@RequiredArgsConstructor
public class LoggingAspect {
  private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
  private final ObjectMapper objectMapper;

  // 모든 컨트롤러 대상으로 로그 수집
  @Pointcut("within(*..*Controller)")
  public void onRequest() { }

  @Around("onRequest()")
  public Object requestLogging(ProceedingJoinPoint joinPoint) throws Throwable {
    // 리퀘스트 정보를 담을 객체
    final RequestApiInfo apiInfo = new RequestApiInfo(joinPoint, objectMapper);

    // 로그 적재할 객체
    final LogInfo logInfo = new LogInfo(
            apiInfo.getUrl(),
            apiInfo.getMethod(),
            apiInfo.getHeaders(),
            apiInfo.getParameters(),
            apiInfo.getIp()
    );

    try {
      final Object result = joinPoint.proceed(joinPoint.getArgs());
      // 호출 결과를 로그 적재할 객체에 셋팅
      logInfo.setResponse(result);

      // 로그를 json 으로 변환하여 출력
      final String logMessage = objectMapper.writeValueAsString(Map.entry("logInfo", logInfo));
      logger.debug(logMessage);

      return result;
    } catch (Exception e) {
      // 예외가 발생했을때 로그 적재
      if (e instanceof BizException) {
        StringBuilder builder = new StringBuilder(e.toString());
        Exception exception = ((BizException) e).getException();
        if (exception != null) {
          builder.append(" || ");
          builder.append(exception.toString());
        }

        logInfo.setException(builder.toString());
      } else {
        final StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        logInfo.setException(sw.toString());
      }

      final String logMessage = objectMapper.writeValueAsString(logInfo);
      logger.error(logMessage);

      throw e;
    }
  }
}

 

log4j2.xml 파일 작성

appender 태그에 file 태그안에 경로를 알맞게 수정 필요.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProfile name="loc">
        <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="applicationLogFile"/>
        </root>

        <logger name="com.example.logging.aop.LoggingAspect" level="DEBUG">
            <appender-ref ref="requestLogFile"/>
        </logger>
    </springProfile>

    <appender name="requestLogFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>D:/log/requestLog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}][%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>D:/log/requestLog.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <appender name="applicationLogFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="com.example.logging.filter.CustomLogbackFilter" />
        <file>D:/log/application.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}][%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>D:/log/application.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <logger name="org.springframework.web" level="INFO"/>
    <logger name="org.springframework.security" level="INFO"/>
    <logger name="org.springframework.jndi" level="INFO"/>
    <logger name="org.springframework.beans.factory.support" level="INFO"/>
    <logger name="org.springframework.data.convert" level="INFO"/>

</configuration>

필요한 코드를 작성하고 테스트 해보면 아래와 같이 application.log, requestLog.log 파일이 별도로 생성되고

api에 대한 request, response 로그가 출력된 것을 알 수 있다.

 

여러 블로그를 찾아 본 결과 해당 방법을 이용하여 로그를 키바나로 보는 방법도 있고 별도 디비에 적재하여 관리하는 방법도 있지만 현재 회사에서 키바나 같은 인프라를 구축해서 사용하지 않기 때문에 파일로 저장해서 보는 방식으로 구현해봤다.

 

 

https://github.com/wifi-java/springboot-aop-logging

댓글