실무를 접할 때 새로웠던 것 중 하나는 로깅이었다. 공부를 하면서 프로젝트를 진행할 때는 기껏해봐야 print
를 찍어가면서 내 코드가 어느정도 실행이 되고, 또 어디서 에러가 나는지 파악을 했었는데, 실무에서는 이러한 작업을 로깅
으로 진행하고 있었다.
로깅은 어떤 소프트웨어가 실행될 때 발생하는 이벤트를 추적하는 수단입니다. 소프트웨어 개발자는 코드에 로깅 호출을 추가하여 특정 이벤트가 발생했음을 나타냅니다. 이벤트는 선택적으로 가변 데이터 (즉, 이벤트 발생마다 잠재적으로 다른 데이터)를 포함할 수 있는 설명 메시지로 기술됩니다. 이벤트는 또한 개발자가 이벤트에 부여한 중요도를 가지고 있습니다 - python 3.8.3 document
위의 로깅에 대한 설명을 보면 내가 print
로 해결하려고 했던 일과 모두 겹치는 것을 알 수 있다.
파이썬은 사랑스럽게도 logging
모듈을 기본으로 제공하고 있어서 쉽게 사용할 수 있다.
먼저 로깅을 사용하기 위해 인스턴스를 생성합니다. getLogger
의 인자로는 만들고 싶은 로거 이름을 적으면 되는데, 적지 않으면 root
로 생성됩니다.
import logging
def __get_logger():
"""로거 인스턴스 반환
"""
__logger = logging.getLogger('logger')
그리고 로깅 레벨을 설정해야합니다. 여기서 레벨은 로깅을 출력해 줄 역치를 설정한다고 보면 될 것 같습니다. 특정 레벨을 설정하면, 그 레벨 이상의 로깅만 출력하게 됩니다.
따로 설정하지 않으면 기본 레벨은 WARNING
으로 지정되어 있습니다. 이때 INFO
수준으로 로깅 코드를 작성하면 콘솔에 출력되지 않습니다.
# 예시
import logging
logging.warning('Watch out!')
logging.info('I told you so')
# 콘솔 출력
WARNING:root:Watch out!
로그 레벨은 아래와 같이 5가지가 있습니다.
level | 사용할 때 |
---|---|
DEBUG | 상세한 정보. 보통 문제를 진단할 때만 필요합니다. |
INFO | 예상대로 작동하는지에 대한 확인. |
WARNING | 예상치 못한 일이 발생했거나 가까운 미래에 발생할 문제(예를 들어 〈디스크 공간 부족〉)에 대한 표시. 소프트웨어는 여전히 예상대로 작동합니다. |
ERROR | 더욱 심각한 문제로 인해, 소프트웨어가 일부 기능을 수행하지 못했습니다. |
CRITICAL | 심각한 에러. 프로그램 자체가 계속 실행되지 않을 수 있음을 나타냅니다. |
출처 - python 3.8.3 document
로그 레벨은 다음과 같이 정의할 수 있습니다. <로거 인스턴스>.setLevel(logging.<로그 레벨>)
# 로그 레벨 정의
__logger.setLevel(logging.DEBUG)
로깅 메시지도 포매팅 설정을 통해 커스터마이징 할 수 있습니다. 파이썬의 문자열 포매팅처럼 말이죠.
# 로그 포맷 정의
formatter = logging.Formatter(
'LOG##LOGSAMPLE##%(levelname)s##%(asctime)s##%(message)s >> @@file::%(filename)s@@line::%(lineno)s')
# 로그 출력 예시
> LOG##LOGSAMPLE##INFO##2020-06-12 14:40:32, 790##startProgram >> @@file::sample.py@@line::250
사용할 수 있는 어트리뷰트는 아래와 같습니다.
어트리뷰트 이름 |
포맷 |
설명 |
---|---|---|
args |
직접 포맷할 필요는 없습니다. |
|
asctime |
|
사람이 읽을 수 있는, |
created |
|
|
exc_info |
직접 포맷할 필요는 없습니다. |
예외 튜플 ( |
filename |
|
|
funcName |
|
로깅 호출을 포함하는 함수의 이름. |
levelname |
|
메시지의 텍스트 로깅 수준 ( |
levelno |
|
메시지의 숫자 로깅 수준 ( |
lineno |
|
로깅 호출이 일어난 소스 행 번호 (사용 가능한 경우). |
message |
|
로그 된 메시지. |
module |
|
모듈 ( |
msecs |
|
|
msg |
직접 포맷할 필요는 없습니다. |
원래 로깅 호출에서 전달된 포맷 문자열. |
name |
|
로깅 호출에 사용된 로거의 이름. |
pathname |
|
로깅 호출이 일어난 소스 파일의 전체 경로명 (사용 가능한 경우). |
process |
|
프로세스 ID (사용 가능한 경우). |
processName |
|
프로세스 이름 (사용 가능한 경우). |
relativeCreated |
|
logging 모듈이 로드된 시간을 기준으로 LogRecord가 생성된 시간 (밀리 초). |
stack_info |
직접 포맷할 필요는 없습니다. |
현재 스레드의 스택 바닥에서 이 레코드를 생성한 로깅 호출의 스택 프레임까지의 스택 프레임 정보 (사용 가능한 경우). |
thread |
|
스레드 ID (사용 가능한 경우). |
threadName |
|
스레드 이름 (사용 가능한 경우). |
출처 - python 3.8.3 document
핸들러(처리기, handler)는 로그 메시지를 지정된 대상으로 전달하는 역할을 합니다. Logger 객체는 addHandler
메서드를 사용해 0개 이상의 핸들러를 자신에게 추가할 수 있습니다. 주로 사용하는 핸들러는 StreamHandler
와 FileHandler
가 있습니다. 이 외 핸들러에 대해서는 python 3.8.3 document - 유용한 처리기 부분을 확인해 보세요!
핸들러를 적용하는 방법은 아래와 같습니다. 사용할 핸들러를 정의한 후, 3번에서 작성한 포매터를 핸들러에 적용한 다음 addHandler
메서드를 통해 로거에 핸들러를 넣어줍니다.
# 스트림 핸들러 정의
stream_handler = logging.StreamHandler()
# 각 핸들러에 포멧 지정
stream_handler.setFormatter(formatter)
# 로거 인스턴스에 핸들러 삽입
__logger.addHandler(stream_handler)
위에서 만들어 본 로거 인스턴스를 정의하는 함수는 아래와 같습니다.
import logging
def __get_logger():
"""로거 인스턴스 반환
"""
__logger = logging.getLogger('logger')
# 로그 포멧 정의
formatter = logging.Formatter(
'BATCH##AWSBATCH##%(levelname)s##%(asctime)s##%(message)s >> @@file::%(filename)s@@line::%(lineno)s')
# 스트림 핸들러 정의
stream_handler = logging.StreamHandler()
# 각 핸들러에 포멧 지정
stream_handler.setFormatter(formatter)
# 로거 인스턴스에 핸들러 삽입
__logger.addHandler(stream_handler)
# 로그 레벨 정의
__logger.setLevel(logging.DEBUG)
return __logger
이제 이 함수를 이용해서 로거 인스턴스를 만든 후, 로거를 작성하고 싶은 위치에 메시지를 정의해주면 됩니다.
# 로거 정의
logger = __get_logger()
# 원하는 위치에 로그 메시지 작성 - info level로 아래의 메시지를 출력하겠다.
logger.info("[BATCH EXCHANGE WAIT] %s @@RunTime[%.02f] @@SendCount[%d]" % (
__BATCH_MODE, running_time, send_count))
처음 찾아볼때는 이게 뭔가 싶었는데, 직관적으로 잘 사용할 수 있게 모듈이 만들어져 있어서 금방 이해할 수 있었습니다. 이제 print 말고 logger를 써봅시다!
오호 잘보고 갑니다~