[Python] 데코레이터(장식물)를 사용하여 Logging 기능을 공통화하려고 합니다.
그것은 상당히 편리하다.
Decorator의 사용법을 복습한다는 뜻을 담고 있으며, Decorator를 사용하여 Logging 기능을 공통화하는 방법을 쓴다.
컨디션
소스 코드 test1.py
를 모듈로 decorder를 제작하고test2.py
에서 test1.py
decorder를 가져와 이런 구성을 실시한다.
test1.pyimport inspect
import logging
from functools import wraps
class CustomFilter(logging.Filter):
"""logger用のユーザー定義フィルター"""
def filter(self, record):
"""呼び出し元のファイル名、関数名、行番号が表示されるようにする関数
これでフィルタリングしないとデコレーターを使用した関数(呼び出し元)に関する情報ではなく、
test1.pyの後述のlog関数を元にした情報が出力される
Returns:
True: 常にフィルターをパスする
"""
record.real_filename = getattr(record,
'real_filename',
record.filename)
record.real_funcName = getattr(record,
'real_funcName',
record.funcName)
record.real_lineno = getattr(record,
'real_lineno',
record.lineno)
return True
def get_logger():
"""logging.Loggerの作成
Returns:
logger (logging.Logger): logging.Loggerのインスタンス
"""
log_format = '[%(asctime)s] %(levelname)s\t%(real_filename)s' \
' - %(real_funcName)s:%(real_lineno)s -> %(message)s'
logging.basicConfig(format=log_format, level=logging.INFO)
logger = logging.getLogger(__name__)
logger.addFilter(CustomFilter())
return logger
def log(logger):
"""デコレーターでloggerを引数にとるためのラッパー関数
Args:
logger (logging.Logger)
Returns:
_decoratorの返り値
"""
def _decorator(func):
"""デコレーターを使用する関数を引数とする
Args:
func (function)
Returns:
wrapperの返り値
"""
# funcのメタデータを引き継ぐ
@wraps(func)
def wrapper(*args, **kwargs):
"""実際の処理を書くための関数
Args:
*args, **kwargs: funcの引数
Returns:
funcの返り値
"""
func_name = func.__name__
# loggerで使用するためにfuncに関する情報をdict化
extra = {
'real_filename': inspect.getfile(func),
'real_funcName': func_name,
'real_lineno': inspect.currentframe().f_back.f_lineno
}
logger.info(f'[START] {func_name}', extra=extra)
try:
# funcの実行
return func(*args, **kwargs)
except Exception as err:
# funcのエラーハンドリング
logging.error(err, exc_info=True, extra=extra)
logging.error(f'[KILLED] {func_name}', extra=extra)
else:
logging.info(f'[END] {func_name}', extra=extra)
return wrapper
return _decorator
test2.pyfrom test1 import log, get_logger
logger = get_logger()
def raise_error():
raise Exception('Error!!!!')
@log(logger)
def main():
"""デコレーターの呼び出し元
@log(logger)
def main() <=====> log(logger)(main)()
"""
logger.info('Hello world')
return raise_error()
if __name__ == '__main__':
main()
실행 결과
상기 원본 코드를 실행하면 다음과 같은 결과를 얻을 수 있다.$ python test2.py
[2019-09-11 04:49:48,719] INFO test2.py - main:23 -> [START] main
[2019-09-11 04:49:48,719] INFO test2.py - main:18 -> Hello world
[2019-09-11 04:49:48,719] ERROR test2.py - main:23 -> Error!!!!
Traceback (most recent call last):
File "~/test/test1.py", line 90, in wrapper
return func(*args, **kwargs)
File "test2.py", line 19, in main
return raise_error()
File "test2.py", line 8, in raise_error
raise Exception('Error!!!!')
Exception: Error!!!!
[2019-09-11 04:49:48,720] ERROR test2.py - main:23 -> [KILLED] main
Custom Filter로 처리하지 않으면 KeyError가 되거나 원하는 로그 정보를 제대로 얻지 못할 수 있으므로 주의해야 합니다.
끝말
장식물을 사용하면 간단하게 유니버설 처리를 할 수 있을 뿐만 아니라 간단하게 진행할 수 있을 거라고 생각합니다.개인적으로 소스 코드가 시원해서 너무 좋아요.
이것을 만드는 것은 장식물에 대한 참고가 아니라 logging — Logging facility for Python과cpython/Lib/logging/__init__.py를 읽었다.logging 주위가 더 똑똑해질지도 몰라요.좋은 방법이 있으면 댓글로 남겨주세요
Reference
import inspect
import logging
from functools import wraps
class CustomFilter(logging.Filter):
"""logger用のユーザー定義フィルター"""
def filter(self, record):
"""呼び出し元のファイル名、関数名、行番号が表示されるようにする関数
これでフィルタリングしないとデコレーターを使用した関数(呼び出し元)に関する情報ではなく、
test1.pyの後述のlog関数を元にした情報が出力される
Returns:
True: 常にフィルターをパスする
"""
record.real_filename = getattr(record,
'real_filename',
record.filename)
record.real_funcName = getattr(record,
'real_funcName',
record.funcName)
record.real_lineno = getattr(record,
'real_lineno',
record.lineno)
return True
def get_logger():
"""logging.Loggerの作成
Returns:
logger (logging.Logger): logging.Loggerのインスタンス
"""
log_format = '[%(asctime)s] %(levelname)s\t%(real_filename)s' \
' - %(real_funcName)s:%(real_lineno)s -> %(message)s'
logging.basicConfig(format=log_format, level=logging.INFO)
logger = logging.getLogger(__name__)
logger.addFilter(CustomFilter())
return logger
def log(logger):
"""デコレーターでloggerを引数にとるためのラッパー関数
Args:
logger (logging.Logger)
Returns:
_decoratorの返り値
"""
def _decorator(func):
"""デコレーターを使用する関数を引数とする
Args:
func (function)
Returns:
wrapperの返り値
"""
# funcのメタデータを引き継ぐ
@wraps(func)
def wrapper(*args, **kwargs):
"""実際の処理を書くための関数
Args:
*args, **kwargs: funcの引数
Returns:
funcの返り値
"""
func_name = func.__name__
# loggerで使用するためにfuncに関する情報をdict化
extra = {
'real_filename': inspect.getfile(func),
'real_funcName': func_name,
'real_lineno': inspect.currentframe().f_back.f_lineno
}
logger.info(f'[START] {func_name}', extra=extra)
try:
# funcの実行
return func(*args, **kwargs)
except Exception as err:
# funcのエラーハンドリング
logging.error(err, exc_info=True, extra=extra)
logging.error(f'[KILLED] {func_name}', extra=extra)
else:
logging.info(f'[END] {func_name}', extra=extra)
return wrapper
return _decorator
from test1 import log, get_logger
logger = get_logger()
def raise_error():
raise Exception('Error!!!!')
@log(logger)
def main():
"""デコレーターの呼び出し元
@log(logger)
def main() <=====> log(logger)(main)()
"""
logger.info('Hello world')
return raise_error()
if __name__ == '__main__':
main()
상기 원본 코드를 실행하면 다음과 같은 결과를 얻을 수 있다.
$ python test2.py
[2019-09-11 04:49:48,719] INFO test2.py - main:23 -> [START] main
[2019-09-11 04:49:48,719] INFO test2.py - main:18 -> Hello world
[2019-09-11 04:49:48,719] ERROR test2.py - main:23 -> Error!!!!
Traceback (most recent call last):
File "~/test/test1.py", line 90, in wrapper
return func(*args, **kwargs)
File "test2.py", line 19, in main
return raise_error()
File "test2.py", line 8, in raise_error
raise Exception('Error!!!!')
Exception: Error!!!!
[2019-09-11 04:49:48,720] ERROR test2.py - main:23 -> [KILLED] main
Custom Filter로 처리하지 않으면 KeyError가 되거나 원하는 로그 정보를 제대로 얻지 못할 수 있으므로 주의해야 합니다.끝말
장식물을 사용하면 간단하게 유니버설 처리를 할 수 있을 뿐만 아니라 간단하게 진행할 수 있을 거라고 생각합니다.개인적으로 소스 코드가 시원해서 너무 좋아요.
이것을 만드는 것은 장식물에 대한 참고가 아니라 logging — Logging facility for Python과cpython/Lib/logging/__init__.py를 읽었다.logging 주위가 더 똑똑해질지도 몰라요.좋은 방법이 있으면 댓글로 남겨주세요
Reference
Reference
이 문제에 관하여([Python] 데코레이터(장식물)를 사용하여 Logging 기능을 공통화하려고 합니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/homines22/items/dccae65fa3434641b995텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)