Python,在decorator

时间:2017-07-10 11:12:31

标签: python

当我在装饰器中记录错误时,记录pathname不是我想要的。

logging.conf:

[loggers]
keys=root
[handlers]
keys=console
[formatters]
keys=console
[logger_root]
...
[handler_console]
...
[formatter_console]
format=%(levelname)s - File "%(pathname)s", line %(lineno)s, %(funcName)s: %(message)s

正常情况下,在文件/home/lizs/test/app.py中记录错误:

def app():
    try:
        a   # error, on line 12
    except Exception, err:
        logging.getLogger().error(str(err))

控制台上的调试消息:

ERROR - File "/home/lizs/test/app.py", line 12, app: global name 'a' is not defined

上面的日志记录pathname(/ home / lizs / test / app.py)就是我想要的。但是当我使用装饰器时:

/home/lizs/test/app.py

from decorators import logging_decorator


@logging_decorator
def app():
   a

/home/lizs/test/decorators.py

def logging_decorator(func):
    def error_log():
        try:
            func()  # on line 10
        except Exception, err:
            logging.getLogger().error(str(err))
    return error_log

调试消息:

ERROR - File "/home/lizs/test/decorators.py", line 10, error_log: global name 'a' is not defined

现在,日志记录pathname是装饰器的路径名(/home/lizs/test/decorators.py)。

当我使用装饰器时,如何将日志pathname设置为/home/lizs/test/app.py

2 个答案:

答案 0 :(得分:2)

解决方案:

试试这个:

<强> app.py:

from decorators import logging_decorator

@logging_decorator
def app():
    a

app()

<强> decorators.py:

import logging
import inspect

#   init logger
logger = logging.getLogger()
formatter = logging.Formatter('%(levelname)s - File %(real_pathname)s,'
                              ' line %(real_lineno)s, %(real_funcName)s: %(message)s')
console_handle = logging.StreamHandler()
console_handle.setFormatter(formatter)
logger.addHandler(console_handle)


def logging_decorator(func):
    def error_log():
        try:
            func()
        except Exception as err:
            logger.error(err, extra={'real_pathname': inspect.getsourcefile(func),  # path to source file
                                     'real_lineno': inspect.trace()[-1][2],         # line number from trace
                                     'real_funcName': func.__name__})               # function name

    return error_log

说明:

根据docs here,您可以将字典作为extra参数传递,以使用用户定义的属性填充为日志记录事件创建的__dict__ LogRecord 。然后可以根据需要使用这些自定义属性

因此,because we can't modify pathname directlyreal_pathname的这种方法是最直接的。

链接:

答案 1 :(得分:2)

您的问题是您的异常处理程序比最初引发异常的位置高一级,因此您必须检查堆栈跟踪并使用正确的文件/行信息手动构建LogRecord:

def logging_decorator(func):
    def error_log():
        try:
            func()  # on line 10
        except Exception, err:
            tb = sys.exc_info()[2]     # extract the current exception info
            exc_tup = traceback.extract_tb(tb)[-1]  # extract the deeper stack frame
            logger = logging.getLogger()
            # manually build a LogRecord from that stack frame
            lr = logger.makeRecord(logger.name,
                                   logging.ERROR, exc_tup[0], exc_tup[1],
                                   str(err), {}, None, exc_tup[2])
            logger.handle(lr)     # and ask the logging system to process it
    return error_log