5.5 Logback(로깅 라이브러리)

김찬미·2024년 6월 10일
0
post-thumbnail

Logging이란?

로깅(Logging)이란 애플리케이션이 동작하는 동안 시스템의 상태나 동작 정보를 시간순으로 기록하는 것을 의미한다.

로깅은 개발 영역 중 '비기능 요구사항'에 속한다. 즉, 사용자나 고객에게 필요한 기능은 아니라는 의미이다. 하지만 로깅은 *디버깅하거나 개발 이후 발생한 문제를 해결할 때 원인을 분석하는 데 꼭 필요한 요소이다.

디버깅(영어: debugging): 컴퓨터 프로그램 개발 단계 중에 발생하는 시스템의 논리적인 오류나 비정상적 연산(버그)을 찾아내고 수정하는 작업 과정이다.

Logback이란?

자바 진영에서 가장 많이 사용되는 로깅 프레임워크는 Logback이다. Logback이란 log4j 이후에 출시된 로깅 프레임워크로서 slf4j를 기반으로 구현됐으며, 과거에 사용되던 log4j에 비해 월등한 성능을 자랑한다. 또한 스프링 부트의 spring-boot-starter-web 라이브러리 내부에 내장되어 있어 별도의 의존성을 추가하지 않아도 사용할 수 있다.

Logback의 특징

  • 크게 5개의 로그 레벨(TRACE, DEBUG, INFO, WARN, ERROR)을 설정할 수 있다.
    • ERROR: 로직 수행 중에 시스템에 심각한 문제가 발생해서 애플리케이션의 작동이 불가능한 경우를 의미한다.
    • WARN: 시스템 에러의 원인이 될 수 있는 경고 레벨을 의미한다.
    • INFO: 애플리케이션의 상태 변경과 같은 정보 전달을 위해 사용된다.
    • DEBUG: 애플리케이션의 디버깅을 위한 메시지를 표시하는 레벨을 의미한다.
    • TRACE: DEBUG 레벨보다 더 상세한 메시지를 표현하기 위한 레벨을 의미한다.
  • 실제 운영 환경개발 환경에서 각각 다른 출력 레벨을 설정해 로그를 확인할 수 있다.
  • Logback의 설정 파일을 일정 시간마다 스캔해 애플리케이션을 재가동하지 않아도 설정을 변경할 수 있다.
  • 별도의 프로그램 지원 없이도 자체적으로 로그 파일을 압축할 수 있다.
  • 저장된 로그 파일에 대한 보관 기간 등을 설정해 관리할 수 있다.

Logback 설정

logback-spring.xml 파일 생성

이제 Logback을 사용하기 위한 설정 파일을 만들어보자. 일반적으로 클래스패스(classpath)에 있는 설정 파일을 자동으로 참조하므로 Logback 설정 파일은 리소스 폴더 안에 생성한다. 파일명의 경우 스프링 부트에서는 logback-spring.xml 파일을 참조한다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Property 영역 -->
    <property name="LOG_PATH" value="./logs"/>

    <!-- Appenders 영역 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!-- Encoder 영역 -->
        <encoder>
            <!-- Pattern 영역 -->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <file>${LOG_PATH}/info.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/info_${type}.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
        </encoder>
    </appender>

    <!-- TRACE > DEBUG > INFO > WARN > ERROR > OFF -->
    <!-- Root Logger 영역 -->
    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="INFO_LOG"/>
    </root>
</configuration>

위에 주석으로 기재된 것처럼 설정 파일에는 여러가지 영역이 있다. 여기서는 각 영역 가운데 Logback 설정에서 가장 중요한 Appender 영역과 Root 영역에 대해 좀 더 자세히 알아보자.

Appender 영역

  • Appender 영역은 로그의 형태를 설정하고 어떤 방법으로 출력할지를 설정하는 곳이다.
  • Appender 자체는 하나의 인터페이스를 의미하며, 하위에 여러 구현체가 존재한다.

위 그림은 Logback의 Appender 구조이다. Logback의 설정 파일을 이용하면 그림에 등장하는 각 구현체를 등록해서 로그를 원하는 형식으로 출력할 수 있다.

Appender의 대표적인 구현체

  • ConsoleAppender: 콘솔에 로그를 출력
  • FileAppender: 파일에 로그를 저장
  • RollingFileAppender: 여러 개의 파일을 순회하면서 로그를 저장
  • SNTPAppender: 메일로 로그를 전송
  • DBAppender: 데이터베이스에 로그를 저장
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
          <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        ..(후략)

예제를 보면 appender 요소의 class 속성에 각 구현체를 정의하는 것을 볼 수 있다. 그리고 하단에 filter 요소로 각 Appender가 어떤 레벨로 로그를 기록하는지 지정한다.

