import logging
# 로그 생성
logger = logging.getLogger()
# 로그의 출력 기준 설정
logger.setLevel(logging.INFO)
# log 출력 형식
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# log 출력
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
# log를 파일에 출력
file_handler = logging.FileHandler('my.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
for i in range(10):
logger.info(f'{i}번째 방문입니다.')
네 번째 키워드 인자는 extra 로, 로깅 이벤트용으로 만들어진 LogRecord의 dict 를 사용자 정의 어트리뷰트로 채우는 데 사용되는 딕셔너리를 전달.
FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logger = logging.getLogger('tcpserver')
logger.warning('Protocol problem: %s', 'connection reset', extra=d)
예시
2022-11-08 07:54:49,120 - My logger - ERROR - 404 K2_20200430_002721_P007_RGB_PS.tif
2022-11-08 07:54:49,120 - My logger - ERROR - 404 K2_20200430_002721_P007_RGB_PS.tif
2022-11-08 07:54:49,120 - My logger - ERROR - 404 K2_20200430_002721_P007_RGB_PS.tif
2022-11-08 07:54:49,224 - My logger - ERROR - 404 WV2_20191116_100421_P000_RGB_PS.TIF
2022-11-08 07:54:49,224 - My logger - ERROR - 404 WV2_20191116_100421_P000_RGB_PS.TIF
2022-11-08 07:54:49,224 - My logger - ERROR - 404 WV2_20191116_100421_P000_RGB_PS.TIF
2022-11-08 07:54:49,634 - My logger - ERROR - 404 K3A_20170330_045311_P000_RGB_PS.tif
2022-11-08 07:54:49,634 - My logger - ERROR - 404 K3A_20170330_045311_P000_RGB_PS.tif
2022-11-08 07:54:49,634 - My logger - ERROR - 404 K3A_20170330_045311_P000_RGB_PS.tif
3번씩 중복되어 있다!
원인
같은 logger에 핸들러를 계속해서 붙이므로 한번 logging해도 여러 개의 핸들러가 작동하여 로그는 여러 개가 찍히는 것이다.
logger = logging.getLogger("My logger")
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# file_handler = logging.FileHandler('/var/log/Scene/tiled_status.log')
file_handler = logging.FileHandler('/nas/k8s/dev/data/shared-log/Scene/tiled_api_error.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
def get_logger(log_path):
# Create Logger
logger = logging.getLogger("My logger")
if len(logger.handlers) > 0:
return logger
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# file_handler = logging.FileHandler('/var/log/Scene/tiled_status.log')
file_handler = logging.FileHandler(log_path)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
return logger
log_path = 'logpath'
logger = get_logger(log_path)
# settings.py
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
},
'handlers': {
'default': {
'level': 'INFO',
'formatter': 'standard',
'class': 'logging.StreamHandler',
'stream': 'ext://sys.stdout',
},
'scene_processing': {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": f"/var/log/Scene/INDEX/LOG/{datetime.now().date()}_scene_migrate.log",
"maxBytes": 1024 * 1024 * 5,
"backupCount": 5,
"formatter": "standard",
},
'scene_migration': {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": f"/var/log/Scene/INDEX/LOG/{datetime.now().date()}_scene_migrate.log",
"maxBytes": 1024 * 1024 * 5,
"backupCount": 5,
"formatter": "standard",
},
'scene_indexing': {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": f"/var/log/Scene/INDEX/LOG/{datetime.now().date()}_scene_indexing.log",
"maxBytes": 1024 * 1024 * 5,
"backupCount": 5,
"formatter": "standard",
},
},
'loggers': {
'': {
'handlers': ['default'],
'level': 'WARNING',
'propagate': False
},
'scene_processing': {
'handlers': ['scene_processing'],
'level': 'INFO',
'propagate': False
},
'scene_migration': {
'handlers': ['scene_migration'],
'level': 'INFO',
'propagate': False
},
'scene_indexing': {
'handlers': ['scene_indexing'],
'level': 'INFO',
'propagate': False
},
'__main__': { # if __name__ == '__main__'
'handlers': ['default'],
'level': 'DEBUG',
'propagate': False
},
}
}