파란하늘의 지식창고
반응형

Spring Boot Logback 사용 기본 설정

Spring Boot를 사용한 application에서 로그를 사용하려면 다음 dependency를 추가한다.
(현재는 logback이 기본 참조되어 있는데 과거엔 log4j -> slf4j가 기본 설정이었다.)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

Spring Boot가 제공하는 logback 기본 설정 위치는 spring boot의 base.xml 파일이며 다음과 같다.

https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/base.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
Base logback configuration provided for compatibility with Spring Boot 1.1
-->

<included>
	<include resource="org/springframework/boot/logging/logback/defaults.xml" />
	<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
	<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
	<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
	<root level="INFO">
		<appender-ref ref="CONSOLE" />
		<appender-ref ref="FILE" />
	</root>
</included>

Log pattern이나 log charset, threshold 설정은 defaults.xml에 되어 있다.
https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
Default logback configuration provided for import
-->

<included>
	<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
	<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
	<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />

	<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
	<property name="CONSOLE_LOG_CHARSET" value="${CONSOLE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>
	<property name="CONSOLE_LOG_THRESHOLD" value="${CONSOLE_LOG_THRESHOLD:-TRACE}"/>
	<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
	<property name="FILE_LOG_CHARSET" value="${FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>
	<property name="FILE_LOG_THRESHOLD" value="${FILE_LOG_THRESHOLD:-TRACE}"/>

	<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
	<logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
	<logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
	<logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/>
	<logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
	<logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/>
	<logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
	<logger name="org.springframework.boot.actuate.endpoint.jmx" level="WARN"/>
</included>

이 설정을 기반으로 file appender 설정이 되어 있다.
https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/file-appender.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
File appender logback configuration provided for import, equivalent to the programmatic
initialization performed by Boot
-->

<included>
	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>${FILE_LOG_THRESHOLD}</level>
		</filter>
		<encoder>
			<pattern>${FILE_LOG_PATTERN}</pattern>
			<charset>${FILE_LOG_CHARSET}</charset>
		</encoder>
		<file>${LOG_FILE}</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
			<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
			<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
			<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
			<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7}</maxHistory>
		</rollingPolicy>
	</appender>
</included>

사용자는 최종적으로 base.xml을 참조한 logback.xml이나 logback-spring.xml을 해당 프로젝트의 /src/main/resources에 추가하여 사용하게 된다.
(Spring Boot 변수 사용을 위해 logabck-spring.xml을 사용하는 것을 추천)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <!-- ... 이하 생략. 사용자별 개별 설정 -->
</configuration>

log 위치 설정

위에 기본 제공되는 설정을 보면 기본 로그 위치가 다음과 같다.

<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>

LOG_FILE 변수를 따로 선언하지 않은 경우 docker image를 빌드하여 서버를 띄우면 /tmp/spring.log 에 쌓이게 된다.

위치를 변경하고자 하는 경우 logback.xml이나 logback-spring.xml에서 다음과 같이 LOG_FILE 변수를 선언하면 된다.

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE configuration> 
<configuration> 
    <property name="LOG_FILE" value="${LOG_FILE:-./logs/catalina.log}"/> 
    <include resource="org/springframework/boot/logging/logback/base.xml"/> 
</configuration>


이와 같이 설정되어 있고 kubernetes에 배포하여 사용하는 경우 kubernetes의 deployment 설정에 workingDir이 선언되어 있으면 해당 위치 하위에 logs/catalina.log가 생성되게 된다.

예를 들어 workingDir: /usr/local/app 와 같이 설정되어 있다면 /usr/local/app/logs/catalina.log 위치에 로그가 저장된다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: '${name}-deployment'
  namespace: default
spec:
  template:
    spec:
      containers:
        - workingDir: /usr/local/app
          image: '이미지주소'
          ... 이하 생략

Tomcat Access Log 설정

Spring Boot는 accesslog에 대한 설정도 제공한다.

tomcat access log 설정을 하려는 경우 다음 옵션을 설정하면 된다. (jetty, undertow도 동일한 형태)

server.tomcat.accesslog.enabled=true

이 옵션은 Spring Boot의 ServerProperties에서 제공하는 설정 옵션이다.
https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java

access log의 경우 기본 설정 위치가 다음과 같다.

/tmp/tomcat.[port].[난수]/logs/access_log.[날짜].log

이는 ServerProperties의 AccessLog 기본 설정 위치가 다음과 같기 때문이다.

private String directory = "logs";

access log 위치 설정

ServerProperties의 기본 값은 다음과 같다.

server.tomcat.accesslog.directory=logs


이 경우 ServerProperties의 accesslog의 경우 기본 위치가 /tmp/tomcat.[port].[난수]/logs 이다.
위치가 /tmp/tomcat.[port].[난수] 하위로 고정되어 있다.

