사용자 도구

사이트 도구


java:logback

Logback

종류별 정리

  • logback-core : 로그백의 핵심
  • logback-classic : log4j 처럼 애플리케이션의 일반 로깅 처리에 사용
  • logback-access : Tomcat/Jetty 등에 설치되어 웹 요청을 로깅할 수 있다.
  • logback-audit : 장시간 추적이 필요한 비지니스 애플리케이션의 이벤트 로깅

LOGBACK-1591 취약점 이슈

설정

  • logback.groovy를 사용하려면 groovy의존성이 필요하다.
    • logback.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <configuration>
          <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  <pattern>▶ %-5level %d{HH:mm:ss.SSS} [%thread] %class{36}.%method:%line - %msg%n</pattern>
              </encoder>
          </appender>
       
          <logger name="org.springframework" level="INFO" />
       
          <root level="info">
              <appender-ref ref="console" />
          </root>
      </configuration>
    • logback.groovy
      def HOSTNAME = hostname // scope 룰 때문에 nested block에서는 hostname 변수 사용불가
       
      appender('console', ConsoleAppender) {
          encoder(PatternLayoutEncoder) {
              pattern = "▶ %-5level %d{HH:mm:ss.SSS} [${HOSTNAME}] [%thread] %logger{36} - %msg%n"
          }
      }
       
      root(INFO, ['console'])

기본 Daily Rolling File Appener 예

appender('daily', RollingFileAppender) {
    file = "/logs/daily.log"
    append = true
 
    rollingPolicy(TimeBasedRollingPolicy) {
        fileNamePattern = "/logs/daily.log.%d{yyyy-MM-dd}.gz" // 자동 gz 압축
        maxHistory = 10 // 10일간만 보관
    }
 
    encoder(PatternLayoutEncoder) {
        pattern = '%-5level %d{yyyy-MM-dd HH:mm:ss} [%thread] %logger{36} - %msg%n'
    }
 
    // 특정 레벨 이상만 로깅
    filter(ch.qos.logback.classic.filter.ThresholdFilter) {
        level = INFO // INFO 이상 레벨만 로깅
    }
 
    // 마커가 필요할 때
    filter(EvaluatorFilter) {
        evaluator(ch.qos.logback.classic.boolex.OnMarkerEvaluator) {
            marker = '마커이름'
            marker = '또다른마커이름'
        }
        onMismatch = DENY
        onMatch = NEUTRAL
    }
}

기본 Fixed Window Rolling File Appender

appender('fixedWindowRollingFile', RollingFileAppender) {
    file = '/logs/fixedWindowRollingFile.log'
    append = true
 
    encoder(PatternLayoutEncoder) {
        pattern = '%-5level %d{yyyy-MM-dd HH:mm:ss} [%thread] %logger{36} - %msg%n'
    }
 
    rollingPolicy(ch.qos.logback.core.rolling.FixedWindowRollingPolicy) {
        fileNamePattern = '/logs/fixedWindowRollingFile.log.%i'
        minIndex = 1 // xx.log.1 ~ xx.log.5
        maxIndex = 5
    }
 
    triggeringPolicy(ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy) {
        maxFileSize = '100MB' // 파일 크기 100MB될때마다 롤링
    }
}

Multi line to Single line

Grep 시에 여러줄로 된 로그를 올바로 grep하기 힘들 경우 multi line 을 single line으로 합쳐서 로깅하면 좀 더 편하게 로깅 가능하다.

CoDeGeneration: Java: Logging in single line for grep

Layout 을 다음과 같이 지정하면 새줄기호(\n)가 \n 문자 그 자체로 표시된다. 예외 Stacktrace까지 모두 한 줄로 합친다.

XML 설정의 경우에는

%replace(%msg){"\n","\\n"} %replace(%xException){"\n", "\\n"}%nopex%n
-- 공백으로 변환됨.
%replace(%msg){'[\r\n]', ''}

