【问题标题】:Show log context level by indentation or prefix length按缩进或前缀长度显示日志上下文级别
【发布时间】:2026-01-04 15:45:02
【问题描述】:

我的想法是制作一个上下文日志记录方案,如下例所示:

[   DEBUG] Parsing dialogs files
[   DEBUG] ... [DialogGroup_001]
[   DEBUG] ...... Indexing dialog xml file [c:\001_dlg.xml]
[   DEBUG] ......... dialog [LobbyA]
[   DEBUG] ............ speech nodes [3]
[   DEBUG] ............... [LobbyA_01]
[   DEBUG] ............... [LobbyA_02]
[   DEBUG] ............... [LobbyA_03]
[   DEBUG] ............ sms nodes [0]
[   DEBUG] ......... dialog [LobbyB]
[   DEBUG] ............ speech nodes [3]
[   DEBUG] ............... [LobbyB_01]
[   DEBUG] ............... [LobbyB_02]
[   DEBUG] ............... [LobbyB_03]
[   DEBUG] ............ sms nodes [0]
[   DEBUG] ... [DialogGroup_002]
[   DEBUG] ...... Indexing dialog xml file [c:\002_dlg.xml]
[   DEBUG] ......... dialog [HighGroundsA]
[   DEBUG] ............ speech nodes [3]
[   DEBUG] ............... [HighGroundsA_01]
[   DEBUG] ............... [HighGroundsA_02]
[   DEBUG] ............... [HighGroundsA_03]
[   DEBUG] ............ sms nodes [0]

此时,我正在使用 Python 的日志记录模块,在记录时带有自定义的手写前缀,例如:

(...)

log.debug('')
log.debug('Parsing dialogs files')
for dlg in defDlgList:
    log.debug('... [{0}]'.format(dlg))

(...)

它工作得很好,但有一些微妙的问题,例如:从内部函数记录时 - 它们可能会从不同的范围调用,并且每次调用的前缀长度可能会有所不同。

我正在寻找一种优雅且不可见的方式来自动为每个日志建立一个“...”前缀的长度。我宁愿避免将前缀长度作为参数传递给每个函数或使用显式调用设置长度,例如:

(...)

logWrapper.debug('')
logWrapper.debug('Parsing dialogs files')
for dlg in defDlgList:
    logWrapper.nextLogLevelBegin()
    logWrapper.debug('[{0}]'.format(dlg))
    logWrapper.nextLogLevelEnd()

(...)

有没有办法从 Python 的解析器中获取当前缩进级别或构造一个范围敏感的包装类来进行日志记录?

【问题讨论】:

    标签: python logging formatting


    【解决方案1】:

    也许您可以使用inspect.getouterframes 来查找缩进级别:

    import inspect
    import logging
    
    logger=logging.getLogger(__name__)
    
    def debug(msg):
        frame,filename,line_number,function_name,lines,index=inspect.getouterframes(
            inspect.currentframe())[1]
        line=lines[0]
        indentation_level=line.find(line.lstrip())
        logger.debug('{i} [{m}]'.format(
            i='.'*indentation_level,
            m=msg            
            ))
    
    def foo():    
        debug('Hi Mom')
        for i in range(1):
            debug("Now we're cookin")
    
    if __name__=='__main__':
        logging.basicConfig(level=logging.DEBUG)
        foo()
    

    产量

    DEBUG:__main__:.... [Hi Mom]
    DEBUG:__main__:........ [Now we're cookin]
    

    【讨论】:

    • 这是我需要知道的! :D:D:D:D 非常感谢!
    • 将此与 textwrap.indent 结合起来取得巨大成功
    【解决方案2】:

    通过文档搜索,我真的没有找到获得当前缩进级别的方法。您能做的最好的事情就是获取当前的函数嵌套级别,如下所示:

    len(traceback.extract_stack());
    

    例子:

    import traceback;
    
    def test():
        print len(traceback.extract_stack()); 
    
    print len(traceback.extract_stack()); # prints 1
    test(); # prints 2
    

    【讨论】:

    • 这很有用,谢谢!我将把它与选择的答案中的解决方案结合起来,使嵌套函数日志正确。
    【解决方案3】:

    将前面的答案与How do I add custom field to Python log format string? 结合可以达到相同的结果,而无需提供自定义的 debug() 方法(因为对于每个级别 info()、error() 等都需要执行相同的操作)。

    import logging
    import traceback
    class CustomAdapter(logging.LoggerAdapter):
        @staticmethod
        def indent():
            indentation_level = len(traceback.extract_stack())
            return indentation_level-4  # Remove logging infrastructure frames
    
        def process(self, msg, kwargs):
            return '{i}{m}'.format(i='\t'*self.indent(), m=msg), kwargs
    
    logger = CustomAdapter(logging.getLogger(__name__), {})
    logger.debug('A debug message')
    logger.error('An error message')
    logger.info('An info message')
    

    【讨论】:

    • 这个片段对我不起作用(使用 Python 2.7)。此外,未定义“记录器”。可以调整/添加相应的行吗?
    • @Mr.EpicFail 我更新以修复丢失的记录器参数。至于python 2.7,我不确定那里发生了什么。