4

あるクラスで呼び出しが行われたときにデバッグ情報をログに記録したい。ログに記録されるデータは次のとおりです。

  • 関数名
  • 関数が呼び出された場所のスタックトレース
  • 関数の実行にかかった時間
  • 関数に渡されるargsとkwargs

ラッパーをかなり汎用的にしたいと思います。同時に、ラッピングは実行時に実行する必要があります。情報をログに記録し、呼び出しを元のインスタンスに委任する次のラッパークラスを作成しました。

import datetime
import traceback
from functools import partial

from logging_module import db_log


class BaseWrapper(object):
    def __init__(self, item):
        self._item = item

    def __getattr__(self, attr):
        return getattr(self._item, attr)


class DBLogWrapper(BaseWrapper):

    @staticmethod
    def _time_method(method):
        name = "{0}.{1}.{2}".format(
            method.im_class.__module__,
            method.im_class.__name__,
            method.__name__
        )

        def timed_method(self, *args, **kwargs):
            begin = datetime.datetime.now()
            return_val = method.im_func(self, *args, **kwargs)
            end = datetime.datetime.now()

            trace = traceback.format_stack()

            db_log(
                name,
                begin,
                end,
                info={
                    'args': args,
                    'kwargs': kwargs,
                    'trace': trace
                }
            )
            return return_val

        return timed_method

    def __init__(self, item, methods):
        super(DBLogWrapper, self).__init__(item)
        for method in methods:
            class_method = getattr(item, method)
            wrapped_method = DBLogWrapper._time_method(class_method)
            wrapped_method = partial(wrapped_method, self._item)
            setattr(self, method, wrapped_method)

使用例:

class MyClass(object):

    def hello(self, greeting):
        print greeting

    def goodbye(self):
        print 'Good Bye'

a = MyClass()

if DEBUG:
    a = DBLogWrapper(a, ['hello'])

a.hello()
a.goodbye()

この場合、への呼び出しhelloはログに記録されますが、への呼び出しは記録されgoodbyeません。

ただし、これは単純なはずのタスクにはやり過ぎのようです。上記のコードを改善する方法、またはそれを実行するためのまったく異なる方法についての提案を探しています。

4

1 に答える 1

3

あなたはあまりにも多くの仕事をしています。まったく必要ありませんpartialtimed_methodパラメータなしで定義し、直接self呼び出すだけです。method

import datetime
import traceback
from functools import partial

def db_log(*args, **kwargs): print args, kwargs # Mock


class BaseWrapper(object):
    def __init__(self, instance):
        self._instance = instance

    def __getattr__(self, attr):
        return getattr(self._instance, attr)


class DBLogWrapper(BaseWrapper):

    @staticmethod
    def _time_method(method):
        name = "{0}.{1}.{2}".format(
            method.im_class.__module__,
            method.im_class.__name__,
            method.__name__
        )

        def timed_method(*args, **kwargs):
            begin = datetime.datetime.now()
            return_val = method(*args, **kwargs)
            end = datetime.datetime.now()

            trace = traceback.format_stack()

            db_log(
                name,
                begin,
                end,
                info={
                    'args': args,
                    'kwargs': kwargs,
                    'trace': trace
                }
            )
            return return_val

        return timed_method

    def __init__(self, instance, methods):
        super(DBLogWrapper, self).__init__(instance)
        for method in methods:
            class_method = getattr(instance, method)
            wrapped_method = DBLogWrapper._time_method(class_method)
            setattr(self, method, wrapped_method)

出力:

>>> a = MyClass()
>>> a = prova.DBLogWrapper(a, ['hello'])
>>> a.hello()
A
('__main__.MyClass.hello', datetime.datetime(2013, 1, 17, 20, 48, 26, 478023), datetime.datetime(2013, 1, 17, 20, 48, 26, 478071)) {'info': {'args': (), 'trace': ['  File "<stdin>", line 1, in <module>\n', '  File "prova.py", line 31, in timed_method\n    trace = traceback.format_stack()\n'], 'kwargs': {}}}
>>> a.goodbye()
B

とにかく、多分あなたは__getattr__代わりにいくつかの魔法を使うかもしれません、例えば:

class DBLogWrapper2(BaseWrapper):

    def __init__(self, instance, methods):
        super(DBLogWrapper, self).__init__(instance)

        self._methods = methods

    def __getattr__(self, attr):
        if attr not in methods:
            return getattr(self._instance, attr)

        def f(*args, **kwargs):
            return self.timed_method(getattr(self._item, attr),
                                     *args, **kwargs)
        return f

    def timed_method(method, *args, **kwargs):
        begin = datetime.datetime.now()
        return_val = method(*args, **kwargs)
        end = datetime.datetime.now()

        trace = traceback.format_stack()

        db_log(name,
            begin,
            end,
            info={
                'args': args,
                'kwargs': kwargs,
                'trace': trace
            }
        )
        return return_val
于 2013-01-17T19:49:02.587 に答える