본문 바로가기
공부방

[TIL 06/23] Logging, Logback

by hseong 2023. 6. 23.

1. Logging

시스템을 작동할 때 시스템 작동 상태의 기록과 보존, 이용자의 습성 조사 및 시스템 동작의 분석 등을 하기 위해 작동 중의 각종 정보를 기록해둘 필요가 있다. 이 기록을 만드는 것을 로깅이라 한다. 즉, 로그 시스템의 사용에 관계된 일련의 사건(이벤트)을 시간의 경과에 따라 기록하는 것이다.

  • 프로그램 개발 중 또는 완료 후에 발생할 수 있는 오류에 대해 디버깅하거나, 운영 중인 프로그램 상태를 모니터링 하기 위해 필요한 정보를 기록하는 것
  • 버그 발생 시 실제 운영중인 애플리케이션에서 어떤 일이 일어났는지 파악하기 어렵기 때문에 로그를 통해서 API 호출 흐름을 파악하고 에러 발생 지점을 찾아내 버그 픽스를 진행할 수 있다.
  • System.out.println()을 사용하면 동기화로 인한 오버헤드가 발생하여 성능 저하가 발생할 수 있다. 운영상에서는 절대 사용해서는 안 된다.

Java Logging Framework

  • java.util.logging
  • Apache Commons logging(ACL)
  • Log4J
  • Logback
  • Slf4J(Simple Logging Facade For Java)
  • 보통 Logback과 Log4J2가 가장 많이 사용된다고 한다.

SLF4J

  • SLF4J는 사용자가 원하는 로깅 프레임워크를 쉽게 사용할 수 있도록 로그를 사용하는 방법을 추상화한 프레임워크이다.
  • 개발자는 SLF4J API를 통해서 로그 구현체가 변경되는 것에 신경쓰지 않고 동일한 방법으로 로그를 찍을 수 있다.
  • 기존에 ACL이 런타임에 클래스 로더에 의존하여 로그 구현체를 선택하던 것에 반해 SLF4J는 클래스 로더에 의존하지 않고 컴파일 타임에 하나의 특정 로깅 프레임워크만 사용하도록 하드 와이어링 된다.
    출처: SLF4J

Log Level

  1. trace
  2. debug
  3. info
  4. warn
  5. error
  • 필요에 따라 로깅 레벨을 조정하여 원하는 만큼 로그를 남길 수 있다.
  • 개발시에는 trace, debug로 시스템이 우리가 원하는대로 동작하는지 찍어볼 수 있을 것이다. 반대로 운영시에는 로깅 레벨을 warn으로 설정하여 중대한 정보만을 기록하도록 조정할 수 있을 것이다.Logger
  • LoggerLoggerFactory를 통해서 만들 수 있다. 이 때, 메서드 단위에서 만들지 않고 클래스 레벨에서 생성한다.
  • Logger는 이름 기반으로 생성되며 일반적으로 패키지를 포함한 클래스명으로 주게 된다.
  • 클래스명.classLoggerFactory.getLogger()에 인수로 전달하면 패키지명이 포함된 클래스명이 로거의 이름이 된다.
  • private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
  • 인스턴스마다 개별 로거를 가지는 것이 아닌 해당 클래스 인스턴스가 모두가 로거를 공유하기 위해 static으로 선언한다.
    • 로거는 한 번 만들어지면 객체가 만들어지고 소멸시까지 절대 바뀌어서는 안 된다.
  • 로거의 사용 방법은 다음과 같이 로거.로깅레벨("내용")으로 사용한다.
  • logger.info("Created new order. orderId is {}", order.getId());
  • 인수로 전달하는 문자열에 중괄호({})를 사용하면 뒤에 들어오는 인수의 순서에 따라 자동으로 해당 자리의 문자열로 치환된다.

2. Logback

  • 로그백 설정을 통해서 우리는 출력되는 로그의 형태를 지정하거나 출력되길 원하는 로깅 레벨을 지정할 수 있다.
  • 로그백은 다양한 설정 파일을 읽을 수 있고 각 파일의 우선수위는 다음과 같다.
    1. logback-test.xml
    2. logback.groovy
    3. logback.xml
    4. 기본 설정 전략을 따름
  • 이러한 로그백 설정 파일은 resources 디렉터리 아래에 배치한다.
  • 기본적인 설정은 로그백 공식문서 Chapter 3: Configuration 에서 찾을 수 있다.

src/main/resources/logback.xml

<configuration> 
  <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/> 
  <import class="ch.qos.logback.core.ConsoleAppender"/> 

  <appender name="STDOUT" class="ConsoleAppender"> 
    <encoder class="PatternLayoutEncoder">
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
    </encoder> 
  </appender>

  <root level="debug"> 
      <appender-ref ref="STDOUT"/> 
  </root>
