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

你做的工作太多了。你根本不需要partial。只需定义timed_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 回答