在编写框架代码时,我更喜欢记录调用者的行号和文件名。例如,如果我检测到不正确使用framerwork级API调用,我想记录....不是作为框架内错误而是作为"调用者错误"。
只有在编写使用内省的低级库和系统时才会发挥作用。
有没有办法让记录器记录下来?#34;一级上升"?我可以创建自定义LogRecord然后修改它并以某种方式在已安装的记录器中使用它吗?试图解决这个问题。
例如:
def set(self, x):
if x not in self._cols:
log.error("Invalid attribute for set", stack_level=-1)
答案 0 :(得分:1)
我所做的与Erik相同,但有一些变化:
import logging
import inspect
from os import path
loggers_dict = {}
def myMakeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
if extra and 'pathname' in extra:
fn = extra.pop('pathname')
rv = logging.LogRecord(name, level, fn, lno, msg, args, exc_info, func)
if extra is not None:
rv.__dict__.update(extra)
return rv
logging.Logger.makeRecord = myMakeRecord
def getLogger():
fn, lno, func = base_logger.findCaller()
extras = {'pathname': fn, 'lineno': lno, 'funcName': func}
fnn = path.normcase(fn)
caller_name = inspect.modulesbyfile.get(fnn, inspect.getmodule(None, fn).__name__)
if caller_name not in loggers_dict:
loggers_dict[caller_name] = logging.getLogger(caller_name)
return loggers_dict[caller_name], extras
def myLogDebug(*msg):
log, extras = getLogger()
if len(msg) == 1:
log.debug(msg[0], extra=extras)
else:
log.debug(' '.join(map(str, msg)), extra=extras)
这里的主要内容是遗留物,到处都是人们在代码上调用myLogDebug的情况,如果我更改所有内容,那将是一团糟。另一个问题是python 2.7版本,如果可以使用this thread中的param堆栈级别,那就太好了。
然后,我修改了一些内容以获得正确的调用方堆栈框架级别,并且与原始方法没有任何改变。
编辑-1
caller_name = inspect.modulesbyfile.get(fnn, inspect.getmodule(None, fn).__name__)
这部分是一个小技巧,只是为了不要一直在检查模块中getmodule
运行。有一个字典(内部缓存modulesbyfile
)可以在第一个getmodule
之后直接访问模块名称。
有时会调试源帮助的跟踪,但没有记录。
答案 1 :(得分:0)
好的,我想出来了。
首先,您需要使用inspect来获取框架。然后使用info修改log中的extra =参数。但是你还必须覆盖makerecord以防止阻止日志工作文件名和亚麻覆盖的不适当防护。
def myMakeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, sinfo):
rv = logging.LogRecord(name, level, fn, lno, msg, args, exc_info, func, sinfo)
if extra is not None:
rv.__dict__.update(extra)
return rv
def mylog(logger, msg, level, *args)
logging.Logger.makeRecord = myMakeRecord
frame = inspect.currentframe()
caller = frame.f_back
override = {
"lineno":caller.f_lineno,
"filename":os.path.basename(caller.f_code.co_filename),
}
logger.log(level, msg, extra=override, *args)
奇怪的是,当额外的和sinfo的默认值为none时,我无法使用它(就像它们在原始定义中那样)。也许myMakeRecord应该使用* args ....但是这需要抓住extra = args[9]
......这很奇怪......但也许不那么糟糕(更有可能是未来证明)。
答案 2 :(得分:0)
这是很容易在Google上找到的,因为它已添加到python 3.8中,并且在其他答案中也提到过,但这是一个更好的解释。您与stack_level
参数很接近,但是它应该为stacklevel
,并且值应该为2
。例如,您可以这样做:
import logging
def fun():
logging.basicConfig(format='line %(lineno)s : %(message)s')
log = logging.getLogger(__name__)
log.error('Something is wrong', stacklevel=2)
if __name__ == '__main__':
fun()
这将输出:
line 9 : Something is wrong
如果堆栈级别设置为默认值1
,它将输出:
line 6 : Something is wrong
摘自文档:https://docs.python.org/3/library/logging.html#logging.Logger.debug
第三个可选关键字参数是stacklevel,默认为1。如果大于1,则在计算为日志记录事件创建的LogRecord中设置的行号和函数名时,将跳过相应数量的堆栈帧。可以在记录帮助程序时使用它,以便记录的函数名称,文件名和行号不是帮助程序功能/方法的信息,而是其调用方的信息。此参数的名称反映了警告模块中的等效参数。