logger 应该是参数还是全局变量?

Should logger be an argument or a global variable?

在阅读 Python 代码时,我通常会看到以下两个约定之一:

def something(logger):
  logger.info('doing something')

或:

LOGGER = logging.getLogger(__NAME__)
def something():
  LOGGER.info('doing something')

前者是否有任何优势,即线程安全而另一个则不是?还是纯粹的文体差异?

如果需要 固定 记录器,请使用全局记录器:

LOGGER = logging.getLogger('stuff.do')

# logger depends on what we are
def do_stuff(operation: Callable):
    LOGGER.info('will do stuff')
    operation()
    LOGGER.info('just did stuff')

do_stuff(add_things)
do_stuff(query_things)

这通常用于记录共享操作以进行诊断。例如,Web 服务器将记录创建和销毁线程。

如果要更改 记录器,请使用记录器参数:

# logger depends on what we do
def do_stuff(operation: Callable, logger: Logger):
    logger.info('will do stuff')
    operation()
    logger.info('just did stuff')

do_stuff(add_things, logging.getLogger('add'))
do_stuff(query_things, logging.getLogger('query'))

这通常用于记录可配置操作以进行记账。例如,Web 服务器会记录不同种类 的请求及其结果。


使用哪一个完全取决于记录器的选择是取决于全局数据还是本地数据。

如果可以全局决定记录器的选择,那么这样做可以避免通过记录器传递污染函数签名。这改进了模块化,因为您可以 add/remove logging 调用而不更改其他代码。使用日志记录查找错误时,您可能希望将日志记录添加到可疑代码部分并将其从经过验证的代码部分中删除。

如果记录器的选择取决于本地状态,则传递记录器或它们的名称通常是唯一的选择。使用日志记录来记录正在发生的事情时,有时您希望稍后添加新的操作主题。


使用这两种方法都没有运行时或安全方面的优势,除了可以避免传递信息的操作。 logging 模块设计为 thread-safe:

Thread Safety

The logging module is intended to be thread-safe without any special work needing to be done by its clients. It achieves this though using threading locks; there is one lock to serialize access to the module’s shared data, and each handler also creates a lock to serialize access to its underlying I/O.

完全等同于为同一个记录器创建一个新的"instance",或者为同一个记录器创建一个别名:

>>> a = logging.getLogger('demo')
>>> b = a
>>> c = logging.getLogger('demo')
>>> a is b is c
True