私は、特定の関数またはメソッドを呼び出すために使用される引数を記録するデコレータを作成しました。以下に示すように、それはlogRecord
に報告行番号がデコレータの行番号ではなく、ラップされているfunc
の行番号であることを除いてうまく機能:Pythonロガーでlinenoをオーバーライドする最も良い方法
from functools import wraps
import inspect
import logging
arg_log_fmt = "{name}({arg_str})"
def log_args(logger, level=logging.DEBUG):
"""Decorator to log arguments passed to func."""
def inner_func(func):
line_no = inspect.getsourcelines(func)[-1]
@wraps(func)
def return_func(*args, **kwargs):
arg_list = list("{!r}".format(arg) for arg in args)
arg_list.extend("{}={!r}".format(key, val)
for key, val in kwargs.iteritems())
msg = arg_log_fmt.format(name=func.__name__,
arg_str=", ".join(arg_list))
logger.log(level, msg)
return func(*args, **kwargs)
return return_func
return inner_func
if __name__ == "__main__":
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
fmt = "%(asctime)s %(levelname)-8.8s [%(name)s:%(lineno)4s] %(message)s"
handler.setFormatter(logging.Formatter(fmt))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
@log_args(logger)
def foo(x, y, z):
pass
class Bar(object):
@log_args(logger)
def baz(self, a, b, c):
pass
foo(1, 2, z=3)
foo(1, 2, 3)
foo(x=1, y=2, z=3)
bar = Bar()
bar.baz(1, c=3, b=2)
以下の出力でこのサンプル結果
2015-09-07 12:42:47,779 DEBUG [__main__: 25] foo(1, 2, z=3)
2015-09-07 12:42:47,779 DEBUG [__main__: 25] foo(1, 2, 3)
2015-09-07 12:42:47,779 DEBUG [__main__: 25] foo(y=2, x=1, z=3)
2015-09-07 12:42:47,779 DEBUG [__main__: 25] baz(<__main__.Bar object at 0x1029094d0>, 1, c=3, b=2)
すべての行番号がデコレータを指していることに注意してください。
inspect.getsourcelines(func)
私は関心のある行番号を得ることができますが、lineno
を上書きしようとすると、エラーが発生します。logger.debug
ラップされた関数の行番号をロギングステートメントに表示する最良の方法は何ですか?
入力いただきありがとうございます。 logger.logに 'my_log_info'を渡す必要がありますか?また、私はロガーを他の場所で多く使用しているので、永久に変異させたくありません。私はその観点から猿パッチに不快です。 –
いいえ、これは2.7で書かれたように動作します(試してみてください)。パッチはあまりにも恐ろしいことではありません。他の場所からのロギングは、ここからロギングしていない場合は元のコードが呼び出されるため、いつものように動作します。 –
ありがとう、そうそうです。私はもう少しテストをして、あなたの解決策をコードレビューよりも先にする必要があります;-)これを答えとして受け入れます。 –