index

logging

로깅은 소프트웨어가 실행되는 동안 발생하는 정보를 기록하는 작업을 말한다

기록되는 데이터를 로그라고 하며 이 데이터를 통해 시스템의 상태를 파악하고 오류를 분석하며 성능을 모니터링한다 (observability)

다음과 같은 목적으로 애플리케이션 내부에서 일어나는 일을 로그를 사용할 수 있다

debugging, troubleshooting

특정 api에 호출 시 오류가 발생했다면 로그를 통해 원인을 분석한다

e.g) 결제 시스템에서 오류 발생 -> 로그를 조회하여 문제 발생 지점 확인

auditing, security

보안 및 법적 규제 준수를 위해 로그 기록을 활용한다

e.g) 사용자 로그인 이력, 데이터 수정 이력 등

performance monitoring

api 응답 시간이 느려지는 경우 로그를 통해 특정 요청에서 병목 현상이 있는지 확인한다

e.g) 데이터베이스 쿼리 시간의 로그를 확인하여 성능 확인

logging system components

logger

log forwarder

log storage

log analysis tools

alerting system

log level

로그는 중요도(severity)에 따라 여러 레벨로 구분된다

모니터링 시스템이나 알림 시스템에서 로그 레벨을 통해 특정 로그만 필터링하거나 저장할 수 있다

주요 로그 레벨

log format

unstructured(human-readable): simple text log

일반적인 로그는 사람이 읽기 쉬운 단순한 텍스트 포맷을 가진다

이 포맷은 로깅 시스템을 통해 검색, 필터링, 자동 분석 등의 기능을 활용하기 어렵다

2025-03-01 10:00:00 INFO User login: userId=12345
2025-03-01 10:00:05 ERROR Database connection failed

structured(machine-readable): json, key-value

structured 로깅은 기계가 읽을 수 있는 포맷으로 로그를 출력하는 방법을 말하며 로그 관리 시스템이 효율적인 검색 및 분석을 가능하게 한다

json, key-value 방식의 포맷을 가질 수 있으며 로그 관리 벤더에 따라 ecs (elastic common schema), logstash 포맷 등으로 나뉠 수 있다

{
  "timestamp": "2025-03-01T10:00:00Z",
  "level": "INFO",
  "message": "User login",
  "userId": "12345"
}

logging destination

표준: 터미널에서 로그를 출력한다 (System.out.println())

파일: 로그를 파일로 저장한다 (/var/log/app.log)

로깅 시스템: grafana loki, elasticsearch, opentelemetry collector, aws cloudwatch 등과 같은 로깅 시스템에 로그 내용을 전달한다 (structured format)

데이터베이스: db에 로그를 저장하고, 로그 관리 시스템과 연동한다

logger vs console

콘솔은 사용자와 컴퓨터가 상호작용할 수 있는 텍스트 기반 입출력 인터페이스를 말한다

일반적으로 운영체제나 개발환경에서 제공하는 CLI(터미널, 명령 프롬프트, IDE 터미널)를 콘솔이라고 말한다

로거와 콘솔을 사용하는 개발자의 입장에서 보면 문자열을 출력하는 동작을 수행하기 위해 참조하는 객체가 다를 뿐 둘의 차이가 크게 느껴지지 않을 수 있다

// Hello Logger 로깅
logger.info("Hello Logger");

// 콘솔에 Hello Console 출력
System.out.println("Hello Console");

그러나 로거와 콘솔은 근본적으로 용도 자체가 다르다

콘솔은 터미널이라고도 하며, 앞서 설명했듯이 사용자가 컴퓨터에게 명령을 내리거나 컴퓨터가 처리한 결과값을 사용자에게 보여주기 위한 텍스트 기반의 입출력 인터페이스로써 사용된다

위의 System.out.println 메서드는 특정 문자열을 출력해달라고 운영 체제에게 요청하는 동작을 수행하며, 운영 체제는 전달받은 문자열을 터미널로 출력할 뿐이다

즉, 콘솔은 특정 운영 체제와 통신할 수 있는 도구에 불과하다

반면 로거는 애플리케이션을 모니터링하기 위한 도구로써 존재한다

데이터를 콘솔과 마찬가지로 터미널 창에 출력할 수 있을 뿐만 아니라, 네트워크를 통해 외부 모니터링 시스템이나 수집기에 전달하거나 데이터베이스에 저장할 수 있다

로거와 콘솔의 추가적인 차이점은 다음과 같다

log level

콘솔: 로그 수준 지원 X

로거: DEBUG, INFO, WARN, ERROR, TRACE 등의 로그 레벨을 통해 메시지 우선순위와 중요도 구분을 지원하여 외부 모니터링 시스템에서 필터링할 수 있도록 한다

formatting

콘솔: 수동으로 포맷을 작성해야 한다

로거: 날짜, 시간, 클래스명, 스레드 정보 등을 자동으로 포함하여 내용을 구성할 수 있다 (로그 패턴 활용)

performance

콘솔: 동기식으로 동작하며 대량 로그 시 성능 저하가 발생한다

로그: 비동기 로깅, 버퍼링 등으로 성능을 최적화할 수 있다

function expansion

콘솔: 확장 불가

로거: 다양한 로깅 프레임워크(slf4j, logback, log4j 등)와 통합하여 기능을 확장할 수 있다

maintenance

콘솔: 로그 내용을 유지보수하기 상당히 어렵다

로거: 구조화된 로그 기록으로 분석하거나 문제 원인을 찾는 등 유지보수하기 용이하다

log file management strategies

파일에 로그 데이터를 저장하여 관리하는 방식을 사용하면 다음과 같은 사항을 고려할 필요가 있다

운영 환경에서 파일 기반 로깅을 효율적으로 관리할 수 있는 전략은 다음과 같다

로그 파일 회전, 압축 및 삭제

로테이션이란 특정 조건을 기준으로 새로운 로그 파일을 만들거나(로테이션) 기존의 로그 파일들을 압축 또는 삭제하여 파일 크기와 로그 저장소의 공간을 관리하는 방법을 말한다

일반적으로 다음과 같은 유형으로 나뉜다

아래와 같은 설정으로 스프링 부트의 기본 로깅 프레임워크인 logback을 이용한 로그 파일 관리 전략을 수립할 수 있다

로그 파일 분리

날짜별 로그를 저장하거나 INFO, ERROR 로그 레벨별로 파일을 구분하여 저장할 수 있다

날짜별 로그 저장: app-2025-03-01.log, app-2025-03-02.log

레벨별 로그 저장: info.log, error.log

logging best practice

Observability에서 Logging이 가지는 한계

Observability에서는 로그뿐만 아니라 메트릭(Metrics)과 트레이스(Tracing)이 함께 사용되어야 함. 로그는 개별 이벤트 중심이므로 전체 흐름을 파악하기 어려움 고성능 분산 시스템에서는 로그만으로 병목 현상과 지연 시간을 분석하기 어려움 대량의 로그를 저장하고 분석하는 비용이 큼

따라서 Structured Logging, Centralized Logging, Correlation ID(MDC) 적용이 필수적