Если вы проверите Исходники 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
, который бы делегировал свои вызовы соответствующему классу регистратора, основываясь на том, что его имя было. Возможно, есть и другие, более изящные возможности, но сейчас я не могу придумать ничего.