logging 사용 이유
이 모듈은 응용 프로그램과 라이브러리를 위한 유연한 이벤트 로깅 시스템을 구현하는 함수와 클래스를 정의합니다.
표준 라이브러리 모듈로 로깅 API를 제공하는 것의 주요 이점은, 모든 파이썬 모듈이 로깅에 참여할 수 있어서, 응용 프로그램 로그에 여러분 자신의 메시지를 제삼자 모듈의 메시지와 통합할 수 있다는 것입니다.
출처 https://docs.python.org/ko/3/library/logging.html
print문을 코드 여기저기에 집어넣고 일일이 결과를 확인하는 경험은 누구나 있을 것이다.
개발과정, 디버깅, 실행 시 코드가 제대로 작동하는지 확인하기 위한 일이었을 텐데, 실무에 뛰어드는 개발자라면 logging을 이용해서 스마트하게 로그를 남겨보자.
모듈 작동 요소
- Logger : 어플리케이션 코드가 직접 사용할 수 있는 인터페이스를 제공함
- Handler : logger에 의해 만들어진 log 기록들을 적합한 위치로 보냄
- Filter : 어떤 log 기록들이 출력되어야 하는지를 결정함
- Formatter : log 기록들의 최종 출력본의 레이아웃을 결정함
로깅 레벨 설정
LEVEL | 어떤 상황에서 일어나는가 |
DEBUG | 간단히 문제를 진단하고 싶을 때 필요한 자세한 정보를 기록함 |
INFO | 계획대로 작동하고 있음을 알리는 확인 메시지 |
WARNING | 소프트웨어가 작동은 하고 있지만, 예상치 못한 일이 발생했거나 할 것으로 예측된다는 것을 알림(default) |
ERROR | 중대한 문제로 인해 소프트웨어가 몇몇 기능들을 수행하지 못함을 알림 |
CRITICAL | 작동이 불가능한 수준의 심각한 에러가 발생함을 알림 |
logging.setLevel(logging.DEBUG) 설정 이유
로깅의 기본 레벨은 WARNING이다.
로깅 레벨은 DEBUG < INFO < WARNING < ERROR < CRITICAL 순이므로 기본 레벨을 재설정하지 않으면 DEBUG, INFO 수준의 로그는 기록되지 않기 때문이다.
logging 사용법
맛보기
import logging
logging.error('Watch out! Error exist!')
위처럼 입력하고 실행하면 ERROR:root:Watch out! Error exist! 이 콘솔에 출력된다.
인쇄된 메시지에는 수준 표시와 로깅 호출에 제공된 이벤트의 설명(‘Watch out! Error exist!’)이 포함된다. 필요한 경우 실제 출력을 매우 유연하게 포맷 할 수 있다.
파일에 로깅하기
import logging
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
위를 실행하면 example.log라는 이름의 파일로 로그 파일이 생성된다. 내용은 다음과 같다.
DEBUG:root:debug message
INFO:root:info message
WARNING:root:warning message
ERROR:root:error message
기본 filemode='a' 이므로, example.log를 삭제하지 않는 이상 새롭게 로깅할 시 기존 파일에 추가로 수정되는 형식이다.
메세지 포맷 변경하기
basicConfig 사용하기
import logging
logging.basicConfig(format='[%(levelname)s][%(asctime)s]: %(message)s', level=logging.DEBUG)
logging.debug('debug message')
위를 실행하면 [DEBUG][2023-07-18 11:53:27,425]: debug message 가 출력된다.
Formatter 사용하기
logging.Formatter(
fmt = None, # 메시지 출력 형태. None일 경우 raw 메시지를 출력.
datefmt = None, # 날짜 출력 형태. None일 경우 '%Y-%m-%d %H:%M:%S,%f'.
style = '%' # '%', '{', '$' 중 하나. `fmt`의 style을 결정.
)
레벨 이름, 날짜/시간, 메세지내용 외에도 파일명, 코드라인번호 등 다양한 포맷을 설정 가능하다.
Handler 사용하기
Handler에 앞서 확인한 level과 Formatter를 지정하여 logger instance에 추가하는 방식
콘솔(stream)에 로그 남기기
logging.StreamHandler()
파일에 로그 쌓기
logging.FileHandler('example.log')
자정에 자동으로 날짜를 파일명으로 하는 파일 생성하며 로그 쌓기
handlers.TimedRotatingFileHandler(
filename='example.log',
when="midnight",
interval=1,
backupCount=30,
encoding="utf-8")
Handler 사용한 로깅
#1 logger instance를 만든다.
logger = logging.getLogger(name)
#2 logger의 level을 가장 낮은 수준인 DEBUG로 설정해둔다.
logger.setLevel(logging.DEBUG)
#3 formatter 지정 [시간 - logger이름 - level이름 - 메시지] 형식
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
#4 handler instance 생성
stream_handler = logging.StreamHandler()
file_handler = logging.FileHandler(filename="test.log")
#5 handler 별로 다른 level 설정
stream_handler.setLevel(logging.INFO)
file_handler.setLevel(logging.DEBUG)
#6 handler 출력 format 지정
stream_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
#7 logger에 handler 추가
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
logging.exception으로 에러 이유, 발생 지점 traceback 로깅
에러의 이유, 실행 실패 시 traceback을 로깅하고 싶다면 logging.exception을 사용하자.
traceback 모듈을 사용해도 되지만, 이미 logging을 사용하고 있다면 logging.exception이 훨씬 간편하다.
참고로 logging.exception은 ERROR 레벨로 표시된다.
traceback + logging
import logging
import traceback
logging.error(f'error! \n{traceback.format_exc()}')
logging only
import logging
logging.exception('error!')
실제처럼 logging 사용해보기
import logging
def func(a, b):
return a / b
if __name__ == "__main__":
log = logging.getLogger('example')
log.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(name)s][%(asctime)s][%(levelname)s|%(filename)s:%(lineno)s] >> %(message)s', "%Y-%m-%d %H:%M:%S")
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
log.addHandler(stream_handler)
file_handler = logging.FileHandler('./example.log', encoding='utf-8')
file_handler.setFormatter(formatter)
log.addHandler(file_handler)
while True:
x, y = map(int, input('x, y 입력(종료 시 -1 -1): ').split())
if x == -1 and y == -1:
log.warning('-1을 입력하여 종료합니다.')
break
try:
ans = func(x, y)
log.info(f'success answer: {ans}')
except:
log.error('fail')
log.exception('fail reason')
실행결과
[example][2023-07-18 13:28:09][INFO|logging_ex.py:26] >> success answer: 5.0
[example][2023-07-18 13:28:11][INFO|logging_ex.py:26] >> success answer: 2.0
[example][2023-07-18 13:28:15][ERROR|logging_ex.py:28] >> fail
[example][2023-07-18 13:28:15][ERROR|logging_ex.py:29] >> fail reason
Traceback (most recent call last):
File "e:\logging_ex.py", line 25, in <module>
ans = func(x, y)
File "e:\logging_ex.py", line 4, in func
return a / b
ZeroDivisionError: division by zero
[example][2023-07-18 13:28:16][WARNING|logging_ex.py:22] >> -1을 입력하여 종료합니다.
'Python' 카테고리의 다른 글
[python] DataFrame 결합 - pandas.concat (0) | 2023.07.28 |
---|---|
[python] ast.literal_eval VS eval 비교하기 (0) | 2023.07.26 |
[python] csv 모듈 사용하기 (0) | 2023.07.25 |
[python] APScheduler 사용하기 (1) | 2023.07.20 |
[python] pymysql VS sqlalchemy 소개 및 사용법 (0) | 2023.07.19 |