Pythonで関数ごとにログを出したいが関数名とか毎回手入力は面倒くさいなあという方用
要らないログを省いた結果、コード自体が長くなってしまったので、モジュール化することにした。
自分を呼び出してしまうので、ファイル名を変更する方は__file__
で自動化すればいいかもしれない
必要であればlogging
で出力する
ログ出力用
getLog.py
import inspect
from datetime import datetime
import os
import functools
def log_function_call(func):
"""関数が呼び出されるたびにログを出力するデコレータ"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 呼び出し元のフレームを取得
frame = inspect.currentframe()
# フレームスタックを辿り、`getLog.py` 以外のフレームを探す
for outer_frame in inspect.getouterframes(frame):
file_name = os.path.basename(outer_frame.filename)
# `getLog.py` 以外のファイルを探す 呼び出し元になってしまうため
if "getLog.py" not in file_name and not file_name.startswith(("pydevd", "debugpy")):
break
current_function = func.__name__
line_number = outer_frame.lineno
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 引数の情報を取得
args_str = ', '.join(repr(arg) for arg in args)
kwargs_str = ', '.join(f"{key}={value!r}" for key, value in kwargs.items())
all_args_str = ', '.join(filter(None, [args_str, kwargs_str]))
# 引数がない場合も () を表示
if all_args_str:
args_display = f"({all_args_str})"
else:
args_display = "()"
# ログ出力
print(f"{timestamp} - {file_name}:{line_number} - {current_function}{args_display}")
# 元の関数を実行
return func(*args, **kwargs)
return wrapper
実装用コード
from getLog import log_function_call
@log_function_call
def test(a):
print(f"tggest: {a}")
@log_function_call
def example_function(x, y, z=1):
print("example_function:x, y, z=0")
if __name__ == "__main__":
test("Hello")
example_function(5, 10, z=3)
example_function(1, 2) # 引数なし
結果
2024-09-08 12:59:38 - main.py:11 - test('Hello')
tggest: Hello
2024-09-08 12:59:38 - main.py:12 - example_function(5, 10, z=3)
example_function:x, y, z=0
2024-09-08 12:59:38 - main.py:13 - example_function(1, 2)
example_function:x, y, z=0