로그

../_images/35254379756_c9fe23f843_k_d.jpg

logging 모듈은 2.3 버전 이래로 파이썬의 표준 라이브러리였습니다. 이에 관해서는 PEP 282 을 보시면 간략히 써져 있습니다. 그 문서는 읽기 어렵기로 악명이 높지만, 그래도 basic logging tutorial 은 그나마 낫습니다.

대안으로, loguru 는 단순한 print 구문을 사용하는 것만큼이나 간단한 로깅 방법을 제공합니다.

로그를 남기는데에는 2가지 목적이 있습니다:

  • 진단용 로그 는 어플리케이션의 동작과 관련된 이벤트를 기록합니다. 예를 들어 사용자가 오류 보고서를 남기면, 그 로그를 해당 에러와 관련된 상황을 확인하는데 쓸 수 있습니다.

  • 감사용 로그 는 비지니스 분석에 필요한 이벤트를 기록합니다. 사용자가 무슨 동작을 했는지 알아낼 수 있으며, 다른 사용자와는 무슨 동작을 했는지도 상세하게 알 수 있습니다. 이를 통해 보고서를 작성하거나 업무적으로 최적화를 할 수 있습니다.

… Print는?

print 가 logging보다 좋은 경우는 커맨드라인 어플리케이션에서 help 구문을 화면에 보여줄 때 뿐입니다. logging이 print 보다 좋은 이유입니다:

  • log record 는 로그 남기는 이벤트가 발생할 때마다 만들어지는데, 여기에는 로그 남기는 이벤트의 파일명과 경로, 함수, 몇 행에서 문제가 발생했는지 등의 정보가 들어있어 문제를 확인하기에 편리합니다.

  • 내장된 모듈에서 발생한 이벤트들도 로그가 남는데, 이 로그들은 루트 로그 기록기를 통하여 어플리케이션의 로그 스트림으로 보낼 수 있습니다. 필터링해서 걸러내지만 않는다면 말입니다.

  • logging.Logger.setLevel() 메소드를 쓰면 로그를 선택적으로 남길 수 있습니다. logging.Logger.disabled 속성을 True 로 설정하면 로그를 끌 수도 있습니다.

라이브러리에서의 로그 남기기

라이브러리에 로그 설정 하려면 로그 남기기 튜토리얼 을 보시면 됩니다. 로그를 남기는 이벤트가 발생하면 그게 무슨 일인지 알아내야 하는 건 라이브러리가 아니라 사용자 입니다. 따라서 반복적으로 경보를 보내야합니다.

참고

당신의 라이브러리에 NullHandler 이외의 다른 로그 남기는 핸들러를 넣지 말 것을 강력 추천합니다.

라이브러리에서 로그 기록기를 인스턴스화 하는 유일한 방법은 __name__ 전역 변수를 사용해서 만드는 방법 뿐입니다. logging 모듈은 . 을 사용해서 로그 기록기의 계층 구조를 만들기 때문에 __name__ 을 사용해야 충돌을 막을 수 있습니다.

requests의 소스 에서 가져온 훌륭한 예시를 보여드리겠습니다. 이걸 당신의 __init__.py 에 두시면 됩니다.

import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())

어플리케이션에서의 로그 남기기

멋진 앱을 만들기 위한 12가지 는 어플리케이션 개발을 위한 좋은 습관을 들이기에 참고할만 합니다. 로그를 남기는 방법 에 관한 섹션도 있는데, 여기서는 로그 이벤트를 이벤트 스트림으로 취급하여 어플리케이션 환경이 처리할 수 있도록 표준 출력으로 보낼 것을 강력하게 추천합니다.

로그를 설정하는데에는 적어도 3가지 방식이 있습니다:

  • INI 포맷의 파일을 사용하는 방법:
    • 장점: logging.config.listen() 함수로 소켓을 리스닝하여 어플리케이션 실행 중에도 설정을 갱신할 수 있습니다.

    • 단점: 코드로 로거를 설정할 때보다 제어할 수 있는 범위가 적습니다(예: 직접 서브클래스로 만든 필터나 로거).

  • 딕셔너리나 JSON 포맷 파일을 사용하는 방법:
    • 이렇게 하자: 어플리케이션 실행 중에도 업데이트를 할 수 있을 뿐만 아니라, 파이썬 2.6부터는 표준 라이브러리에서 json 모듈을 사용하여 파일에서 설정을 불러올 수도 있다.

    • 이렇게 하지 마세요: 코드에 로그를 설정할 때 할 수 있는 설정도 하지 않는다

  • 코드를 사용하는 방법:
    • 이렇게 하자: 모든 설정을 완벽하게 한다.

    • 단점: 수정하려면 소스코드를 변경해야 합니다.

INI 파일로 설정하는 예시

파일명은 logging_config.ini 로 합시다. 파일 포맷에 대한 자세한 설명은 로그 튜토리얼로그 설정 을 보시면 됩니다.

[loggers]
keys=root

[handlers]
keys=stream_handler

[formatters]
keys=formatter

[logger_root]
level=DEBUG
handlers=stream_handler

[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)

[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s

그런 다음 코드에 logging.config.fileConfig() 를 쓰세요.

import logging
from logging.config import fileConfig

fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

딕셔너리로 설정하는 예시

파이썬 2.7부터는 딕셔너리를 사용해서 상세한 설정을 할 수 있습니다. PEP 391 을 보시면 설정 딕셔너리에 반드시 넣어야 하는 요소와 그렇지 않은 요소를 확인할 수 있습니다.

import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)

dictConfig(logging_config)

logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

코드에 바로 설정하는 예시

import logging

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
        '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

logger.debug('often makes a very good meal of %s', 'visiting tourists')