Python 로그: 깊이 있는 튜토리얼
Python 표준 라이브러리에는 logging 모듈이 첨부되어 있으며, 이 모듈은 대부분의 기본 로그 기능을 제공합니다.정확한 설정을 통해 로그 메시지는 언제 로그를 터치하는지, 실행 중인 프로세스/루틴 같은 로그 상하문을 포함하여 많은 유용한 정보를 가져올 수 있습니다.
비록 이러한 장점이 있지만, 로그 모듈은 정확하게 설정할 수 있는 시간이 좀 걸리기 때문에 종종 무시된다.공식 문건 https://docs.python.org/3/library/logging.html은 완전하지만 최선의 실천을 보여주거나 일지 서프라이즈를 강조하지 않았다.
이 Python 로그 강좌는 로그 모듈에 대한 완전한 문서가 아니라 로그 개념과 주의해야 할 문제점을 소개하는 입문 안내서입니다.이 글은 최선의 실천으로 끝날 것이다.
이 문서의 모든 코드 세그먼트는 로그 모듈을 가져온 것으로 가정합니다.
import logging
Python 로그의 개념
이 절은 로그 모듈에서 자주 만나는 개념들을 개술하였다.
수평
로그 수준은 주어진 로그에 대응하는 중요성:
error
로그는 warn
로그보다 더 긴급해야 하고, debug
로그는 디버깅 프로그램에서만 사용해야 한다.Python에는 6개의 로그 레벨이 있습니다.각 레벨은 하나의 정수와 연결되며, 이 정수는 로그의 심각성을 나타냅니다.
NOTSET=0, DEBUG=10, INFO=20, WARN=30, ERROR=40, CRITICAL=50.
포맷 프로그램
로그
formatter
은 상하문 정보를 추가하여 로그 메시지를 풍부하게 합니다.로그의 발송 시간, 위치 (Python 파일, 줄 번호, 방법 등) 및 추가 상하문 (예를 들어 루트와 프로세스 id) 을 이해하는 데 매우 유용합니다. (다중 루트 프로그램을 디버깅할 때 매우 유용합니다.)예를 들어 이 로그 포맷 프로그램을 통해 로그
hello world
을 보낼 때:"%(asctime)s — %(name)s — %(levelname)s — %(funcName)s:%(lineno)d — %(message)s"
그것은2018-02-07 19:47:41,864 - a.b.c - WARNING - <module>:1 - hello world
처리 프로그램
로그
handler
은 로그를 올바르게 쓰기/표시하는 구성 요소입니다. StreamHandler
을 통해 컨트롤러에 로그를 표시하고, FileHandler
을 통해 파일을 작성하고, SMTPHandler
을 통해 이메일을 보낼 수 있습니다.각 로그 프로세서에는 다음과 같은 두 개의 중요한 필드가 있습니다.
StreamHandler
및 FileHandler
:console_handler = logging.StreamHandler()
file_handler = logging.FileHandler("filename")
레코더
기록기는 우리가 그것과 가장 많은 상호작용을 할 대상이자 가장 복잡한 개념이다.다음 방법으로 새 레코더를 얻을 수 있습니다.
toto_logger = logging.getLogger("toto")
레코더에는 다음과 같은 세 가지 주요 필드가 있습니다.Propagate
: 로그를 기록기의 부급으로 전파해야 하는지 여부를 결정합니다.기본값은 True
입니다.Level
: 프로세서 레벨과 마찬가지로 기록기 레벨은 중요하지 않은 로그를 필터하는 데 사용됩니다.이외에 로그 처리 프로그램과 달리 하위 기록기에서만 단계를 검사합니다.로그가 상위 레벨로 전파되면 레벨을 확인하지 않습니다.이것은 일종의 비직각적인 행위다.Handlers
: 로그가 기록기에 도착했을 때 보내는 프로세서 목록입니다.이것은 유연한 로그 처리를 허용합니다. 예를 들어 모든 디버그 로그를 기록하는 파일 로그 처리 프로그램과 관건적인 로그에만 사용되는 전자 우편 로그 처리 프로그램이 있습니다.이 방면에서 기록기-처리 프로그램 관계는 발표자-소비자 관계와 유사하다. 로그는 기록기 등급 검사를 통해 모든 처리 프로그램에 방송된다.레코더의 이름은 고유합니다. 이것은
toto
이라는 레코더가 생성된 경우 logging.getLogger("toto")
의 후속 호출이 같은 객체로 되돌아온다는 것을 의미합니다.assert id(logging.getLogger("toto")) == id(logging.getLogger("toto"))
이미 짐작했듯이 벌목꾼은 차원 구조를 가지고 있다.차원 구조의 맨 위에는 root
기록기가 있는데 logging.root
을 통해 이 기록기에 접근할 수 있다.logging
모듈(예를 들어 logging.debug()
)에서 직접 방법을 호출할 때 이 기록기를 호출합니다.기본적으로 루트 로그 레벨은 WARN
이므로 각 레벨이 낮은 로그 (예: logging.info("info")
) 는 무시됩니다.루트 레코더의 또 다른 특수성은 기본 프로세서가 WARN보다 높은 로그를 처음 기록할 때 만들어진다는 것이다.일반적으로
logging.debug()
등의 방법을 통해 루트 기록기를 직접 또는 간접적으로 사용하는 것을 권장하지 않는다.기본적으로 새 레코더를 만들 때 부모 레코더는 루트 레코더로 설정됩니다.
lab = logging.getLogger("a.b")
assert lab.parent == logging.root # lab's parent is indeed the root logger
그러나 기록기는 점 기호를 사용한다. 이것은 a.b
이라는 기록기가 기록기 a
의 자급이 된다는 것을 의미한다.그러나 레코더 a
이 생성되었을 때만 이것이 사실입니다. 그렇지 않으면 ab
의 상위 항목은 여전히 root
레코더입니다.la = logging.getLogger("a")
assert lab.parent == la # lab's parent is now la instead of root
기록기가 레벨 검사에 따라 로그를 통과해야 하는지 여부를 결정할 때 (예를 들어 로그 레벨이 기록기 레벨보다 낮으면 로그를 무시합니다) 실제 레벨이 아닌 logger.level
을 사용합니다.유효 등급이 NOTSET
, 즉 디버깅에서 임계까지의 모든 값이 아니라면 유효 등급은 기록기 등급과 같다.그러나 레코더 레벨이 NOTSET
이면 유효 레벨은 NOTSET 레벨이 아닌 최초의 조상 레벨이 됩니다.기본적으로 새 레코더의 레벨은
NOTSET
입니다. 루트 레코더의 레벨은 WARN
이기 때문에 레코더의 유효 레벨은 WARN입니다.따라서 새 레코더에 일부 프로세서가 추가되더라도 로그 레벨이 WARN을 초과하지 않는 한 이러한 프로세서는 호출되지 않습니다.toto_logger = logging.getLogger("toto")
assert toto_logger.level == logging.NOTSET # new logger has NOTSET level
assert toto_logger.getEffectiveLevel() == logging.WARN # and its effective level is the root logger level, i.e. WARN
# attach a console handler to toto_logger
console_handler = logging.StreamHandler()
toto_logger.addHandler(console_handler)
toto_logger.debug("debug") # nothing is displayed as the log level DEBUG is smaller than toto effective level
toto_logger.setLevel(logging.DEBUG)
toto_logger.debug("debug message") # now you should see "debug message" on screen
기본적으로 logger
레벨은 로그의 통과 여부를 결정하는 데 사용됩니다. 로그 레벨이 레코더 레벨보다 낮으면 로그를 무시합니다.Python 로깅 모범 사례
로그 모듈은 정말 편리하지만, 가장 우수한 파이썬 개발자라도 오랫동안 골치 아플 수 있는 괴벽이 포함되어 있다.다음은 이 모듈을 사용하는 가장 좋은 방법입니다.
logging.info()
같은 함수를 호출하지 마십시오. 이 함수는 백엔드에서 루트 기록기를 호출합니다.사용 중인 라이브러리에서 오류 메시지를 캡처하려면 루트 레코더를 설정하는 것이 유용합니다.logging.getLogger(logger name)
으로 새 레코더를 만드십시오.나는 보통 __name__
을 단일 모듈의 레코더 이름이나 전체 응용 프로그램/라이브러리의 정적 이름으로 사용하지만, 일치하기만 하면 모든 이름을 사용할 수 있다.더 많은 처리 프로그램을 추가하기 위해서, 나는 보통 기록기를 되돌려 주는 방법이 있다. (https://gist.github.com/nguyenkims/e92df0f8bd49973f0c94bddf36ed7fd0에서 요점을 찾을 수 있습니다.)import logging
import sys
from logging.handlers import TimedRotatingFileHandler
FORMATTER = logging.Formatter("%(asctime)s — %(name)s — %(levelname)s — %(message)s")
LOG_FILE = "my_app.log"
def get_console_handler():
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(FORMATTER)
return console_handler
def get_file_handler():
file_handler = TimedRotatingFileHandler(LOG_FILE, when='midnight')
file_handler.setFormatter(FORMATTER)
return file_handler
def get_logger(logger_name):
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG) # better to have too much log than not enough
logger.addHandler(get_console_handler())
logger.addHandler(get_file_handler())
# with this pattern, it's rarely necessary to propagate the error up to parent
logger.propagate = False
return logger
새 레코더를 만들고 사용한 후:my_logger = get_logger("my module name")
my_logger.debug("a debug message")
RotatingFileHandler
클래스를 사용합니다. 예를 들어 예시에서 사용한 TimedRotatingFileHandler
이 아니라 FileHandler
을 사용합니다. 파일이 크기 제한에 도달하면 자동으로 파일을 회전하거나 매일 회전하기 때문입니다.Reference
이 문제에 관하여(Python 로그: 깊이 있는 튜토리얼), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/simplelogin/python-logging-an-in-depth-tutorial-f15텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)