在跟踪装饰器中,如何检索从装饰函数返回的返回语句的行号



在Python 3.5中,我正在编写一个元类,它将跟踪装饰器(方法入口和出口的日志记录)添加到所有方法中。在调用log函数之前,我正在使用logging.getLogRecordFactory()/setLogRecordFactory()修改LogRecord中的一些字段,尤其是我想修改"lineno"。对于入口跟踪,我使用co_firstlineno,对于由于异常而导致的出口跟踪,我将使用回溯的tb_lineno。

如何检索从修饰函数返回的return语句(可能有多个这样的语句)的行号,或者在返回时没有return语句的情况下检索该函数最后一个语句的行号?

sys.setrace()解决了我的问题。

这里是我的元类的完整代码,它为使用类的所有方法添加了入口/出口跟踪:

import functools
import inspect
import logging
import sys
import types
TRACE = 5
logging.addLevelName(TRACE, "TRACE")

def _trace(self, msg, *args, **kwargs):
if self.isEnabledFor(TRACE):
self._log(TRACE, msg, args, **kwargs)
logging.Logger.trace = _trace

def get_logger(func, cls=None):
logger_name = "%s.%s" % (
func.__module__,
func.__qualname__ if cls is None else
"%s.%s" % (
cls.__qualname__,
func.__qualname__))
return logging.getLogger(logger_name)

def trace(func, cls=None):
@functools.wraps(func)
def wrapper(*args, **kwargs):
old_record_factory = logging.getLogRecordFactory()
code = inspect.unwrap(func).__code__
pathname = code.co_filename
funcname = func.__name__
def create_record_factory(lineno):
def record_factory(*args, **kwargs):
record = old_record_factory(*args, **kwargs)
record.lineno = lineno
record.pathname = pathname
record.funcName = funcname
return record
return record_factory
logger = get_logger(func, cls)
exception_line = None
return_line = None
def call_tracer(frame, event, arg):
if event == 'call' and frame.f_code == code:
return exit_tracer
def exit_tracer(frame, event, arg):
nonlocal exception_line, return_line
if event == 'return':
return_line = frame.f_lineno
elif event == 'exception':
exception_line = frame.f_lineno
return exit_tracer
def trace_wrapper(msg, lineno):
logging.setLogRecordFactory(create_record_factory(lineno))
logger.trace(msg)
logging.setLogRecordFactory(old_record_factory)
signature = inspect.signature(func)
bound_args = signature.bind(*args, **kwargs)
trace_wrapper(
">>> %s(%s)" % (
func.__name__,
", ".join(
"%s=%r" % item
for item in bound_args.arguments.items())),
code.co_firstlineno)
old_tracer = sys.gettrace()
try:
sys.settrace(call_tracer)
result = func(*args, **kwargs)
trace_wrapper(
"<<< %s returns %r" % (func.__name__, result),
return_line)
return result
except Exception as exc:
trace_wrapper(
"<<< %s raises %r in line %d" % (
func.__name__, exc, exception_line),
return_line)
raise
finally:
sys.settrace(old_tracer)
return wrapper

class LoggingMeta(type):
def __init__(cls, cls_name, bases, cls_dict, **kwargs):
super().__init__(cls_name, bases, cls_dict, **kwargs)
for attr_name, attr_value in cls.__dict__.items():
if isinstance(attr_value, classmethod):
setattr(cls, attr_name, classmethod(
trace(attr_value.__func__)))
elif isinstance(attr_value, staticmethod):
setattr(cls, attr_name, staticmethod(
trace(attr_value.__func__)))
elif isinstance(attr_value, types.FunctionType):
setattr(cls, attr_name, trace(attr_value))

相关内容

  • 没有找到相关文章

最新更新