</configuration>
  • 해당 설정에서 모든 로깅 레벨은 debug로 설정되어 있다.
  • STDOUT이라는 이름을 가진 appender를 통해 로그 출력을 지정하고 있다.
    • appender는 어디에 어떤 포맷으로 로그를 남길 수 있는 지 설정한다.
    • 로그는 콘솔에 남길 수도 있고 파일, DB 또는 Kibana를 이용할 수도 있다.
  • pattern을 이용해 어떠한 포맷으로 로그를 남길지 지정하고 있다.
  • logger
  • 로거 생성시 전달한 이름을 이용하면 특정 패키지 하위의 로깅 레벨에 대해서도 지정할 수 있다.
  • 다음은 com.example.hello 패키지 하위의 로거 레벨을 debug로 지정, additivity 설정을 통해 중복 출력이 되지 않도록 지정, STDOUT이라는 appender로 출력되도록 지정한 것이다.
<logger name="com.example.hello" level="debug" additivity="false">
    <appender-ref ref="STDOUT" />
</logger>

property

  • 동일한 패턴을 변수처럼 여러 블럭에서 사용하기 위해 property 블럭을 사용할 수도 있다.
  • 마치 key=value처럼 name과 그에 해당하는 value를 지정할 수 있다.
<property name="LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
  • 이렇게 정의한 property는 여러 appender에서 출력 패턴을 지정하기 위해서 사용할 수 있다.
<appender name="STDOUT" class="ConsoleAppender">
  <encoder class="PatternLayoutEncoder">
    <pattern>${LOG_PATTERN}</pattern>
  </encoder>
</appender>
  • 이러한 패턴의 정의 방법은 Logback 공식문서 Chapter 6: Layouts 에서 찾아볼 수 있다.
  • 로깅 프레임워크 내부에서 만들어진 로깅 이벤트가 패턴을 정의하며 지정한 conversion word에 의해서 문자열로 변환되어 우리가 원하는 출력문이 만들어진다.

appender

  • 우리는 다양한 appender를 추가하여 로그의 출력을 콘솔, 파일, DB 다양한 지정이 가능하다.
  • 다음은 로그의 파일 출력을 위한 appender이다.
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <file>logs/testFile.log</file>
  <encoder> 
    <pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
  </encoder> 
</appender>
  • 출처: Chapter 4: Appenders
  • 이를 추가하여 애플리케이션을 실행시키면 logs/testFile.log 로그 파일이 생성된 것을 확인할 수가 있다.
  • 계속해서 애플리케이션을 실행시키면 동일한 로그 파일에 로그가 추가되는 것을 확인할 수 있는데 이는 appender<append>false</append>를 추가하거나 타임스탬프를 파일 이름 뒤에 추가하는 방식으로 해결할 수 있다.
  • 이 외에 파일로 로그를 남기는 경우 RollingFileAppender를 많이 사용한다.
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> 
  <file>logFile.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 
    <!-- daily rollover --> 
    <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> 
    <!-- keep 30 days' worth of history capped at 3GB total size --> 
    <maxHistory>30</maxHistory> 
    <totalSizeCap>3GB</totalSizeCap> 
  </rollingPolicy> 
  <encoder> 
    <pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern> 
  </encoder> 
</appender>
  • RollingFileAppenderTimeBasedRollingPolicy를 이용하면 매일 새로운 로그 파일이 생성되어 해당 파일에 로그가 기록된다.
  • 날짜가 변경되는 시점에 새로운 logFile.log가 생성되고 기존의 파일은 logFile.2023-06-23.log라는 이름으로 변경된다.

3. 스프링 부트

로깅

  • 스프링 부트는 우리가 지금까지 logback.xml로 설정한 다양한 로깅 설정을 편리하게 지원한다.
  • 기본적으로 다음과 같은 로깅 포맷이 지정되어 있다.
2023-06-23T18:15:22.625+09:00 INFO 119272 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 
2023-06-23T18:15:22.631+09:00 INFO 119272 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 
2023-06-23T18:15:22.632+09:00 INFO 119272 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.8] 
2023-06-23T18:15:22.695+09:00 INFO 119272 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 
2023-06-23T18:15:22.695+09:00 INFO 119272 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization
  • 또한 application.properties를 통해서 편리하게 로그 레벨을 변경할 수 있다.
logging:
  level:
    root: info

스프링 부트 스타터

  • 예전에는 하나의 애플리케이션을 구성하기 위해 다양한 라이브러리에 대한 의존성을 일일히 추가하여야 했다.
  • 스프링 부트가 제공하는 starter 의존성은 관련된 의존성을 모아둔 세트이다. 이를 통해 우리는 굉장히 빠르게 하나의 프로젝트를 구성하여 개발에만 집중할 수 있다.

SpringBootApplication

  • @SpringBootApplication 하위에 컴포넌트 스캔을 비롯한 설정 정보가 포함되어 있다.
  • 이를 통해 기본적인 것들이 설정되고 특별히 설정이 필요한 부분에 대해서만 신경써서 진행할 수 있다.

실행 가능 Jar

  • 스프링 부트는 특별한 구조의 jar를 제공함으로써 별도의 라이브러리를 설정해주지 않고도 jar 하나만으로 애플리케이션을 기동시킬 수 있는 편리함을 제공한다.