import functools
import inspect
import logging
import threading
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,
},
)
raise
return wrapper