로깅(Logging)

로그의 사전적 의미는 "무엇인가를 기록하는 행위" 입니다.
이러한 파일을 모아 놓은 것을 Log 파일이라고 합니다.

 

어플리케이션을 운영하던 도중에 장애가 발생, 잘못된 접근, 사용자의 악의적인 행위, 예상치 못한 작동 등과 같이 문제가 발생했을 경우, 문제의 원인을 파악해야 합니다. 이를 위해서 날짜, 시간, 서비스, 로직 등에 대한 정보가 필요합니다.

이런 정보를 얻기 위해서 Exception 이 발생하거나 중요한 기능들이 실행되는 부분에서는 로그(log) 를 남기는 것이 필요합니다.

로깅

간단히 말하자면 로그를 기록하는 행위를 로깅(loggin) 이라고 합니다.

정보를 제공하는 일련의 기록인 로그를 생성하도록 시스템을 작성하는 활동이며 버그에 대한 유용한 정보를 제공해 줄 수 있거나 성능에 관한 통계와 정보를 제공해 줄 수 있습니다.

 

로그를 출력하는 방법

대표적으로 2가지 방법이 있습니다.

 

1. System.out.println()

2. 로깅 라이브러리

System.out.pringln()

우리는 Java 를 처음 배울때 기록하는 기능인 System.out.println() 을 알고 있습니다. 하지만 이런 기능은 시스템을 운영하는 데 있어서 적절하지 않습니다. 왜 정보를 System.out.println() 으로 남기지 않는 것일까요?

 

System.out.println() 은 속도가 너무 느리며 또한 최소한의 정보도 없습니다. 

콜솔창에 출력만 하기 때문에 나중에 다시 확인할 수가 없는 단점과 데이터를 쌓고 남기기 힘들다는 치명적인 단점이 있습니다.

로깅라이브러리

로그는 주로 로깅 라이브러리를 이용하여 남깁니다. 

최소한의 정보(날짜/시간/타입 등) 이 제공되며, 데이터를 서버에 저장하고 파일화가 가능합니다.

 

자바 로깅 라이브러리 종류는 다음과 같습니다.

java.util.loggin.Logger
Log4J, log4j2
Logback
SLF4j

Log Level

로그는 레벨을 6단계로 나눕니다.

TRACE > DEBUG > INFO > WARN > ERROR > FATAL

 

 

Logger

Logger 는 프로그램 실행 중 발생하는 이벤트(정보, 경고, 오류 등) 을 콘솔(터미널) 이나 파일에 기록하는 도구입니다.

예를 들어, "프로그램이 정상적으로 작동하는지", "문제가 발생했는지" 등을 확인하기 위해 사용됩니다.

 

Logger 는 다양한 로그 레벨을 지원합니다.

SEVERE 은 심각한 오류, WARING 은 경고, INFO 은 정보, FINE 는 상세한 디버깅 정보 입니다.

 

Logger 은 java.util.loggin.Logger 클래스에서 사용됩니다.

import java.util.logging.Logger;

 

이 클래스를 이용해서 Logger 이름을 붙여 생성하고 로그 메시지를 기록합니다.

import java.util.logging.Logger;

public class MyClass {
    // Logger 생성
    private static final Logger logger = Logger.getLogger(MyClass.class.getName());

    public static void main(String[] args) {
        logger.info("프로그램 시작");

        try {
            int result = divide(10, 2); // 정상 실행
            logger.info("결과: " + result);
            
            result = divide(10, 0); // 오류 발생
        } catch (ArithmeticException e) {
            logger.severe("오류 발생: " + e.getMessage());
        }

        logger.info("프로그램 종료");
    }

    // 간단한 나눗셈 함수
    public static int divide(int a, int b) {
        return a / b; // b가 0이면 예외 발생
    }
}

 

출력 결과 (콘솔)

Nov 29, 2024 10:30:00 AM MyClass main
INFO: 프로그램 시작
Nov 29, 2024 10:30:00 AM MyClass main
INFO: 결과: 5
Nov 29, 2024 10:30:00 AM MyClass main
SEVERE: 오류 발생: / by zero
Nov 29, 2024 10:30:00 AM MyClass main
INFO: 프로그램 종료

 

로그를 콘솔뿐만 아니라 파일에도 저장할 수 있습니다.

이를 위해 FileHandler 를 사용합니다.

 

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Logger;

public class MyClass {
    private static final Logger logger = Logger.getLogger(MyClass.class.getName());

    public static void main(String[] args) {
        try {
            // FileHandler를 사용해 로그를 파일로 저장
            FileHandler fileHandler = new FileHandler("app.log", true);
            logger.addHandler(fileHandler);

            logger.info("프로그램 시작");
            logger.warning("경고: 로그 파일 확인");
            logger.severe("심각한 오류 발생!");

        } catch (IOException e) {
            logger.severe("파일 핸들러 초기화 오류: " + e.getMessage());
        }
    }
}

 

장점 

외부 라이브러리 사용 없이 로깅이 가능합니다.

단점

자바 1.4에 출시되었지만, 출시 시점에 이미 잘 만들어진 log4j 가 존재했습니다.

다른 라이브러리와 비교했을 때 퍼포먼스 (속도) 가 느립니다.

나만의 custom 레벨을 만들면 메모리 누수가 일어납니다.

 

Log4j

log4j 는 가장 오래된 로깅 프레임워크로써 Apache 의 Java 기반 로깅 프레임워크 입니다.

현재는 2015 년 기준으로 개발을 중단한 상태이며 성능적으로 더 개선되고 좋은 log4j2 가 등장하여 잘 사용하지 않습니다.

 