절대 경로로 지정해야 한다.
예를 들어 /usr/local/app/logs 하위에 위치하고 싶다면

server.tomcat.accesslog.directory=/usr/local/app/logs

위와 같이 설정해야 한다.

Spring Boot가 제공하는 server.tomcat.accesslog.* 옵션의 경우 tomcat-embed-core의 org.apache.catalina.valves.AccessLogValve를 사용한다.

Valve는 Tomcat이 제공하는 개념으로 연결된 Catalina container (Engine, Host 또는 Context)에 대한 요청 처리 파이프라인에 추가할 구성 요소이다.
https://tomcat.apache.org/tomcat-10.1-doc/config/valve.html

사용자는 이 ValveBase를 확장하여 개별 구현할 수 있다.
AccessLog를 위해 Tomcat의 경우 AccessLogValve를 제공하고 있고 Logback은 LogbackValve를 제공하고 있다.

https://tomcat.apache.org/tomcat-10.1-doc/config/valve.html#Access_Logging
https://logback.qos.ch/access.html

Tomcat이 제공하는 AccessLogValue도 로그를 보는데 불편하진 않지만 logback을 사용하는 설정이 아니기 때문에 파일 압축 같은  로그 처리를 제공하지 않는다.
또한 로그파일의 위치 설정의 처리 방식이 logback과 다르기 때문에 logback 설정과 tomcat의 AccessLogValue 설정을 같이 사용하면 각각의 설정을 개별로 숙지해야 하는 것이 불편하다.

굳이 catalina.out은 logback을 사용하고 access log는 tomcat log를 사용하여 설정을 관리하는 방식을 두 가지로 하는 것보단 catalina.out, access log, 기타 등등 추가될 여러 경우의 로그들을 모두 logback 단일로 관리하는 것이 현재 시점에선 나은 방법일 듯하다.

Logback Tomcat Access Log 설정

Logback은 AccessLog에 설정을 위해 LogbackValue를 제공한다.
이를 사용하여 로그를 구현하면 된다.

https://logback.qos.ch/access.html

logback-access dependency를 추가한다.

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
</dependency>

Spring Boot는 ConfigurableWebServerFactory를 구현하여 Jetty, Netty, Tomcat, Undertow를 customize 할 수 있다.

https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.servlet.embedded-container.customizing.programmatic

Tomcat Servlet의 경우 다음과 같이 WebServerFactoryCustomizer bean을 선언하여 LogbackValve를 추가할 수 있다.

@Component
public class LogbackTomcatServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
		
        var logbackValve = new LogbackValve();
        logbackValve.setFilename("logback-access.xml");
        factory.addEngineValves(logbackValve);
		
    }

}

logback-access.xml을 대략 다음처럼 선언한다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
    <property name="ACCESS_LOG_FILE" value="${ACCESS_LOG_FILE:-./logs/access.log}"/>
    <property name="ACCESS_LOG_PATTERN" value="${ACCESS_LOG_PATTERN:-combined}"/>
	
    <appender name="ACCESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${ACCESS_LOG_PATTERN}</pattern>
        </encoder>
        <file>${ACCESS_LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${ACCESS_LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7}</maxHistory>
        </rollingPolicy>
    </appender>

    <appender-ref ref="ACCESS_FILE" />
</configuration>

logback-access-spring-boot-starter 소개

Logback Tomcat AccessLog 설정의 경우 한 가지 단점이 있다.

logback에서 제공하는 LogbackValve가 classpath resource를 지원하지 않는다.
즉, 여러 프로젝트가 사용할 logback-access.xml 설정을 공통으로 쓰기 위해 참조 jar의 classpath:[logback-access.xml위치]의 파일을 사용하는 것을 지원하지 않는다.

LogbackValve를 확장하여 개별 구현하기 까다로운 부분이 있어 대안으로 logback-access-spring-boot-starter를 사용하는 것을 추천한다.

https://github.com/akkinoc/logback-access-spring-boot-starter


2023-02-22 추가

만약 logbackValve를 추가한 이후 application에서 다음과 같은 에러가 발생한다면

ERROR c.b.b.c.u.s.r.c.ApiExceptionHandler - Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.
java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.

이는 추가한 logbackValve가 async supported 기본 설정이 false여서 그렇다.

다음과 같이 asyncSupported 설정을 활성화해주어야 한다.

@Component
public class LogbackTomcatServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
		
        var logbackValve = new LogbackValve();
        logbackValve.setFilename("logback-access.xml");
        logbackValve.setAsyncSupported(true);
        factory.addEngineValves(logbackValve);
		
    }

}
반응형
profile

파란하늘의 지식창고

@Bluesky_

내용이 유익했다면 광고 배너를 클릭 해주세요