<encoder>
	<!-- Pattern 영역 -->
	<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] 
    [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>

다음으로는 endcoder 요소를 통해 로그의 표현 방식을 패턴(pattern)으로 정의한다. 사용 가능한 패턴은 몇 가지 정해져 있으며, 대표적인 패턴은 다음과 같다.

패턴의미
%Logger{length}로거의 이름
%-5level로그 레벨, -5는 출력 고정폭의 값
%msg(%message)로그 메세지
%d로그 기록 시간
%p로깅 레벨
%F로깅이 발생한 애플리케이션 파일명
%M로깅이 발생한 메서드 이름
%I로깅이 발생한 호출지의 정보
%thread현재 스레드명
%t로깅이 발생한 스레드명
%c로깅이 발생한 카테고리
%C로깅이 발생한 클래스명
%m로그 메시지
%n줄바꿈
%r애플리케이션 실행 후 로깅이 발생한 시점까지의 시간
%L로깅이 발생하는 호출 지점의 라인 수

위와 같은 패턴을 활용해 다음과 같은 패턴을 만들 수 있다.

<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>

Root 영역

설정 파일에 정의한 Appender를 활용하려면 Root 영역에서 Appender를 참조해 로깅 레벨을 설정한다. 만약 특정 패키지에 대해 다른 로깅 레벨을 설정하고 싶다면 root 대신 logger를 사용해 예제와 같이 지정할 수 있다.

<!-- root 예제 -->
<root level="INFO">
	<appender-ref ref="console"/>
	<appender-ref ref="INFO_LOG"/>
</root>
<!-- logger 예제 -->
<logger name="com.springboot.api.controller" level="DEBUG" additivity="false">
    <appender-ref ref="console"/>
    <appender-ref ref="INFO_LOG"/>
</logger>

logger의 요소

  • name: 패키지 단위로 로깅이 적용될 범위를 지정
  • level: 로그 레벨을 지정
  • additivity: 앞에서 지정한 패키지 범위에 하위 패키지를 포함할지 여부
    (기본값은 true, 이 경우 하위 패키지를 모두 포함)

Logback 적용하기

이제 프로젝트에 Logback을 적용해 보자. Logback은 출력할 메시지를 Appender에게 전달한 Logger 객체를 각 클래스에 정의하여 사용한다. 이번에는 저번 글에서 작성했던 GetController에 Logger를 적용해보겠다.

1) Logger 선언

아래와 같이 GetController의 LOGGER 전역 변수로 logger 객체를 정의한다.

@RestController
@RequestMapping("/api/v1/get-api")
public class GetController {

    private final Logger LOGGER = LoggerFactory.getLogger(GetController.class);
    
    ..(후략)

LOgger는 LoggerFactory를 통해 객체를 생성한다. 이때 클래스의 이름을 함께 지정해서 클래스의 정보를 Logger에서 가져가게 한다.

2-1) 로그 출력 코드 삽입

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String getHello() {
        // 로그 출력 코드 삽입
        LOGGER.info("getHello 메소드가 호출되었습니다.");
        return "Hello World";
    }

    // http://localhost:8080/api/v1/get-api/name
    @GetMapping(value = "/name")
    public String getName() {
        // 로그 출력 코드 삽입
        LOGGER.info("getName 메소드가 호출되었습니다.");
        return "Flature";
    }

위와 같이 GetController의 코드를 수정하면 info 레벨에서 코드가 출력된다. 실제로 로그가 출력되는지 확인하기 위해 Swagger 페이지에 접속해서 테스트를 진행해보자.

Swagger에서 logger를 삽입한 메서드를 선택한 후 [Execute] 버튼을 눌러 테스트를 해보면

위와 같이 인텔리제이 IDEA의 콘솔 화면에 로그가 출력되는 것을 확인할 수 있다.

2-2) 변수의 값을 로그로 출력

로그를 통해 컨트롤러에 들어오는 값을 확인하고 싶다면 다음과 같이 코드를 작성한다.

    @GetMapping(value = "/variable1/{variable}")
    public String getVariable1(@PathVariable String variable) {
        // 변수의 값을 로그로 출력
        LOGGER.info("@PathVariable을 통해 들어온 값 : {}", variable);
        return variable;
    }

이렇게 변수를 지정해 변수로 들어오는 값을 로깅할 수도 있다. 변수의 값이 들어갈 부분을 중괄호({})로 지정하면 포매팅을 통해 로그 메시지가 구성된다.

예제의 메서드를 Swagger를 통해 'test'라는 단어를 입력하고 호출하면 아래와 같은 결과를 확인할 수 있다.

profile
백엔드 개발자

0개의 댓글

관련 채용 정보