LogBack

LogBack 은 Java 애플리케이션에서 로그를 기록하기 위해 사용하는 강력하고 유연한 로깅 프레임워크 입니다.

Logback 은 SFL4J 의 기본 구현체로, SLF4J 를 통해 사용됩니다. 성능, 유연성, 설정 간결함에서 뛰어나며, 특히 Log4j 의 후계자로 설계되었습니다.

 

Logback 구성요소

Logback 은 크게 3가지 구성요소로 이루어져 있습니다.

1. Appender ( 출력 경로 )

로그를 출력하는 대상(콘솔, 파일)을 정의합니다.

예를 들어 콘솔에 출력하거나 로그 파일에 저장하는 것을 의미합니다.

 

2. Layout/Encoder( 출력 형식 )

로그 메시지의 출력 형식을 정의합니다.

로그에 날짜, 로그 레벨, 메시지를 어떻게 표시할지 설정합니다

 

3. Logger( 로거 )

애플리케이션 코드에서 직접 사용하는 로깅 인터페이스입니다.

특정 클래스나 패키지에 로거를 연결해 관리합니다.

 

LogBack 의 특징

Logback은 SLF4J 를 통해 사용되므로 다른 로깅 프레임워크로 쉽게 교체할 수 있습니다.

또한 LogBack 은 메모리 효율적이고 속도가 빠르게 설계되었으며, 로그 파일이 너무 커지면 자동으로 압축하거나 새로운 파일로 분리할 수 있습니다.

 

Logback 설정

Logback의 설정은 logback.xml 파일에서 정의됩니다.

이 파일은 src/main/resources 디렉토리에 위치해야 합니다.

<configuration>
    <!-- 콘솔에 로그를 출력하는 Appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 로그를 파일에 저장하는 Appender -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>app.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 기본 로거 설정 -->
    <root level="info">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

 

파일 분리 저장

로그 파일이 일정 크기를 초과하면 새로운 파일로 저장하도록 설정할 수 있습니다.

<configuration>
    <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="ROLLING_FILE" />
    </root>
</configuration>

 

실전 예시 코드

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogbackExample {
    private static final Logger logger = LoggerFactory.getLogger(LogbackExample.class);

    public static void main(String[] args) {
        logger.info("프로그램 시작");
        logger.debug("디버깅 중: 변수 값 확인");
        logger.warn("경고: 예상치 못한 상황");
        logger.error("오류 발생: 파일을 찾을 수 없습니다.");
    }
}

SLF4j

SLF4J 는 Simple Loggin Facade for Java 의 약자로, Java 에서 로그를 관리하기 쉽게 만들어주는 로깅 인터페이스입니다. 즉 SLF4J 는 다양한 로깅 프레임워크(Logback, Log4j, java.util.loggin 등) 와 프로그램을 연결해주는 중간 다리 역할 입니다.

 

예를 들어, SLF4J 를 TV 리모콘이라고 생각하시면, TV(로깅 프레임워크) 가 많지만, 리모컨(SLF4J) 를 사용하면 각기 다른 TV 를 쉽게 조작할 수 있습니다. 즉, SLF4J 를 사용하면 특정 로깅 프레임워크에 종속되지 않고, 필요에 따라 교체할 수 있습니다.

 

과거에는 로깅 프레임워크마다 사용법이 달랐습니다.

Log4j 는 Logger logger = Logger.getLogger(MyClass.class);
java.util.loggin 은 Logger logger = Logger.getLogger("MyClass");

 

이렇게 로깅 코드를 작성하면 특정 프레임워크에 종속되어, 교체하려면 코드를 모두 수정해야 했습니다.

사용법

사용하기 전에 Maven 으로 의존성을 추가해야 합니다.

프로젝트에 SLF4J 를 추가하고 Logback 을 구현체로 사용하셔야 합니다.

<dependencies>
    <!-- SLF4J API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.9</version>
    </dependency>
    <!-- Logback (SLF4J 구현체) -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.11</version>
    </dependency>
</dependencies>

 

SLF4J 와 함께 Logback 을 사용하는 경우, 로그 형식은 logback.xml 파일로 설정해야 합니다.

src/main/resources 에 추가합니다.

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

 

마지막으로 사용하고 싶은 클래스에 SLF4J 의 LoggerFactory 를 이용하여 Logger 을 생성합니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MySLF4JExample {
    // Logger 생성
    private static final Logger logger = LoggerFactory.getLogger(MySLF4JExample.class);

    public static void main(String[] args) {
        logger.info("프로그램 시작");
        logger.debug("디버깅 중: 변수 값 확인");
        logger.warn("경고: 예상치 못한 상황");
        logger.error("오류 발생: 파일을 찾을 수 없습니다.");
    }
}

 

실행 결과

2024-11-29 12:00:00 [main] INFO  MySLF4JExample - 프로그램 시작
2024-11-29 12:00:00 [main] DEBUG MySLF4JExample - 디버깅 중: 변수 값 확인
2024-11-29 12:00:00 [main] WARN  MySLF4JExample - 경고: 예상치 못한 상황
2024-11-29 12:00:00 [main] ERROR MySLF4JExample - 오류 발생: 파일을 찾을 수 없습니다.

'Java' 카테고리의 다른 글

List, ArrayList, LinkedList  (0) 2024.12.10
제어자  (0) 2024.12.04
해시맵(HashMap)  (1) 2024.11.29
MyBatis  (0) 2024.11.21
MVC구조 & Controller, Service, DAO, VO  (1) 2024.11.18