python 在代码中实现可选的日志记录器

utugiqy6  于 2023-02-03  发布在  Python
关注(0)|答案(7)|浏览(172)

我想在一个函数中实现一个可选的日志记录器,类似于:

def foo(arg1, arg2, arg3, logger=None):
    logger = logger or (lambda *x: None)

    ...
    self.logger.debug("The connection is lost.")

我希望日志记录发生在日志记录器存在的情况下。否则,日志记录器的调试将不会做任何事情。
基本上,最简单的实现方法是将每个调试语句嵌套在if logger块中,但当有许多调试语句时,这似乎很混乱。

tag5nh1u

tag5nh1u1#

选项很少:

    • 创建虚拟记录器(我的最爱):**
logger = logger or logging.getLogger('dummy') #  without configuring dummy before.
    • 创建具有一个级别null效果的虚拟对象**:
class DummyObject(object):
    def __getattr__(self, name):
        return lambda *args, **kwargs: None

logger = logger or DummyObject()
    • 在块中嵌套每个调试语句:**
if logger:
    logger.debug("abc")
z31licg0

z31licg02#

从Python 2.7开始,logging模块中包含了一个不执行任何操作的NullHandler

import logging      
logging.getLogger('foo').addHandler(logging.NullHandler())

有关配置库的日志记录,请参阅文档。

smdncfj3

smdncfj33#

这就是logging模块的作用。How to useCookbook
如果你真的想自己卷,我看到几个替代方案:

*self.logger属性。在构造对象时设置或从基类继承。每个对象都有自己的记录器,因此您可以选择性地记录每个示例。
*具有静态方法或独立模块的Logger类。可以具有不执行任何操作的默认方法,但用户可以在需要时自由地将其替换为真实的的处理程序。所有类访问同一对象或模块。失去粒度,但需要设置的工作较少。
*Decorators。在每个你想记录的方法上放一个@log('message', LEVEL),这样当方法被调用时会自动调用日志。相当干净,不太灵活。

r8uurelv

r8uurelv4#

默认情况下,当logging.getLogger被调用时,用来构造一个新的日志记录器的类是logging.Logger,它会默认地将propagate属性设置为True(文档来源)。
如果此属性[propagate]的计算结果为true,则记录到此记录器的事件除了传递到附加到此记录器的任何处理程序之外,还将传递到更高级别(祖先)记录器的处理程序。
因此,只要logger的父记录器中有一个有一些重要的处理程序,@fcs的答案就不会起作用,这是最有可能的情况,因为根logger是所有logger的父记录器,它几乎总是有StreamHandlerstderr流。
以下简单的修复方法将起作用:

import logging
null_logger = logging.getLogger('foo')
null_logger.addHandler(logging.NullHandler())  # read below for reason
null_logger.propagate = False
null_logger.error("error")  # This message will still go nowhere

请注意,添加logging.NullHandler是必要的,因为如果日志记录没有被任何处理程序处理,它将被logging.lastResort处理,logging.lastResort仍然会向stderr发送此消息(Python 3.2以后的行为)。

mutmk8jj

mutmk8jj5#

我认为您需要的是日志过滤,所以我的答案是如何简单地实现日志过滤。
Python的日志包已经做到了这一点,你有很多方法来进行日志过滤。
两种基本方式是:

  • 记录级过滤
  • 记录器名称筛选

它们都使用日志的配置,因此可以很容易地进行配置。
例如:

import logging

logging.basicConfig()  # easily setup a StreamHandler output

logging.getLogger("realm1").setLevel(logging.WARNING)
logging.getLogger("realm2").setLevel(logging.INFO)

def test():
    r1logger = logging.getLogger("realm1")
    r2logger = logging.getLogger("realm2")
    r1logger.info('r1 info')  # won't print
    r2logger.info('r2 info')  # will print

if __name__ == '__main__':
    test()

因此,除非您需要在运行时动态地本地更改日志记录策略,否则使用默认日志记录器并仔细配置日志记录就足够了。

apeeds0o

apeeds0o6#

你还可以做什么,它是更短的然后设置一个真实的的记录器与NullHander:

logger = Mock()
ig9co6j1

ig9co6j17#

当我想在有记录器的情况下记录一条消息时,我会定制一个函数来检查记录器,否则,我会打印消息,但是您可以删除else子句,如果没有记录器,它将不执行任何操作。

def emit_message(message: str, level: Optional[str] = None) -> None:
    allowed_levels = ['debug', 'info', 'warning', 'error', 'critical']
    if logging.getLogger().hasHandlers() and level in allowed_levels:
        method = getattr(logging, level)
        method(message)
    else:
        print(message)

相关问题