是否可以使用多個日誌記錄器(即logging.getLogger("base.foo")
和logging.getLogger("base.bar")
)登錄到單個目標(即使用一個FileHandler
),併爲每個日誌記錄器使用不同的格式化器。如何在python中使用不同的格式器和相同的日誌處理程序
根據我的理解,只能將一個格式化程序分配給每個句柄。也許有可能將格式化程序與記錄程序而不是處理程序相關聯?
是否可以使用多個日誌記錄器(即logging.getLogger("base.foo")
和logging.getLogger("base.bar")
)登錄到單個目標(即使用一個FileHandler
),併爲每個日誌記錄器使用不同的格式化器。如何在python中使用不同的格式器和相同的日誌處理程序
根據我的理解,只能將一個格式化程序分配給每個句柄。也許有可能將格式化程序與記錄程序而不是處理程序相關聯?
根據record.name
很容易發送到不同的格式化程序。下面是證明了概念的示例代碼:
import logging
class DispatchingFormatter:
def __init__(self, formatters, default_formatter):
self._formatters = formatters
self._default_formatter = default_formatter
def format(self, record):
formatter = self._formatters.get(record.name, self._default_formatter)
return formatter.format(record)
handler = logging.StreamHandler()
handler.setFormatter(DispatchingFormatter({
'base.foo': logging.Formatter('FOO: %(message)s'),
'base.bar': logging.Formatter('BAR: %(message)s'),
},
logging.Formatter('%(message)s'),
))
logging.getLogger().addHandler(handler)
logging.getLogger('base.foo').error('Log from foo')
logging.getLogger('base.bar').error('Log from bar')
logging.getLogger('base.baz').error('Log from baz')
另一種方法是從它手動打開文件,並創建兩個流處理器具有不同的格式化。
很好的解決丹尼斯的出色解決方案。
Logging name system基於分層結構:
name
的潛在的時段分隔的層次值,像foo.bar.baz
(儘管它也可以是隻是普通foo
,例如)。 層次列表中進一步向下的記錄器是列表中較高位置的 記錄器的子項。例如,給定一個記錄器使用一個名稱的foo
,伐木者與foo.bar
,foo.bar.baz
名稱和foo.bam
是 所有的foo
後代。
例如,當您對某個記錄器使用setLevel()時,此級別也將應用於子記錄器。這就是爲什麼你可能希望你的格式化程序將用於記錄器,它也是兒童記錄器。例如,'one.two'
格式化程序也應該應用於'one.two.three'
記錄程序(如果沒有設置'one.two.three'
的格式化程序)。下面是做任務(Python 3的代碼)DispatchingFormatter的版本:
class DispatchingFormatter:
"""Dispatch formatter for logger and it's sub logger."""
def __init__(self, formatters, default_formatter):
self._formatters = formatters
self._default_formatter = default_formatter
def format(self, record):
# Search from record's logger up to it's parents:
logger = logging.getLogger(record.name)
while logger:
# Check if suitable formatter for current logger exists:
if logger.name in self._formatters:
formatter = self._formatters[logger.name]
break
else:
logger = logger.parent
else:
# If no formatter found, just use default:
formatter = self._default_formatter
return formatter.format(record)
例子:
handler = logging.StreamHandler()
handler.setFormatter(DispatchingFormatter({
'one': logging.Formatter('%(message)s -> one'),
'one.two': logging.Formatter('%(message)s -> one.two'),
},
logging.Formatter('%(message)s -> <default>'),
))
logging.getLogger().addHandler(handler)
print('Logger used -> formatter used:')
logging.getLogger('one').error('one')
logging.getLogger('one.two').error('one.two')
logging.getLogger('one.two.three').error('one.two.three') # parent formatter 'one.two' will be used here
logging.getLogger('other').error('other')
# OUTPUT:
# Logger used -> formatter used:
# one -> one
# one.two -> one.two
# one.two.three -> one.two
# other -> <default>
你知道如何做到這一點filehandler嗎?同樣的方式? – Henry
@亨利,是的,只需將第二個片段「StreamHandler()」更改爲「FileHandler(文件名)」即可。格式化程序本身可以應用於任何類型的處理程序。 –
優秀,優雅的解決方案。對於記錄,這也基於record.levelno,因此格式化程序字典鍵可以是'logging.DEBUG'而不是''base.foo'' – lorenzog