앞에서 로그를 사용하게 쉽게 작업해보았습니다. 아래 소스를 기반으로 class도 만들어 봤습니다.
https://swlock.blogspot.com/2019/04/python-logging-using-logging-in-python.html
그런데 사용중에 문제가 발생하였습니다.
로그가 두번씩 출력되는 현상입니다. 일단 문제의 상황을 간단하게 예제로 만들어 보았습니다.
customlog.py
import datetime import sys import os import time import logging from logging import FileHandler from logging import handlers #https://swlock.blogspot.com/2019/04/python-logging-using-logging-in-python.html class CustomLog: def __init__(self): pass def create(self,logfolder='',app_name=__name__,logprefix='log_'): LOG_PREFIX = logprefix LOG_FOLDER_NAME = logfolder # 로그 파일 핸들러 now = datetime.datetime.now().isoformat()[:10] logf = LOG_PREFIX + now + ".log" if LOG_FOLDER_NAME!='' and not os.path.exists(LOG_FOLDER_NAME): os.makedirs(LOG_FOLDER_NAME) logf = os.path.join(LOG_FOLDER_NAME, logf) logger = self.getLogger(logf, app_name) return logger def getLogger(self, filename, app_name, NEED_BACKUP_FILE = True, NEED_SCREEN_AND_FILE = True, level=logging.DEBUG, format='%(asctime)s,%(message)s', maxBytes=10*1024*1024, backupCount = 10, encoding='utf-8'): self.logger = logging.getLogger(app_name) # log level은 아래 순서에 따른다. setLevel 과 같거나 큰 출력만 나오게 된다. # logger.setLevel(logging.CRITICAL) 인 경우 logger.critical() 만 나온다 # # logger.debug("debug") # logger.info("info") # logger.warning("warning") # logger.error("error") # logger.critical("critical") self.logger.setLevel(level) # 포맷을 결정한다. 좀더 자세한 정보는 다음 링크를 참조한다 # https://docs.python.org/3/library/logging.html#logrecord-attributes logFormatter = logging.Formatter(format) if NEED_BACKUP_FILE == True: rotatingHandler = handlers.RotatingFileHandler(filename=filename, maxBytes=maxBytes, backupCount = backupCount, encoding=encoding) rotatingHandler.setFormatter(logFormatter) self.logger.addHandler(rotatingHandler) else: fileHandler = logging.FileHandler(filename) fileHandler.setFormatter(logFormatter) self.logger.addHandler(fileHandler) # StreamHandler 는 self.logger.critical("critical") 이런 형태의 로그가 화면으로도 나오고 file로도 저장이 되도록 한다 if NEED_SCREEN_AND_FILE == True: streamHandler = logging.StreamHandler() streamHandler.setFormatter(logFormatter) self.logger.addHandler(streamHandler) return self.logger if __name__ == "__main__": logger = CustomLog().create('log') print("test") logger.debug("debug test") logger.info("info test") logger.warning("warning test") logger.error("error test") logger.critical("critical test")
logtest2.py
import customlog test2 = 1 logger = customlog.CustomLog().create('Log') print("log test2") logger.debug("debug test") logger.info("info test") logger.warning("warning test") logger.error("error test") logger.critical("critical test")
logtest1.py
import customlog import logtest2 logger = customlog.CustomLog().create('Log') print("logtest1") print(logtest2.test2) logger.debug("debug test") logger.info("info test") logger.warning("warning test") logger.error("error test") logger.critical("critical test")
test 결과
>python logtest1.py log test2 2020-11-01 13:56:43,674,debug test 2020-11-01 13:56:43,676,info test 2020-11-01 13:56:43,676,warning test 2020-11-01 13:56:43,677,error test 2020-11-01 13:56:43,678,critical test logtest1 1 2020-11-01 13:56:43,681,debug test 2020-11-01 13:56:43,681,debug test 2020-11-01 13:56:43,682,info test 2020-11-01 13:56:43,682,info test 2020-11-01 13:56:43,684,warning test 2020-11-01 13:56:43,684,warning test 2020-11-01 13:56:43,687,error test 2020-11-01 13:56:43,687,error test 2020-11-01 13:56:43,688,critical test 2020-11-01 13:56:43,688,critical test
logtest1.py에서 출력하는 log들이 두번씩 출력되게 됩니다.
원인
imoprt logtest2에 의해 customlog의 create()가 두번 호출 되었기 때문입니다.
logger = customlog.CustomLog().create('Log')
분석을 해봐도 CustomLog의 instance가 두번 생성되는것은 아닌 상황이고, handler가 두번 붙어있는건 맞지만 handler를 초기화 해줘도 실제 초기화 되지 않았습니다.
해결 방법
propagate 값을 False로 설정하여 계층화된 로그의 내용이 모이지 않도록 합니다.
그리고 log 처리기에 각각의 이름을 붙여줍니다.
전체 소스
propagate = False 추가함
customlog.py
import datetime import sys import os import time import logging from logging import FileHandler from logging import handlers #https://swlock.blogspot.com/2019/04/python-logging-using-logging-in-python.html class CustomLog: def __init__(self): pass def create(self,logfolder='',app_name=__name__,logprefix='log_'): LOG_PREFIX = logprefix LOG_FOLDER_NAME = logfolder # 로그 파일 핸들러 now = datetime.datetime.now().isoformat()[:10] logf = LOG_PREFIX + now + ".log" if LOG_FOLDER_NAME!='' and not os.path.exists(LOG_FOLDER_NAME): os.makedirs(LOG_FOLDER_NAME) logf = os.path.join(LOG_FOLDER_NAME, logf) logger = self.getLogger(logf, app_name) return logger def getLogger(self, filename, app_name, NEED_BACKUP_FILE = True, NEED_SCREEN_AND_FILE = True, level=logging.DEBUG, format='%(asctime)s,%(message)s', maxBytes=10*1024*1024, backupCount = 10, encoding='utf-8'): self.logger = logging.getLogger(app_name) self.logger.propagate = False # log level은 아래 순서에 따른다. setLevel 과 같거나 큰 출력만 나오게 된다. # logger.setLevel(logging.CRITICAL) 인 경우 logger.critical() 만 나온다 # # logger.debug("debug") # logger.info("info") # logger.warning("warning") # logger.error("error") # logger.critical("critical") self.logger.setLevel(level) # 포맷을 결정한다. 좀더 자세한 정보는 다음 링크를 참조한다 # https://docs.python.org/3/library/logging.html#logrecord-attributes logFormatter = logging.Formatter(format) if NEED_BACKUP_FILE == True: rotatingHandler = handlers.RotatingFileHandler(filename=filename, maxBytes=maxBytes, backupCount = backupCount, encoding=encoding) rotatingHandler.setFormatter(logFormatter) self.logger.addHandler(rotatingHandler) else: fileHandler = logging.FileHandler(filename) fileHandler.setFormatter(logFormatter) self.logger.addHandler(fileHandler) # StreamHandler 는 self.logger.critical("critical") 이런 형태의 로그가 화면으로도 나오고 file로도 저장이 되도록 한다 if NEED_SCREEN_AND_FILE == True: streamHandler = logging.StreamHandler() streamHandler.setFormatter(logFormatter) self.logger.addHandler(streamHandler) return self.logger if __name__ == "__main__": logger = CustomLog().create('log') print("test") logger.debug("debug test") logger.info("info test") logger.warning("warning test") logger.error("error test") logger.critical("critical test")
__name__ 추가함
logtest2.py
import customlog test2 = 1 logger = customlog.CustomLog().create('Log',__name__) print("log test2") logger.debug("debug test") logger.info("info test") logger.warning("warning test") logger.error("error test") logger.critical("critical test")
__name__ 추가함
logtest1.py
import customlog import logtest2 logger = customlog.CustomLog().create('Log',__name__) print("logtest1") print(logtest2.test2) logger.debug("debug test") logger.info("info test") logger.warning("warning test") logger.error("error test") logger.critical("critical test")
실행결과
C:\Users\USER\Documents\python\grammer>python logtest1.py log test2 2020-11-01 14:17:13,104,debug test 2020-11-01 14:17:13,106,info test 2020-11-01 14:17:13,107,warning test 2020-11-01 14:17:13,108,error test 2020-11-01 14:17:13,108,critical test logtest1 1 2020-11-01 14:17:13,111,debug test 2020-11-01 14:17:13,112,info test 2020-11-01 14:17:13,113,warning test 2020-11-01 14:17:13,114,error test 2020-11-01 14:17:13,117,critical test
추가로 핸들러를 초기화 하는것도 참고 바랍니다.
아래 colored log 에서는 create할때 handler를 clear하는 코드를 넣었습니다.
python 에서 logging 사용하기(using logging in python)
https://swlock.blogspot.com/2019/04/python-logging-using-logging-in-python.html
python logger,colored log (컬러 로거 logger)
https://swlock.blogspot.com/2021/10/python-loggercolored-log-logger.html
python 멀티 로그 레벨을 가지는 로거(how to set multi setlevel in python logger)
https://swlock.blogspot.com/2021/10/python-how-to-set-multi-setlevel-in.html
댓글 없음:
댓글 쓰기