Как написать собственные методы ведения журнала для собственных уровней ведения журнала - PullRequest
6 голосов
/ 10 февраля 2011

Привет
Я хотел бы расширить мой регистратор (взятый logging.getLogger ("rrcheck")) моими собственными методами, такими как: def warnpfx(...):

Как это сделать лучше всего?

Мое первоначальное желание - чтобы корневой регистратор записывал все в файл и дополнительно называл регистратор ("rrcheck") для записи в стандартный вывод, но последний также должен иметь некоторые другие методы и уровни. Мне нужно добавить некоторые сообщения с префиксом "! PFXWRN" (но только те, которые идут на стандартный вывод) и оставить другие сообщения без изменений. Я также хотел бы установить уровень ведения журнала отдельно для root и для именованного регистратора.

Это мой код:

class CheloExtendedLogger(logging.Logger):
    """
    Custom logger class with additional levels and methods
    """
    WARNPFX = logging.WARNING+1

    def __init__(self, name):
        logging.Logger.__init__(self, name, logging.DEBUG)                

        logging.addLevelName(self.WARNPFX, 'WARNING')

        console = logging.StreamHandler()
        console.setLevel(logging.DEBUG)
        # create formatter and add it to the handlers
        formatter = logging.Formatter("%(asctime)s [%(funcName)s: %(filename)s,%(lineno)d] %(message)s")
        console.setFormatter(formatter)

        # add the handlers to logger
        self.addHandler(console)

        return

    def warnpfx(self, msg, *args, **kw):
        self.log(self.WARNPFX, "! PFXWRN %s" % msg, *args, **kw)


logging.setLoggerClass(CheloExtendedLogger)    
rrclogger = logging.getLogger("rrcheck")
rrclogger.setLevel(logging.INFO)

def test():
    rrclogger.debug("DEBUG message")
    rrclogger.info("INFO message")
    rrclogger.warnpfx("warning with prefix")

test()

И это вывод - неправильная функция и номер lilne: warnpfx вместо test

2011-02-10 14:36:51,482 [test: log4.py,35] INFO message
2011-02-10 14:36:51,497 [warnpfx: log4.py,26] ! PFXWRN warning with prefix

Может быть, мой собственный подход к логгеру не самый лучший?
В каком направлении вы хотели бы пойти (собственный регистратор, собственный обработчик, собственный форматер и т. Д.)?

Как действовать, если я хотел бы иметь еще один регистратор?
К сожалению, при ведении журнала нет возможности зарегистрировать собственный регистратор, поэтому getLogger (имя) может потребовать требуемый ...

С уважением,
Збигнев

1 Ответ

5 голосов
/ 10 февраля 2011

Если вы проверите Исходники Python , вы увидите, что виновником является метод Logger.findCaller, который проходит по стеку вызовов и ищет первую строку, которой нет в файле logging.py. Из-за этого пользовательский вызов self.log в CheloExtendedLogger.warnpfx регистрирует неправильную строку.

К сожалению, код в logging.py не очень модульный, поэтому исправление довольно уродливо: вам нужно самостоятельно переопределить метод findCaller в своем подклассе, чтобы он учитывал как файл logging.py, так и файл, в котором находится ваш регистратор (обратите внимание, что в вашем файле не должно быть никакого кода, кроме регистратора, иначе результаты будут неточными). Это требует изменения одной строки в теле метода:

class CheloExtendedLogger(logging.Logger):

    [...]

    def findCaller(self):
        """
        Find the stack frame of the caller so that we can note the source
        file name, line number and function name.
        """
        f = logging.currentframe().f_back
        rv = "(unknown file)", 0, "(unknown function)"
        while hasattr(f, "f_code"):
            co = f.f_code
            filename = os.path.normcase(co.co_filename)
            if filename in (_srcfile, logging._srcfile): # This line is modified.
                f = f.f_back
                continue
            rv = (filename, f.f_lineno, co.co_name)
            break
        return rv

Чтобы это работало, вам нужно определить собственную переменную _srcfile в вашем файле. Опять же, logging.py не использует функцию, а скорее помещает весь код на уровень модуля, поэтому вам нужно снова скопировать-вставить:

if hasattr(sys, 'frozen'): #support for py2exe
    _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
elif string.lower(__file__[-4:]) in ['.pyc', '.pyo']:
    _srcfile = __file__[:-4] + '.py'
else:
    _srcfile = __file__
_srcfile = os.path.normcase(_srcfile)

Ну, может, если вам не нужны скомпилированные версии, вам хватит двух последних строк.

Теперь ваш код работает должным образом:

2011-02-10 16:41:48,108 [test: lg.py,16] INFO message
2011-02-10 16:41:48,171 [test: lg.py,17] ! PFXWRN warning with prefix

Что касается нескольких классов регистраторов, если вы не возражаете против зависимости между именем регистратора и классом регистратора, вы можете создать подкласс logging.Logger, который бы делегировал свои вызовы соответствующему классу регистратора, основываясь на том, что его имя было. Возможно, есть и другие, более изящные возможности, но сейчас я не могу придумать ничего.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...