Log - woowacourse-teams/2020-zeze GitHub Wiki
๋ก๊ทธ๋ ํ์๋ฅผ ์ํด ๋จ๊ธด๋ค. ํ๋ก๊ทธ๋จ ๊ฐ๋ฐ์ด๋ ์ด์ ์ ๋ฐ์ํ๋ ๋ฌธ์ ์ ์ ์ถ์ ํ๊ฑฐ๋ ์ด์ ์ํ๋ฅผ ๋ชจ๋ํฐ๋ง ํ๋ ์ ๋ณด๋ฅผ ๊ธฐ๋กํ๋ ๊ฒ์ด๋ค.
- ์ ํฌ ํ์ access, api, db, error 4๊ฐ์ ํ์ผ ํํ๋ก ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ธฐ๋ก ํ๊ณ , ์ถํ์ ELK๋ฅผ ์ ์ฉํ์ฌ ๋ก๊ทธ๋ฅผ ๊ฒ์ํ ์์ ์ ๋๋ค.
- ํ์ฌ๋ logback์ ์ฌ์ฉํ๊ณ ์๋๋ฐ ์ถํ์ ์ฑ๋ฅ์ด ๋ ์ข์ log4j2๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ ์์ ์ ๋๋ค.
- java spring ์์ฒด์์ ์ ๊ณตํ๋ logback์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ logback-spring.xml๋ก ํ์ผ์ ์์ฑํด์ฃผ์ด์ผ ํฉ๋๋ค.
- ๋ค๋ฅธ ์ค์ ์ ์ฌ์ฉํ์ฌ logback.xml ์ฌ์ฉ๋ ๊ฐ๋ฅํฉ๋๋ค.
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 60์ด๋ง๋ค ์ค์ ํ์ผ์ ๋ณ๊ฒฝ์ ํ์ธ ํ์ฌ ๋ณ๊ฒฝ์ ๊ฐฑ์ -->
<configuration scan="true" scanPeriod="60 seconds">
<springProperty scope="context" name="LOG_LEVEL" source="${logging.level.root}"/>
<!-- pattern ๊ฐ๋ตํ ๋ณด์ฌ์ค -->
<property name="LOG_PATTERN" value="%-5level %d{yy-MM-dd HH:mm:ss} - %msg%n"/>
<!-- API -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- ํ์ผ๊ฒฝ๋ก ์ค์ -->
<file>${LOG_PATH}/api.log</file>
<!-- ์ถ๋ ฅํจํด ์ค์ -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<!-- Rolling ์ ์ฑ
-->
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- .gz,.zip ๋ฑ์ ๋ฃ์ผ๋ฉด ์๋ ์ผ์๋ณ ๋ก๊ทธํ์ผ ์์ถ -->
<fileNamePattern>${LOG_PATH}/api.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- ํ์ผ๋น ์ต๊ณ ์ฉ๋ kb, mb, gb -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- ์ผ์๋ณ ๋ก๊ทธํ์ผ ์ต๋ ๋ณด๊ด์ฃผ๊ธฐ(~์ผ), ํด๋น ์ค์ ์ผ ์ด์๋ ํ์ผ์ ์๋์ผ๋ก ์ ๊ฑฐ-->
<maxHistory>30</maxHistory>
<!--<MinIndex>1</MinIndex> <MaxIndex>10</MaxIndex>-->
</rollingPolicy>
</appender>
<!-- ์๋ฌ์ ๊ฒฝ์ฐ ํ์ผ์ ๋ก๊ทธ ์ฒ๋ฆฌ -->
<appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<file>${LOG_PATH}/err.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<!-- Rolling ์ ์ฑ
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- .gz,.zip ๋ฑ์ ๋ฃ์ผ๋ฉด ์๋ ์ผ์๋ณ ๋ก๊ทธํ์ผ ์์ถ -->
<fileNamePattern>${LOG_PATH}/err.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- ํ์ผ๋น ์ต๊ณ ์ฉ๋ kb, mb, gb -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- ์ผ์๋ณ ๋ก๊ทธํ์ผ ์ต๋ ๋ณด๊ด์ฃผ๊ธฐ(~์ผ), ํด๋น ์ค์ ์ผ ์ด์๋ ํ์ผ์ ์๋์ผ๋ก ์ ๊ฑฐ -->
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<!-- DB -->
<appender name="DB" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- ํ์ผ๊ฒฝ๋ก ์ค์ -->
<file>${LOG_PATH}/db.log</file>
<!-- ์ถ๋ ฅํจํด ์ค์ -->
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<!-- Rolling ์ ์ฑ
-->
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- .gz,.zip ๋ฑ์ ๋ฃ์ผ๋ฉด ์๋ ์ผ์๋ณ ๋ก๊ทธํ์ผ ์์ถ -->
<fileNamePattern>${LOG_PATH}/db.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- ํ์ผ๋น ์ต๊ณ ์ฉ๋ kb, mb, gb -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- ์ผ์๋ณ ๋ก๊ทธํ์ผ ์ต๋ ๋ณด๊ด์ฃผ๊ธฐ(~์ผ), ํด๋น ์ค์ ์ผ ์ด์๋ ํ์ผ์ ์๋์ผ๋ก ์ ๊ฑฐ-->
<maxHistory>30</maxHistory>
<!--<MinIndex>1</MinIndex> <MaxIndex>10</MaxIndex>-->
</rollingPolicy>
</appender>
<!-- root๋ ๋ฒจ ์ค์ -->
<root level="${LOG_LEVEL}">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="Error"/>
</root>
<logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
<appender-ref ref="DB"/>
</logger>
<logger name="org.hibernate.type" level="TRACE" additivity="false">
<appender-ref ref="DB"/>
</logger>
</configuration>- application.yml์ ์๋์ ๊ฐ์ ์ค์ ์ผ๋ก access.log๋ฅผ ๋จ๊ธธ ์๋ ์์ต๋๋ค.
server:
tomcat:
accesslog:
directory: /[access log path]/
enabled: true
file-date-format: .yyyy-MM-dd
max-days: 14
pattern: '%h %l %u %t "%r" %s %b %D'
prefix: test_access
rotate: true
suffix: .log
basedir: /[tomcat base directory]/- ์ ํฌ ํ์ ๋ก๊ทธ ์ ๋ต์ ๋ง๋ ํ์ผ ์์ฑ๊ณผ ํจํด์ ํ์ฉํ๊ธฐ ์ํด logback-access.xml์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ต๋๋ค.
- tomcat์ ์ด์ฉํ์ฌ logback์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ tomcat์ logbackValve๋ฅผ ์ฐ๊ฒฐํด์ผ ํฉ๋๋ค.
- web.xml์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋๋ฐ springboot์์๋ configuration์ ์ด์ฉํ์ฌ web.xml์ ์ค์ ์ ์ถ๊ฐํด์ผํ๊ธฐ ๋๋ฌธ์ tomcat๋ง ์ค์ ํ๋ ์ฝ๋๋ฅผ ๊ตฌํํ์ต๋๋ค.
@Configuration
public class TomcatConfig implements WebServerFactoryCustomizer {
@Override
public void customize(WebServerFactory factory) {
TomcatServletWebServerFactory containerFactory = (TomcatServletWebServerFactory)factory;
LogbackValve logbackValve = new LogbackValve();
logbackValve.setFilename("src/main/resources/logback/logback-access.xml");
containerFactory.addContextValves(logbackValve);
}
}logback-access.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
<appender name="ACCESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/access.log</file>
<encoder>
<pattern>%fullRequest%fullResponse</pattern>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/access.%d{yyyy-MM-dd}_%i/.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<appender-ref ref="ACCESS_FILE"/>
</configuration>- ๋ชจ๋ Controller์ ๋ฉ์๋ ํธ์ถ์ log๋ก ๋จ๊ธฐ๊ธฐ ์ํด interceptor๋ฅผ ์ด์ฉํ์ฌ log๋ฅผ ๋จ๊ฒผ์ต๋๋ค.
- interceptor์์ prehandle์์ ๋ด์ฉ์ ์ถ๋ ฅํ๊ฒ ์๋ํด๋ณด์๋๋ฐ ๋์ง ์์๊ณ , afterComletion์ ๊ตฌํํด์ผ ๋ก๊ทธ๊ฐ ๋จ๊ฒจ์ก์ต๋๋ค.
- ๊ทธ ์์ฒญ์ ํด๋นํ๋ params, request body, response body๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด filter๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
- tomcat์์ httpservletrequet, response์ ๊ฐ์ ํ๋ฒ ์ฝ์ด๋ค์ด๋ฉด ๊ทธ ์ดํ๋ถํฐ๋ ๊ฐ๋ค์ ์ฝ์ด์ฌ ์ ์๊ธฐ ๋๋ฌธ์ filter์์ ๊ฐ์ cacheํ์ฌ ์ฌ์ฉํฉ๋๋ค.
public class LogServletWrappingFilter 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();
}
}@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception exception
) throws Exception {
ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper)request;
ContentCachingResponseWrapper cachingResponse = (ContentCachingResponseWrapper)response;
String params = request.getParameterMap()
.entrySet()
.stream()
.map(entry -> String.format("{\"%s\":\"%s\"}", entry.getKey(), Arrays.toString(entry.getValue())))
.collect(Collectors.joining(","));
log.warn("Params : {}", params);
log.warn(
"RequestBody : {} / ResponseBody : {}",
objectMapper.readTree(cachingRequest.getContentAsByteArray()),
objectMapper.readTree(cachingResponse.getContentAsByteArray())
);
}