groovy 설정으로 할 경우에는

%replace(%msg){"\n","\\n"} %replace(%xException){"\\n", "\\\\n"}%nopex%n

Spring Framework

  • LogbackConfigListener 설정시 리스너는 선언 순서에 따라 실행되므로 로깅 관련 리스너를 최상단으로 올려서 로깅 정책이 처음 부터 적용될 수 있도록 처리할 것.
  • LogbackConfigurer 웹과 무관하게 logback 설정 파일 위치지정. Spring의 ResourceUtils를 사용하므로 Spring 방식의 resource location 지정 방법 사용 가능.

Appenders

Marker

  • org.slf4j.Marker는 태그와 비슷한 것이다.
  • 어펜더는 특정 마크가 된 로그에 대해 필터링을 하여 처리할 수 있다.
  • 마커 A와 B가 있을 때,
    • A.add(B)A extends B와 유사하다. 즉, A는 B를 상속하므로, B를 처리하는 어펜더는 A도 함께 처리할 수 있다.

Shutdown Hook

  • logback이 정상 종료되게 하려면 JVM ShutdownHook을 걸어주는게 좋다.
  • 특히 Logback AsyncAppender 사용시에 모든 로그가 남은 뒤에 종료되게 하고자 한다면 더욱 중요하다.
  • ch.qos.logback.ext.spring.web.LogbackConfigListener 혹은 LogbackConfigServlet 사용시 서블릿 컨텍스트 종료시 자동 종료되도록 등록 되는 듯 보임.
  • Code로
    // assume SLF4J is bound to logback-classic in the current environment
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
    loggerContext.stop();
  • XML 설정
    <configuration debug="true">
       <!-- in the absence of the class attribute, assume 
       ch.qos.logback.core.hook.DelayingShutdownHook -->
       <shutdownHook/>
      .... 
    </configuration>

Filter

GEventEvaluator

  • EventEvaluatorFilter에서 Groovy 로 필터 조건을 줄수 있는 Evaluator.
  • ILoggingEvent 객체가 e 혹은 event 변수로 전달되며 TRACE,DEBUG,…ERROR도 변수로 전달된다.
  • level 변수는 toInt()로 숫자로 변환하여 비교할 수 있다. e.level.toInt() >= WARN.toInt() 형태.
  • 설정 파일 파싱할 때 미리 컴파일 되므로 매번 컴파일 된다고 걱정하지 않아도 된다.
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">      
      <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"> 
        <expression>
           e.level.toInt() >= WARN.toInt() &amp;&amp;  <!-- Stands for && in XML -->
           !(e.mdc?.get("req.userAgent") =~ /Googlebot|msnbot|Yahoo/ )
        </expression>
      </evaluator>
      <OnMismatch>DENY</OnMismatch>
      <OnMatch>NEUTRAL</OnMatch>
    </filter>
    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger - %msg%n
      </pattern>
    </encoder>
  </appender>

Stacktrace 에서 불필요한 스택 감추기

<configuration>
    <property name="STACKTRACE_FILTER" value="
        불필요한 stack 문자열 나열,
        org.junit.platform.engine.,
        org.junit.platform.launcher.,
        org.junit.platform.commons.util.ReflectionUtils.,
        org.junit.jupiter.engine.,
        org.junit.jupiter.params.provider.,
        org.junit.jupiter.api.AssertThrows.,
        org.junit.jupiter.api.AssertDoesNotThrow.,
        org.assertj.core.api.AssertionsFor,
        org.assertj.core.api.ThrowableAssert.,
        org.gradle.
    "/>
 
    <property name="CONSOLE_LOG_PATTERN"
              value=".... %exception{full,${STACKTRACE_FILTER}} %n"/>
  • 단점, String.contains() 로 검사한다.
  • Test 용 설정에 적용하면 좋음.
java/logback.txt · 마지막으로 수정됨: 2023/09/21 13:21 저자 kwon37xi