본문 바로가기
MLOps/Template

[pytorch] Template - logger 구현 [3편]

by Finn# 2023. 7. 10.
728x90

Motivation

 이전 게시글에서 json - 인코딩/ 디코딩과 관련한 함수를 정의하고 이해해보는 시간을 가졌다.

기본적으로 학습에 필요한 파라미터들을 configuration 파일을 만들어서 쉽게 수정할 때도 json을 사용할 수 있지만 위 Template에서는 log 데이터에 대한 form을 저장하고 적용하는 용도로 logging_config.json 파일을 제작하여 활용하고 있다. 따라서 이번 게시글에서는 logging_config 제작에 필요한 logging의 기본적인 개념과 실제로 구현하는 과정에 대해서 알아보고자 한다.


logging

 우리가 작성한 코드가 잘 작동하는 지 print(확인용 멘트)를 활용하여 체크하곤 한다. 하지만 실무에서는 이런 print보다 좀 더 체계적인 확인방법을 사용한다고 한다. 그게 바로 logging이다. logging은 코드에서 발생한 이벤트를 추적하는 수단으로 활용되며 python 자체에서 logging 라이브러리를 제공하고 있어서 사용하면 코드가 잘 작성되고 있는 지 확인해볼 수 있다.

 

 json 파일없이 일반적으로 python editor 상에서 logger를 생성하기 위해선 다음과 같은 코드를 활용할 수 있다.

import logging

# logger 생성
root_logger = logging.getLogger(name = '') # 가장 상위 logger (모든 logger들의 부모)
logger = logging.getLogger(name = 'normal')

# logger 수준 설정
logger.setLevel(logging.INFO)

 


logging 요소

 Logging은 크게 3가지 요소로 구성되어 있다.

logging 각각의 요소에 해당하는 개념과 자주 사용되는 옵션들에 대해서 살펴보자.

 

1. level

 

 일반적으로 log를 남길 level은 다음과 같이 구분된다.

DEBUG(디버그) >  INFO(정보) >  WARNING(경고) > ERROR(에러) > CRITICAL(심각)

 

  • DEBUG : 디버깅이 필요하다고 생각하는 정보
  • INFO : 정상적으로 동작하고 있음을 나타내는 정보
  • WARNING : 동작은 하나, 예상하지 못한 일이 발생했음을 나타내는 정보
  • ERROR : 함수가 실행되지 않을 때를 나타내는 정보
  • CRITICAL : 프로그램이 동작하지못할 때를 나타내는 정보

사용자 입장에서 위에서 소개한 단계를 통해 자신이 log를 남기고 싶은 수준의 level을 정하고 해당 level 이상되는 log들에 대해서 남길 것을 명령한다.

def main():
	logging.debug('출력 문자')
    logging.info('출력 문자')
    logging.warning('출력 문자')
    logging.error('출력 문자')
    logging.critical('출력 문자')
    

if __name__="__main__":
	main()

 

2. handler

logger를 생성하고 어떤 수준에서 log를 남길 지 정했다면, log를 어디에 출력/저장할 것인지 정해야한다.

이때 사용할 수 있는 개념이 handler이다. handler에는 크게 2가지 방식으로 나뉜다. 첫번째는 StreamHandler  이 Handler의 경우에는 흔히 우리가 보는 CMD 환경 즉 Console에다 log를 출력하는 방식을 담당하는 Handler이다.

 

 두번째 Handler는 FileHandler 방식이다. 이 방식은 Console에 log를 출력하는 것이 아니라 실제 파일로 log 기록을 남기는 기능을 지원하는 Handler이다.

 

 출력/저장으로 크게 나누면 2가지이지만 세부적인 기능 차이까지 고려하면 다음과 같이 나뉠 수도 있다.

logging에 대한 데이터를 어떻게 다루는 지 정의하는 파트인 만큼 사실 사용자 입장에서 중요한 파트이니, 꼭 알아두고 활용할 수 있다면 좋을 것이다.

  • StreamHandler : Console에 Log 남기기
  • FileHandler : log를 계속 지정해둔 파일에 쌓을 때 사용
    • 파일명 : string : '로그저장할 파일명.log'
    • mode = "w" or "a"  (a : 추가, w : 덮어쓰기)
  • RotationFileHandler : 파일용량을 정해서 log를 쌓을 때
    • 파일명 : string : '로그저장할 팡리명.log' or '로그저장할 파일명.txt'
    • mode = "a" or "w"  (a : 추가, w : 덮어쓰기)
    • maxBytes = 최대 파일당 몇 byte까지 제작가능한 지 설정
    • backupCount = 몇개의 backup파일 생성할 지 설정
  • TimedRotatiingFileHandler : 시간을 정해두고 log를 쌓고 제거할 때
    • 파일명 : string : '로그저장할 팡리명.log' or '로그저장할 파일명.txt'
    • when = 'h' (언제까지 log를 쌓을 지 설정)
    • interval = integer (1일때 1분 기준으로 log 생성)
    • backupCount = 몇개의 backup파일 생성할 지 설정

 

