改变python记录器堆栈级别

时间:2018-04-23 17:56:48

标签: python logging introspection

在编写框架代码时,我更喜欢记录调用者的行号和文件名。例如,如果我检测到不正确使用framerwork级API调用,我想记录....不是作为框架内错误而是作为"调用者错误"。

只有在编写使用内省的低级库和系统时才会发挥作用。

有没有办法让记录器记录下来?#34;一级上升"?我可以创建自定义LogRecord然后修改它并以某种方式在已安装的记录器中使用它吗?试图解决这个问题。

例如:

def set(self, x):
   if x not in self._cols:
      log.error("Invalid attribute for set", stack_level=-1)

3 个答案:

答案 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中设置的行号和函数名时,将跳过相应数量的堆栈帧。可以在记录帮助程序时使用它,以便记录的函数名称,文件名和行号不是帮助程序功能/方法的信息,而是其调用方的信息。此参数的名称反映了警告模块中的等效参数。