gpt4 book ai didi

python - Python 模块的正确日志记录实现

转载 作者:行者123 更新时间:2023-11-28 22:43:37 24 4
gpt4 key购买 nike

有谁知 Prop 有良好日志记录实现的模块的任何好的示例?

我一直在用几种不同的方式进行日志记录,但我不确定哪种方式最符合 Pythonic。

对于脚本,这就是我一直在做的:

import logging

LOGGER = logging.getLogger(__program__)
STREAM = logging.StreamHandler()
FORMATTER = logging.Formatter('(%(name)s) %(levelname)s: %(message)s')
STREAM.setFormatter(FORMATTER)
LOGGER.addHandler(STREAM)

def main():
LOGGER.warning('This is a warning message.')

这是在全局命名空间中执行的,我可以从任何地方调用 LOGGER

上述解决方案对于模块来说不是一个好主意,因为代码是在导入时执行的。因此,对于模块,我一直在尽早调用此 _logging() 函数来设置日志记录。

def _logging():
import logging

global logger
logger = logging.getLogger(__program__)
stream = logging.StreamHandler()
formatter = logging.Formatter('(%(name)s) %(levelname)s: %(message)s')
stream.setFormatter(formatter)
logger.addHandler(stream)

def main():
_logging()
logger.warning('This is a warning message.')

因为 logger 是全局的,我可以在任何需要的地方调用它。然而,pylint 发出了一个global-variable-undefined 警告。它被定义为 在模块级别未定义的全局变量记录器 当通过“全局”语句定义变量但变量未在模块范围内定义时使用但我不太确定为什么这是一个问题。

或者我应该尽早调用 _logger() 函数(减去全局函数),然后在需要的地方创建记录器吗?

def _logging():
import logging

logger = logging.getLogger(__program__)
stream = logging.StreamHandler()
formatter = logging.Formatter('(%(name)s) %(levelname)s: %(message)s')
stream.setFormatter(formatter)
logger.addHandler(stream)

def main():
_logging()
logger = logging.getLogger(__program__)
logger.warning('This is a warning message.')

最后一种技术似乎是最简洁的,尽管也是最乏味的,特别是因为我经常从几十个小类、函数、方法等中记录日志。是否有来自已经在该领域开辟道路的人员/模块的示例?

最佳答案

如果我没理解错的话,您正在分别配置每个模块中的日志记录。这将是不必要的,并且有悖于 logging 模块的设计。

我认为日志记录的关键是理解 logging module is a stateful object in you Python process .至少对我来说,在那种洞察力之后,在大多数情况下只有一种明显的方法可以进行日志记录。

您应该在程序开始时配置日志记录。定义处理程序、格式化程序等,配置将在整个程序中保留,只要它没有被显式覆盖。

所有进行日志记录的模块都可以在导入logging 后立即定义一个全局记录器。无需将其放入函数中。正如文档所建议的那样,根据模块名称(包括包路径)命名每个记录器是一种很好的做法:

import logging
logger = logging.getLogger(__name__)

了解程序中的记录器形成层次结构也很重要。默认情况下,记录器将记录传播给它们的 parent 。这意味着在底部有一个记录器(root)获取所有记录,除非你配置一些记录器来防止这种情况发生。通常只配置根记录器就足够了。

更具体一点,让我们编写一个包含两个模块的程序,one.pytwo.pyone.py 包含一个函数 main,它将成为程序的入口点。我们将使用 dictConfig 配置日志记录,这让我们可以很好地将日志记录配置与其余代码分开。我们会将配置字典放在单独的 YAML 文件中,如下所示:

# logging_config.yaml

version: 1

formatters:
brief:
format: '%(message)s'
default:
format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'

handlers:
console:
class: logging.StreamHandler
formatter: brief
stream: ext://sys.stdout
file:
class: logging.handlers.RotatingFileHandler
formatter: default
filename: example.log
maxBytes: 1024
backupCount: 3

loggers:
two:
level: INFO
handlers: [file]
propagate: False

root:
level: INFO
handlers: [console]

这些片段大部分改编自the documentation .在此配置中,我们定义来自记录器 two 的 INFO 级别以上的所有内容都记录到一个文件中。来自 two 的记录也不会进一步传播。如果根记录器的所有内容都高于 INFO 级别,则将其馈送到控制台。

one 模块的定义可以是这样的:

# one.py

import logging
import logging.config
import yaml

def configure_logging(filename):
with open(filename) as f:
config = yaml.load(f)
logging.config.dictConfig(config)

def main():
configure_logging('logging_config.yaml')

from two import func
logger = logging.getLogger(__name__)

logger.info('Starting the program')
func()
logger.info('Finished')

这里有一个棘手的细节是我们导入模块 two 并仅在设置配置后定义记录器。这样做是因为默认情况下 dictConfig 禁用所有现有的记录器。

最后,这里是模块 two 的定义:

# two.py
import logging

logger = logging.getLogger(__name__)

def func():
logger.info('Doing stuff')

现在,如果我们运行该程序,我们会得到以下输出:

>>> import one
>>> one.main()
Starting the program
Finished

日志文件 example.log 包含以下行:

2015-06-07 15:04:15 INFO     two             Doing stuff

可以在 Python 文档中找到日志记录的优秀示例:

关于python - Python 模块的正确日志记录实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30552351/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com