logging 라이브러리의 4대 핵심 컴포넌트

알겠습니다. 파이썬의 표준 import logging 라이브러리의 핵심 구조와 동작 원리를 개발자의 관점에서 설명해 드리겠습니다.

logging 라이브러리는 “무엇을(What), 어디에(Where), 어떻게(How), 언제(When) 기록할 것인가”를 제어할 수 있도록 모듈화된 강력한 시스템입니다. 이 시스템은 크게 4가지 주요 컴포넌트로 구성됩니다.

logging 라이브러리의 4대 핵심 컴포넌트

Logging Flow
(이미지 출처: 파이썬 공식 문서)

1. 로거 (Loggers)

  • 역할: 애플리케이션 코드에서 로그 메시지를 생성하는 진입점입니다. 개발자가 직접 상호작용하는 주체입니다.
  • 생성: log = logging.getLogger(__name__)
  • 기능:
    • 메소드 제공: log.debug(), log.info(), log.warning(), log.error(), log.critical()과 같은 메소드를 제공하여 개발자가 메시지를 기록할 수 있게 합니다.
    • 심각도(Level) 기반 필터링: 각 로거는 자신만의 레벨(예: log.setLevel(logging.DEBUG))을 가질 수 있습니다. 생성된 메시지의 레벨이 로거의 레벨보다 낮으면, 메시지는 다음 단계(핸들러)로 전달되지 않고 즉시 무시됩니다.
    • 메시지 전파: 로거는 계층 구조를 가집니다. getLogger('app.ui')getLogger('app')의 자식입니다. 메시지는 자신의 핸들러에서 처리된 후, 부모 로거의 핸들러로 전파(propagate)될 수 있습니다. (기본값: True)

2. 핸들러 (Handlers)

  • 역할: 로거로부터 전달받은 로그 메시지(정확히는 LogRecord 객체)를 실제 목적지로 보내는(dispatch) 역할을 합니다. “어디에(Where)” 기록할지를 결정합니다.
  • 종류:
    • StreamHandler: 콘솔(터미널)과 같은 스트림(sys.stdout, sys.stderr)으로 출력합니다. SF Downloader에서 사용하는 핵심 핸들러입니다. (정확히는 이것을 대체하는 ForwardingHandler를 사용)
    • FileHandler: 파일에 로그를 기록합니다.
    • RotatingFileHandler: 파일 크기가 일정 이상 커지면 새 파일에 로깅을 시작합니다. (예: app.log, app.log.1, app.log.2)
    • TimedRotatingFileHandler: 특정 시간 간격(매일, 매시간 등)으로 로그 파일을 교체합니다.
    • SMTPHandler: 이메일로 로그를 보냅니다. (주로 심각한 오류 발생 시)
    • … 등 다양한 종류가 있습니다.
  • 연결: logger.addHandler(my_handler) 코드를 통해 하나의 로거에 여러 개의 핸들러를 붙일 수 있습니다. 예를 들어, 하나의 로그를 콘솔과 파일 양쪽에 동시에 기록할 수 있습니다.

3. 포매터 (Formatters)

  • 역할: 로그 메시지의 **최종 출력 형식(레이아웃)**을 결정합니다. “어떻게(How)” 기록할지를 담당합니다.
  • 생성: formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  • 기능:
    • LogRecord 객체에 담긴 다양한 정보(시간, 로거 이름, 레벨, 메시지 등)를 지정된 형식의 문자열로 변환합니다.
    • %(asctime)s, %(levelname)s 등 다양한 속성을 조합하여 원하는 로그 포맷을 만들 수 있습니다.
  • 연결: handler.setFormatter(formatter) 코드를 통해 각 핸들러에 포매터를 설정합니다.

4. 필터 (Filters)

  • 역할: 로거의 레벨 필터링보다 더 세밀한 조건으로 로그를 전달할지 여부를 결정합니다. “언제(When)” 또는 “어떤 조건에서” 기록할지를 제어합니다.
  • 기능:
    • filter(record) 메소드를 구현한 객체입니다. 이 메소드가 True를 반환하면 로그가 처리되고, False를 반환하면 무시됩니다.
    • LogRecord 객체의 모든 속성(로거 이름, 레벨, 메시지 내용 등)을 검사하여 복잡한 필터링 규칙을 만들 수 있습니다.
  • 연결: logger.addFilter(my_filter) 또는 handler.addFilter(my_filter) 코드를 통해 로거나 핸들러에 필터를 추가할 수 있습니다.
    • SF Downloader에서는 LevelFilter라는 커스텀 필터를 만들어, config.ini에서 지정한 레벨의 로그만 통과시키도록 핸들러에 연결했습니다.

SF Downloader에서의 동작 흐름

  1. log = get_logger(__name__) (로거): 개발자는 코드에서 log.info("메시지", context="main")을 호출합니다. logging 시스템은 이 정보로 LogRecord 객체를 생성합니다.
  2. LevelFilter (필터): SF_Downloader 로거에 연결된 ForwardingHandler는 먼저 LevelFilter에게 LogRecord를 검사받습니다. config.iniINFO가 활성화되어 있으면 필터를 통과합니다.
  3. ForwardingHandler (핸들러): 필터를 통과한 LogRecord 객체를 받아서, 포매팅 없이 그대로 자신의 stream(sys.stdout, 즉 Stream 객체)으로 전달합니다.
  4. Stream.write(record): Stream 객체는 LogRecord를 받습니다.
  5. ContextFormatter (포매터): Stream 객체 내부의 format() 메소드가 ContextFormatter를 사용하여 LogRecord를 최종 출력 문자열로 변환합니다. (예: “2023… – INFO – … – [main] 메시지”)
  6. 최종 출력: 변환된 문자열은 original_stream(터미널)으로, LogRecord의 정보는 딕셔너리로 변환되어 시그널을 통해 GUI로 전송됩니다.

이처럼 logging 라이브러리는 각 컴포넌트의 역할을 명확히 분리하고, 이들을 조합하여 매우 유연하고 강력한 로깅 시스템을 구축할 수 있도록 설계되어 있습니다.

답글 남기기