Logging 전략

DDANDARA ㅣ 2023. 1. 19. 16:20

Python에서는 logging 모듈을 기본적으로 제공

1-1. Log Level

  • Python의 기본 레벨 = WARNING

https://docs.python.org/3.7/howto/logging.html#when-to-use-logging

1-2. Root Logger

import logging
logging.warning('This is a warning message')
# 아래와 같은 메시지가 콘솔에 출력됩니다
# WARNING:root:This is a warning message

logging.info('This is an info message')
# 아무 것도 프린트 되지 않습니다
import logging
logging.basicConfig(level=logging.INFO)

logging.info('This is an info message')
# 아래와 같은 메시지가 출력됩니다!
# INFO:root:This is an info message
import logging
logging.basicConfig(filename='dummy.log', level=logging.INFO)
logging.debug('this is a debug')
logging.info('this is an info')
logging.warning('this is a warning')
    [dummy.log]
    INFO:root:this is an info
    WARNING:root:this is a warning
import logging
logging.basicConfig(
  format = '%(asctime)s:%(levelname)s:%(message)s',
  datefmt = '%m/%d/%Y %I:%M:%S %p',
  level = logging.DEBUG
)
logging.debug('This is a formatted debug message')

# 03/12/2019 05:20:12 PM:DEBUG:This is a formatted debug message

Custom Logging Module

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

Logging HOWTO - Python 3.9.5 documentation

 

Logger

  1. 로그를 생성할 수 있는 method를 제공해줍니다. (Logger.debug(), Logger.info(), …)
  2. 로그 레벨과 Logger에 적용된 filter를 바탕으로 처리해야 할 메시지를 판단합니다.
  3. 적절한 Handler들에게 로그 메시지에 부가정보가 더해진 LogRecord 인스턴스를 전달해줍니다.

 

logger Object = logging.getLogger(name)

 

  • name이 주어진 경우, 해당 이름에 해당하는 logger를, name이 주어지지 않은 경우 root logger를 전달받습니다.
  • name은 마침표(.)로 구분되는 계층구조를 가지고 있습니다. 예를 들어, getLogger('foo.bar')는 getLogger('foo')의 자식 logger를 반환합니다.
  • Logger의 레벨이 정해지지 않은 경우 (logger.setLevel(...)을 설정해주지 않은 경우), 자신의 부모 logger의 레벨을 사용합니다.
  • 자식 logger는 메시지를 자기 부모 logger의 handler에게 propagate합니다.

 

logging range

  • Module-wise : Python에서는 하나의 파일이 하나의 모듈입니다. 따라서, 파일의 제일 위쪽에서 모듈명(__name__)으로 Logger를 만드는 방법이 있습니다.
  • Instance-wise : __init__() method 내에서 Logger를 생성하는 방법으로, 각 인스턴스마다 고유한 Logger를 가지게 됩니다.
  • Class-wise : __class__.__qualname__에 해당하는 Logger를 생성하는 방법입니다. 클래스별로 고유한 Logger를 가지게 됩니다.
  • Function-wise : 함수 내에서 Logger를 생성할 수도 있습니다. (e.g. main())

 

Handler

Handler는 로그 메시지를 출력하는 역할

import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)

logger.info('This is an INFO message')
# This is an INFO message
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

file_handler = logging.FileHandler('dummy.log')
logger.addHandler(file_handler)

logger.info('This is an INFO message. Hello dummy!')

Formatter

Formatter는 최종적으로 출력 될 로그 메시지의 formatting에 관여

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

Example

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # 모든 레벨의 로그를 Handler들에게 전달해야 합니다.

formatter = logging.Formatter('%(asctime)s:%(module)s:%(levelname)s:%(message)s', '%Y-%m-%d %H:%M:%S')

# INFO 레벨 이상의 로그를 콘솔에 출력하는 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

# DEBUG 레벨 이상의 로그를 `debug.log`에 출력하는 Handler
file_debug_handler = logging.FileHandler('debug.log')
file_debug_handler.setLevel(logging.DEBUG)
file_debug_handler.setFormatter(formatter)
logger.addHandler(file_debug_handler)

# ERROR 레벨 이상의 로그를 `error.log`에 출력하는 Handler
file_error_handler = logging.FileHandler('error.log')
file_error_handler.setLevel(logging.ERROR)
file_error_handler.setFormatter(formatter)
logger.addHandler(file_error_handler)

ToJson

{
  "version": 1,
  "disable_existing_loggers": false,
  "formatters": {
    "basic": {
      "format": "%(asctime)s:%(module)s:%(levelname)s:%(message)s",
      "datefmt": "%Y-%m-%d %H:%M:%S"
    }
  },
  "handlers": {
    "console": {
      "class": "logging.StreamHandler",
      "level": "INFO",
      "formatter": "basic"
    },
    "file_debug": {
      "class": "logging.FileHandler",
      "level": "DEBUG",
      "formatter": "basic",
      "filename": "debug.log"
    },
    "file_error": {
      "class": "logging.FileHandler",
      "level": "ERROR",
      "formatter": "basic",
      "filename": "error.log"
    }
  },
  "loggers": {
    "__main__": {
      "level": "DEBUG",
      "handlers": ["console", "file_debug", "file_error"],
      "propagate": true
    }
  }
}
import logging
import logging.config
import json

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

logger = logging.getLogger(__name__)

Daily Log File 생성

handler = TimedRotatingFileHandler('log/flask_server.log', when="D", interval=1)
handler.suffix = "%Y-%m-%d_%H-%M-%S"