2009-11-16 19 views
14

是否可以使用多個日誌記錄器(即logging.getLogger("base.foo")logging.getLogger("base.bar"))登錄到單個目標(即使用一個FileHandler),併爲每個日誌記錄器使用不同的格式化器。如何在python中使用不同的格式器和相同的日誌處理程序

根據我的理解,只能將一個格式化程序分配給每個句柄。也許有可能將格式化程序與記錄程序而不是處理程序相關聯?

回答

11

根據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') 

另一種方法是從它手動打開文件,並創建兩個流處理器具有不同的格式化。

+0

優秀,優雅的解決方案。對於記錄,這也基於record.levelno,因此格式化程序字典鍵可以是'logging.DEBUG'而不是''base.foo'' – lorenzog

1

很好的解決丹尼斯的出色解決方案。

Logging name system基於分層結構:

name的潛在的時段分隔的層次值,像 foo.bar.baz(儘管它也可以是隻是普通foo,例如)。 層次列表中進一步向下的記錄器是列表中較高位置的 記錄器的子項。例如,給定一個記錄器使用一個名稱的foo ,伐木者與foo.barfoo.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> 
+0

你知道如何做到這一點filehandler嗎?同樣的方式? – Henry

+0

@亨利,是的,只需將第二個片段「StreamHandler()」更改爲「FileHandler(文件名)」即可。格式化程序本身可以應用於任何類型的處理程序。 –

相關問題