Python indentation\context level для записи длины префикса

моя идея состоит в том, чтобы создать схему ведения журнала контекста, как показано на примере ниже:

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

(...)

Это работает нормально, но есть некоторые тонкие проблемы, например: при регистрации из внутренних функций - их можно вызывать из разных областей, а длина префикса может варьироваться для каждого вызова.

Я ищу элегантный и невидимый способ установить длину префикса "..." автоматически для каждого журнала. Я предпочел бы избегать передачи длины префикса в качестве параметра для каждого func или установки длины с использованием явных вызовов, например:

(...)

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

(...)

Есть ли способ получить текущий уровень отступов от парсера Python или создать класс оболочки, чувствительный к области видимости, для ведения журнала?

Ответ 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]

Ответ 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

Объединяя предыдущие ответы с Как добавить настраиваемое поле в строку формата журнала Python? может достичь того же результата без необходимости предоставления настраиваемого метода 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(logger, {})
logger.debug('A debug message')
logger.error('An error message')
logger.info('An info message')