pythonloggingformatting

Show log context level by indentation or prefix length


My idea is to make a context logging scheme as showed on the example below:

[   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]

At this point, I'm using Python's logging module with custom, hand-written prefixes when logging, for example:

(...)

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

(...)

It's working quite ok, but there are some subtle problems, for example: when logging from inside functions - they may be called from various scopes and prefix length may vary for each call.

I'm looking for an elegant and invisible way to establish a length of a '...' prefix automatically for each log. I'd rather avoid passing prefix length as a parameter to each function or setting the length using explicit calls, for example:

(...)

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

(...)

Is there a way to get the current indentation level from Python's parser or construct a scope sensitive wrapper class for logging?


Solution

  • Perhaps you can use inspect.getouterframes to find the indentation level:

    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()
    

    yields

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