3. formatter

 log를 남기기로 했다면, 어떤 형식으로 남길 것인지를 정해야할 것이다. formatter 설정을 통해서 우리는 어떤 format으로 log를 남기리지 설정해줄 수 있다. formatter에서 사용하는 attribute에는 다음이 있다.

  • asctime = 로그기록시간 ( datafmt 파라미터를 활용하여 format을 정해줄 수 있다)
  • name  = format을 적용할 Logger 이름
  • levelname = format을 적용할 level
  • message = 출력할 메세지
logging.Formatter('텍스트 포멧', datefmt='날짜시간 포멧')

 위 코드에서 사용된 format을 지정할 때, 사용할 수 있는 keyword는 다음과 같다.  자주 사용되는 5가지만 적었다.

  1.  %(name)s : 로그 이름
  2.  %(message)s :로그 메세지
  3.  %(levelname)s :로그 레벨
  4.  %(filename)s :파일명
  5.  %(asctime)s :날짜 시간

logging  활용 형태 (3가지)

 

++) 위에서 설명한 과정을 생성한 logger에 반영하는 과정까지 다시 정의하면 다음과 같다.

import logging

logger = logging.getLogger(name='normal')
logger.setLevel(logging.INFO)

formatter = logging.Formatter('텍스트 포멧', datefmt='날짜시간 포멧')

handler = logging.FileHandler('저장할 로그파일명.log')
handler.setFormatter(formatter)
logger.addHandler(handler)

 

 

++) 위와 같이 정의하기도 할수도 있지만,   한번에 baseConfig 메서드를 활용하여 필요한 logger를 정의할 수도 있다.

import logging

logging.basicConfig( level = logging.INFO,
					 filename = 'test.log',
                     filemode = 'w',
                     format = '텍스트 포멧')
 
 logging.info('출력 문자'

 

 

++) 이 외에도 자주 사용하는 configuration의 경우에는 json이나 yaml 파일로 저장해두고 사용할 수도 있다. (자주사용)

**아래 코드에서 등장하는 "disable_existing_loggers"는 기존에 logging.dictConfig()나 logging.fileConfig()를 호출할 시에 기존에 존재하는 logger들이 비활성화되는 issue를 막기위해 False로 설정해주면서 호출 시에 다른 logger들이 비활성화되는 것을 방지해주는 용도로 사용한다.

{	
    "version" : ,
    "disable_existing_loggers": ,
    "formatters": {
    		    format1 : {},
                    format2 : {}
                   },
    "handlers": {
    		    console : {class, level, formatter},
                    info_file_handler : {class, level, formatter}
                  },
    "root": {level, handlers : [console, info_file_handler]},
}

 

 


Tamplate에서 사용된 코드 정리 (logger_config.json)

 현재 필자가 분석하고 있는 Templat에서는 위에서 공부했던 방식 중 json을 활용한 방식으로 logger_configuraiton을 정의하였으며, 위에서 배운 내용들을 토대로 아래 코드를 해석해보자.

{
    "version": 1, 
    "disable_existing_loggers": false, 
    "formatters": {
        "simple": {"format": "%(message)s"}, 
        "datetime": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"}
    }, 
    "handlers": {
        "console": {
            "class": "logging.StreamHandler", 
            "level": "DEBUG", 
            "formatter": "simple", 
            "stream": "ext://sys.stdout"
            }, 
        "info_file_handler": {
            "class": "logging.handlers.RotatingFileHandler", 
            "level": "INFO", 
            "formatter": "datetime", 
            "filename": "info.log", 
            "maxBytes": 10485760, 
            "backupCount": 20, "encoding": "utf8"
        }
    }, 
    "root": {
        "level": "INFO", 
        "handlers": [
            "console", 
            "info_file_handler"
        ]
    }
}

 

 


Reference

https://hwangheek.github.io/2019/python-logging/#3-1-Logging-Configuration-File-in-JSON-or-YAML

https://data-newbie.tistory.com/248


인스타 주소 🎗

https://www.instagram.com/f.inn_sharp/

반응형