TJ_Log
[Python] 파이썬으로 로그 남기기 (Logging 모듈) 본문
python에서 프로그램을 실행하면서 로그를 활용하고 계신가요?
로깅(logging)이란 로그를 남기는 행동으로 흐름과 시나리오를 확인할 수 있는 아주 유용한 도구입니다.
로그를 이용해 프로그램이 실행되는 시간, 사용자 등에 대한 정보를 남기고 저장할 수 있으며, 에러가 발생했을 때도 기존의 stack trace 이상의 정보를 제공받을 수 있습니다. 유용한 로그 정보를 저장하고 성능과 실행과정을 분석하고 진단할 수 있습니다.
이번 글에서는 python logging모듈을 통해 프로그램 실행 중 발생하는 로그를 남기고 활용하는 방법을 알아보겠습니다.
logging 모듈
Python에서 기본적으로 제공하는 logging모듈을 통해 프로그램 실행 중 로그를 남길 수 있습니다.
import python
특정 이벤트가 발생했을 때 로그를 남기게 되는데, 이벤트의 심각성에 따라 logging level을 설정할 수 있습니다. 아래 표는 표준 levels 5개를 나타낸것입니다. https://docs.python.org/3.7/howto/logging.html#when-to-use-logging을 참고해 작성했습니다.
수준(Level) | 언제 사용하는가? |
DEBUG | 일반적으로 문제를 진단할 때만 관심이 있는 자세한 정보입니다. |
INFO | 모든 것이 예상대로 작동하는지 확인합니다. |
WARNING | 예상치 못한 일이 발생했다는 표시 또는 가까운 시일 내에 문제가 발생할 것임을 나타냅니다(예: '디스크 공간 부족'). 소프트웨어는 여전히 예상대로 작동합니다. |
ERROR | 더 심각한 문제로 인해 소프트웨어가 일부 기능을 수행할 수 없습니다. |
CRITICAL | 프로그램 자체를 계속 실행할 수 없음을 나타내는 심각한 오류입니다. |
import logging
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
# log file에 저장된 log
# ...
# WARNING:root:This is a warning message
# ERROR:root:This is an error message
# CRITICAL:root:This is a critical message
위와 같이 logging으로 생성된 메세지는 각 logging level에 대한 정보도 함께 포함하고 있습니다.
기본 설정값
logging의 설정값을 정의하는데 basicConfig()메소드를 사용할 수 있습니다.
basicConfig() 에 사용할 수 있는 parameter는 다음과 같습니다.
- level: logger에서 공개하고 싶은 level을 설정할 수 있습니다. 예를 들어 warning으로 하면 info등급의 로깅은 출력되지않습니다.
- filename: 로그를 적을 파일이름을 명시할 수 있습니다.
- filemode: 파일이름이 주어졌을 때 파일모드를 정할 수 있습니다. default값은 a(append) 입니다.
- format: log message의 포맷을 결정합니다.
import logging
logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
logging.warning('This will get logged to a file')
# app.log 파일에 'root - ERROR - This will get logged to a file' 라고 출력된것을 확인할 수 있습니다.
출력 결과 Formatting
로그를 작성할 때 사용할 수 있는 몇가지 기본 요소들이 있습니다. 그 요소들을 이용해 logging 포멧을 설정할 수 있습니다.
- format에서 사용할 수 있는 설정 속성(attributes) 리스트 매뉴얼
(https://docs.python.org/3/library/logging.html#logrecord-attributes) - process: process id
- levelname: severity level
- message: 로깅 메세지
- asctime: 24시간 기준으로 시간 출력
import logging
logging.basicConfig(format='%(asctime)s.%(msecs)03d - %(levelname)s - %(process)d- %(message)s', datefmt='%d-%b-%y %H:%M:%S')
logging.warning('Admin logged out')
## 31-Dec-22 20:12:06.288 - WARNING - 18472 - Admin logged in
Stack Traces 캡쳐
로깅 모듈은 모든 프로그램 내에서 생성되는 stack traces를 캡쳐하는 기능을 제공합니다. 예를 들어 예외와 관련된 정보는 exc_info=True를 이용하여 출력할 수 있습니다.
import logging
a = 5
b = 0
try:
c = a / b
except Exception as e:
logging.error("Exception occurred", exc_info=True)
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
ERROR:root:Exception occurred
Traceback (most recent call last):
File "exceptions.py", line 6, in <module>
c = a / b
ZeroDivisionError: division by zero
[Finished in 0.2s]
exc_info=True외에 logging.exception() 메소드를 이용해 동일한 기능을 수행할 수 있습니다.
logging.exception() 메소드는 EROOR level의 로그와 stack traces를 동시에 출력합니다.
import logging
a = 5
b = 0
try:
c = a / b
except Exception as e:
logging.exception("Exception occurred")
위 코드의 실행결과는 다음과 같습니다.
ERROR:root:Exception occurred
Traceback (most recent call last):
File "exceptions.py", line 6, in <module>
c = a / b
ZeroDivisionError: division by zero
[Finished in 0.2s]
logging 모듈의 클래스와 함수들
지금까지 logging.debug()와 같이 함수가 직접 호출될 때 마다 로깅 모듈에서 사용하는 root라는 기본 로거를 이용했습니다. 프로그램에 서로 다른 이름의 로거를 사용해야 되는 순간에 어떻게 해야하는지 logging모듈의 클래스와 함수를 살펴보겠습니다.
- Logger:함수를 호출하기 위해 직접적으로 사용되는 클래스입니다.
- LogRecord: logging 작업에 관련된 모든 정보를 담고 있는 클래스입니다.
- Handler: Handler는 LogRecord를 콘솔이나 파일과 같은 필요한 출력 대상으로 보냅니다.
- Formatter: Formatter를 이용해 출력 형식을 지정합니다.
import logging
logger = logging.getLogger('example_logger')
# example_logger 이름의 Logger 객체를 반환합니다
logger.warning('This is a warning')
Handler 사용하기
Handler는 로거를 구성하고 로그가 생성될 때 여러 위치로 로그를 보내려는 경우에 사용됩니다.
Handler는 로그 메시지를 표준 출력 스트림, 파일, HTTP를 이용해 다른 서버 혹은 SMTP를 통해 이메일로 보냅니다.
생성한 로거에는 하나 이상의 Handler가 있을 수 있습니다. 즉, 로그 파일에 저장하고 이메일을 통해 보낼수도 있습니다.
로거와 마찬가지로 Hnadler에서 심각도 수준을 설정할 수도 있습니다. 이는 동일한 로거에 대해 여러 처리기를 설정하지만 각각에 대해 다른 심각도 수준을 원하는 경우에 유용합니다.
예를 들어, 레벨이 WARNING이상의 로그를 콘솔에 기록하기를 원할 수 있지만 레벨이 ERROR이상인 모든 로그도 파일에 저장해야 합니다. 다음은 이를 수행하는 프로그램입니다.
import logging
# 새로운 커스텀 로거 생성
logger = logging.getLogger(__name__)
# handler 생성
c_handler = logging.StreamHandler()
f_handler = logging.FileHandler('file.log')
c_handler.setLevel(logging.WARNING)
f_handler.setLevel(logging.ERROR)
# formatter 를 생성하여 handler 에 부착
c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
c_handler.setFormatter(c_format)
f_handler.setFormatter(f_format)
# handler 를 로거에 부착
logger.addHandler(c_handler)
logger.addHandler(f_handler
logger.warning('This is a warning')
logger.error('This is an error')
Other Configuration
fileConfig() 혹은 dictConfig()를 사용하여 로그 속성을 정의할 수 있습니다.
아래 두가지 예제를 통해 알아보겠습니다.
1) fileConfig()
- 디렉토리에 file.conf라는 이름으로 파일을 생성하고 다음과 같은 설정 정보를 입력했습니다.
[loggers]
keys=root,sampleLogger
[handlers]
keys=consoleHandler
[formatters]
keys=sampleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_sampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=sampleLogger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=sampleFormatter
args=(sys.stdout,)
[formatter_sampleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
- fileConfig()를 이용한 logger 구성 설정
import logging
import logging.config
logging.config.fileConfig(fname='file.conf', disable_existing_loggers=False)
logger = logging.getLogger(__name__)
logger.debug('This is a debug message')
# 2018-07-13 13:57:45,467 - __main__ - DEBUG - This is a debug message
2) dictConfig()
config.yaml파일을 생성하고 다음 값들을 입력합니다.
yaml format에 대한 내용은 다음 링크를 참고해주세요 (https://realpython.com/python-yaml/)
version: 1
formatters:
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
loggers:
sampleLogger:
level: DEBUG
handlers: [console]
propagate: no
root:
level: DEBUG
handlers: [console]
- 생성한 yaml파일을 읽어 dict 객체를 생성하고 이를 이용해 logger를 설정할 수 있습니다.
import logging
import logging.config
import yaml
with open('config.yaml', 'r') as f:
config = yaml.safe_load(f.read())
logging.config.dictConfig(config)
logger = logging.getLogger(__name__)
logger.debug('This is a debug message')
'STUDY > Python' 카테고리의 다른 글
[Python] 정규식 (문자열 처리) - 자주 사용하는 정규식 (0) | 2022.05.29 |
---|