import functools import inspect import logging import threading import traceback thread_local = threading.local() def initialize_thread_local(): """スレッドローカルの初期化をします。""" if not hasattr(thread_local, "depth"): thread_local.depth = 1 def trace_log(func): """トレースログを出力します。 Arguments: func: 関数 """ @functools.wraps(func) def wrapper(*args, **kwargs): initialize_thread_local() logger = logging.getLogger(func.__module__) f_pathname = inspect.getfile(func) f_lineno = inspect.getsourcelines(func)[1] # 関数呼出し前ログ出力 actual_args = args[1:] if args and hasattr(args[0], func.__name__) else args ext_depth = ">" * thread_local.depth logger.debug( f"{func.__name__}: args={actual_args}, {kwargs=}", extra={ "ext_depth": ext_depth, "ext_levelname": "TRACE", "ext_pathname": f_pathname, "ext_lineno": f_lineno, }, ) thread_local.depth += 1 try: # 関数実行 result = func(*args, **kwargs) # 関数呼出し後ログ出力 thread_local.depth -= 1 ext_depth = "<" * thread_local.depth logger.debug( f"{func.__name__}: return={result}", extra={ "ext_depth": ext_depth, "ext_levelname": "TRACE", "ext_pathname": f_pathname, "ext_lineno": f_lineno, }, ) return result except Exception as e: # エラー発生時ログ出力 thread_local.depth -= 1 ext_depth = "#" * thread_local.depth logger.error( f"{func.__name__}: {e=}", extra={ "ext_depth": ext_depth, "ext_pathname": f_pathname, "ext_lineno": f_lineno, }, ) # トレースバック出力 traceback_str = traceback.format_exc() logger.error(f"{traceback_str}") raise return wrapper