Python

[logging] Logger/Handler/Formatter

비번변경 2022. 2. 26. 00:24
Logging 모듈
Python에서 로그를 출력하기 위한 기본 라이브러리

 

이 글에서는 Logging 모듈 사용 방법을 간단하게 정리한다.

 

사용

logging 모듈을 import 한 후, 출력할 로그 메시지를 전달하여 사용할 수 있다.

import logging

logging.debug("debug message")
logging.info("info message")
logging.warning("warning message")
logging.error("error message")
logging.critical("critical message")

아무 처리도 하지 않고 로그 메세지를 출력한 결과는 아래와 같다.

 

실행 결과

logging 실행 결과

debug, info level에 해당하는 메시지는 출력하지 않았음을 확인할 수 있다.

 

Log Level

메세지의 중요도와 심각한 정도를 구분하는 기준으로, 5개의 level이 기본으로 제공된다.

Level 설명
DEBUG 문제 해결에 필요한 세부 정보
INFO 작업이 정상적으로 동작하고 있다는 확인 메세지
WARNING 예상치 못한 일 또는 발생 가능한 문제점 명시
ERROR  프로그램이 함수를 실행하지 못할 정도의 심각한 문제
CRITICAL   프로그램이 동작하지 못할 정도의 심각한 문제

Log Level의 심각도를 오름차순으로 정렬하면 DEBUG - INFO - WARNING - ERROR - CRITICAL로 정렬되며, Python은 기본적으로 WARNING 수준의 메시지부터 출력한다.

즉, WARNING, ERROR, CRITICAL에 해당하는 메세지만 출력한다.

 

Logging 구성 요소

Logging 모듈은 크게 아래와 같은 구성 요소를 가지고 있다.

  • Loggers : 애플리케이션 코드가 직접 사용할 수 있는 인터페이스 제공
  • Handlers : Loggers에 의해 생성된 LogRecord를 처리하여 적절한 위치로 전달
  • Filters : 출력되어야 하는 로그 필터링
  • Formatters : LogRecord의 출력 형태 지정

로그 이벤트는 LogRecord 형태로 Loggers부터 Handlers, Formatters 순서로 전달된다. Flow chart는 아래와 같다.

logging flow chart
https://docs.python.org/3.7/howto/logging.html#logging-flow

 

Loggers

Logger는 아래 코드와 같이 getLogger() 함수를 통해 사용할 수 있다.

import logging

logger = logging.getLogger("<NAME>")

역할

  1. 로그 생성 method 제공
  2. 로그 레벨과 Logger에 적용된 filter를 바탕으로 처리해야 할 메세지 판단
  3. 로그 메세지에 부가 정보가 더해진 LogRecord 인스턴스를 적절한 Handler로 전달

특징

  1. name을 전달하면 해당 이름에 해당하는 Logger를, name을 전달하지 않으면 모든 logger의 부모에 해당하는 Root Logger를 반환한다.
  2. Logger의 level이 정해지지 않으면 부모 Logger의 level을 사용한다.

