如何在python中使用不同的格式化程序和相同的日志记录处理程序

时间:2009-11-16 12:42:04

标签: python logging

是否可以使用多个记录器(即FileHandlerlogging.getLogger("base.foo"))登录到单个目标(即使用一个logging.getLogger("base.bar")),并为每个记录器使用不同的格式化程序。

根据我的理解,只能为每个句柄分配一个格式化程序。也许可以将格式化程序与记录器而不是处理程序相关联?

3 个答案:

答案 0 :(得分:16)

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

另一种方法是手动打开文件并使用不同的格式化程序从中创建两个流处理程序。

答案 1 :(得分:1)

很难解决卓越的Denis解决方案。

Logging name system基于层次结构:

  

name可能是一个以句点分隔的层次值,例如   foo.bar.baz(例如,它也可能只是普通foo)。   分层列表中较低位的记录器是   记录器在列表中更高。例如,给定一个带有名称的记录器   foo的{​​{1}},名称为foo.barfoo.bar.bazfoo.bam的记录器   foo的所有后代。

例如,当您setLevel()某个记录器时,此级别也将应用于子记录器。这就是为什么你可能想要你的格式化程序也将用于记录器和它的儿童记录器。例如,'one.two'格式化程序也应该应用于'one.two.three'记录器(如果没有'one.two.three'设置的格式化程序)。这里有DispatchingFormatter的版本(Python 3代码):

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>

答案 2 :(得分:0)

为您工作吗?不同的日志级别输出格式和不同的日志目标,文件vs标准输出(以及每个目标不同的级别):

    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)

    format_for_stdout = logging.Formatter('%(message)s')
    format_for_logfile = logging.Formatter('%(asctime)s: %(name)s: %(levelname)s: %(message)s')

    handler_logfile = logging.FileHandler('my_awesome_logs.log')
    handler_logfile.setLevel(logging.DEBUG)
    handler_logfile.setFormatter(format_for_logfile)

    handler_stdout = logging.StreamHandler()
    handler_stdout.setLevel(logging.INFO)
    handler_stdout.setFormatter(format_for_stdout)

    logger.addHandler(handler_logfile)
    logger.addHandler(handler_stdout)

    logging.addLevelName(logging.INFO, logging.getLevelName(logging.INFO))
    logging.addLevelName(logging.ERROR, logging.getLevelName(logging.ERROR))