Logger 설정 함수

  • Logger.setLevel() : 처리할 메시지의 최소 레벨 설정. (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
  • Logger.addHandler(), Logger.removeHandler() : Handler 추가 또는 제거. 하나의 Logger는 여러 개의 Handler를 가질 수 있다.
  • Logger.addFilter(), Logger.removeFilter() : Filter 추가 또는 제거

 

로그 생성 함수

  • Logger.debug()
  • Logger.info()
  • Logger.warning()
  • Logger.error()
  • Logger.critical()
  • Logger.exception() : Exception hadler 내에서만 사용 가능하며, stack trace 정보가 포함된 ERROR level의 로그 발생
  • Logger.log()

 

Handlers

로그 메세지를 출력하는 역할을 한다. StreamHandler, FileHandler, RotatingFileHander, SocketHandler 등 다양한 종류(https://docs.python.org/3.7/howto/logging.html#useful-handlers)가 존재한다.

 

Handler 설정 함수

  • Handler.setLevel() : 처리할 메시지의 최소 레벨 설정. Logger의 레벨과 다를 수 있으며, Logger에서 통과된 메시지가 Handler에서 통과되지 않을 수 있다.
  • Handler.setFormatter() : Handler가 사용할 formatter 설정
  • Handler.addFilter(), Handler.removeFilter() : filter 추가 또는 제거

 

StreamHanlder 사용 예시

import logging

logger = logging.getLogger("<NAME>")
logger.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
logger.info("INFO MESSAGE")

 

Formatter

출력되는 로그 메세지의 formatting에 관여한다.

 

생성 코드

formatter는 아래와 같이 생성할 수 있다.

import logging

logging.Formatter(
  fmt = None, # 메시지 출력 형태. None일 경우 raw 메시지를 출력.
  datefmt = None, # 날짜 출력 형태. None일 경우 '%Y-%m-%d %H:%M:%S'.
  style = '%' # '%', '{', '$' 중 하나. `fmt`의 style을 결정.
)

 

format 정의 속성

속성 format 설명
asctime %(asctime)s 인간이 읽을 수 있는 시각
created %(created)f LogRecord가 생성된 시각
filename %(filename)s pathname의 file 이름
funcName %(funcName)s logging call을 포함하는 function 이름
levelname %(levelname)s 메세지의 log level
lineno %(lineno)d logging call이 발생한 코드의 line
module %(module)s filename의 모듈 이름
message %(message)s 메세지
name %(name)s logger 이름
pathname %(pathname)s full pathname
thread %(thread)d thread ID
threadName %(threadName)s thread 이름

위의 속성을 이용해 "로그 발생 시각 [LOG_LEVEL] 메시지"의 형식의 로그 메시지를 출력하게끔 설정할 수 있다.

import logging

logging.Formatter(
  fmt="%(asctime)s [%(levelname)s] %(message)s",
  datefmt = None,
  style = '%'
)

formatter

 

활용

위에서 다뤘던 내용을 기반으로 원하는 로거를 생성하여 사용한다.

 

요구 사항

  • INFO LEVEL 이상의 로그를 출력한다.
  • 출력하는 모든 로그는 file로 저장한다. 즉, fileHandler를 사용한다.
  • 로그 발생 시각, Log Level과 그 메세지를 출력한다.

 

코드

import logging

# Logger
logger = logging.getLogger("stu")
logger.setLevel(logging.DEBUG)

# Handler
file_handler = logging.FileHandler(filename="run.log")
file_handler.setLevel(logging.INFO)

# Formatter
formatter = logging.Formatter(
    fmt="%(asctime)s [%(levelname)s] %(message)s", 
    datefmt=None, 
    style='%'
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

logger.info("info message")
logger.error("error message")

 

실행 결과

달리 파일 경로를 지정하지 않으면 프로젝트의 최상위 경로에 log 파일을 생성한다.

log 파일 생성

 

JSON 파일로 Logger 설정

다른 프로그램에서도 동일한 Logger를 사용할 수 있도록 즉, 재사용성을 위해 관련 설정은 json 또는 yaml 파일로 저장한 뒤 로드하여 사용하려고 한다.

 

설정 파일

{
    "version": 1,
    "formatters": {
        "basic": {
            "format": "%(asctime)s [%(levelname)s] %(message)s"
        }
    },
    "handlers": {
        "file_handler": {
            "class": "logging.FileHandler",
            "level": "INFO",
            "formatter": "basic",
            "filename": "run.log"
        }
    },
    "root": {
        "level": "DEBUG",
        "handlers": ["file_handler"]
    }
}

 

설정 파일 사용

import logging
import logging.config
import json

config = json.load(open('/LOGGER/CONF/PATH/logger.json'))
logging.config.dictConfig(config)

logger = logging.getLogger("<NAME>")

참고 문서

https://hwangheek.github.io/2019/python-logging/

https://greeksharifa.github.io/%ED%8C%8C%EC%9D%B4%EC%8D%AC/2019/12/13/logging/

https://snowdeer.github.io/python/2017/11/17/python-logging-example/