一个使用标准库日志模块检查问题的Flake8插件。
项目描述
A Flake8 plugin that checks for issues using the standard library logging module.
有关简要概述和背景信息,请参阅介绍博客文章。
需求
支持Python 3.8到3.12。
安装
首先,使用
pip安装
python -m pip install flake8-logging
其次,如果您定义了Flake8的
检查Django项目? 查看我的书籍Boost Your Django DX,其中涵盖了Flake8和其他许多代码质量工具。
规则
LOG001 使用 logging.getLogger() 实例化记录器
请注意,记录器绝不应该直接实例化,而应该始终通过模块级函数 logging.getLogger(name) 进行实例化。
直接实例化的记录器不会被添加到记录器树中。这意味着它们绕过所有配置,其消息只发送到最后的处理程序。这可能意味着它们的消息被错误地过滤、格式化,并且只发送到stderr。可能,这样的消息在你的日志工具中不可见,你也不会被通知问题。
使用getLogger()来正确实例化记录器。
此规则检测任何模块级别的Logger()调用。
失败的示例
import logging
logger = logging.Logger(__name__)
已纠正
import logging
logger = logging.getLogger(__name__)
LOG002 使用 __name__ 和 getLogger()
日志记录文档建议此模式
logging.getLogger(__name__)
__name__ 是完全限定的模块名称,例如 camelot.spam,这是记录器名称的预期格式。
此规则检测类似模块级别的错误使用双下划线常量
__cached__ - 模块编译版本的路径名,例如 camelot/__pycache__/spam.cpython-311.pyc。
__file__ - 模块的路径名,例如 camelot/spam.py。
失败的示例
import logging
logger = logging.getLogger(__file__)
已纠正
import logging
logger = logging.getLogger(__name__)
LOG003 extra 键 '<key>' 与 LogRecord 属性冲突
extra 文档说明
传递给 extra 的字典中的键不应与日志系统使用的键冲突。
这种冲突在运行时会导致错误,例如
KeyError: "Attempt to overwrite 'msg' in LogRecord"
不幸的是,只有当消息没有被级别过滤掉时,才会引发此错误。因此,如果测试运行在有限的日志配置下,它们可能不会遇到检查。
此规则通过检查与LogRecord 属性匹配的键来检测这种冲突。
失败的示例
import logging
logger = logging.getLogger(__name__)
response = acme_api()
logger.info("ACME Response", extra={"msg": response.msg})
已纠正
import logging
logger = logging.getLogger(__name__)
response = acme_api()
logger.info("ACME Response", extra={"response_msg": response.msg})
LOG004 避免在异常处理程序外部使用 exception()
此函数应仅从异常处理程序中调用。
在异常处理程序外部调用 exception() 会附加 None 异常信息,导致消息混乱
>>> logging.exception("example")
ERROR:root:example
NoneType: None
使用 error() 代替。要记录捕获的异常,请将其传递给 exc_info 参数。
此规则检测异常处理程序外部的 exception() 调用。
失败的示例
import logging
response = acme_api()
if response is None:
logging.exception("ACME failed")
已纠正
import logging
response = acme_api()
if response is None:
logging.error("ACME failed")
LOG005 在异常处理程序中使用 exception()
在异常处理程序中,exception() 方法比 logger.error() 更可取。该 exception() 方法会自动捕获异常,而 error() 需要显式在 exc_info 参数中传递。两种方法都使用 ERROR 级别进行日志记录。
此规则检测异常处理程序中的 error() 调用,排除那些具有假的 exc_info 参数的调用。
失败的示例
try:
acme_api()
except AcmeError as exc:
logger.error("ACME API failed", exc_info=exc)
已纠正
try:
acme_api()
except AcmeError:
logger.exception("ACME API failed")
或者,如果异常信息确实没有提供信息
try:
acme_api()
except DuplicateError:
logger.error("ACME Duplicate Error", exc_info=False)
LOG006 exception() 的冗余 exc_info 参数
exception() 方法自动捕获异常,使 exc_info 参数成为冗余。
此规则检测到带有 exc_info 参数(为真或捕获的异常对象)的异常处理程序中的 exception() 调用。
失败的示例
try:
acme_api()
except AcmeError:
logger.exception("ACME API failed", exc_info=True)
已纠正
try:
acme_api()
except AcmeError:
logger.exception("ACME API failed")
LOG007 使用 error() 而不是 exception() 并设置 exc_info=False
exception() 方法自动捕获异常。通过设置 exc_info=False 禁用此功能等同于使用 error(),这更清晰且不需要 exc_info 参数。
此规则检测到具有 exc_info 参数(为假)的 exception() 调用。
失败的示例
logger.exception("Left phalange missing", exc_info=False)
已纠正
logger.error("Left phalange missing")
LOG008 warn() 已弃用,请使用 warning() 代替
warn() 方法是一个弃用的、未记录的 warning() 别名。应始终使用 warning()。该方法在 Python 2.7 中弃用,在提交 04d5bc00a2 中弃用,并在 Python 3.13 中移除,在提交 dcc028d924 中移除。
此规则检测到对 warn() 的调用。
失败的示例
logger.warn("Cheesy puns incoming")
已纠正
logger.warning("Cheesy puns incoming")
LOG009 WARN 未记录文档,请使用 WARNING 代替
WARN 常量是 WARNING 的未记录别名。虽然它没有被弃用,但在文档中根本未提及,因此应始终使用记录的 WARNING。
此规则检测对 WARN 的任何导入或访问。
失败的示例
import logging
logging.WARN
已纠正
import logging
logging.WARNING
LOG010 exception() 不接受异常
与其他日志记录器方法一样,exception() 方法将字符串作为其第一个参数。一个常见的误解是将异常传递给它。这样做是多余的,因为 exception() 已经会捕获异常对象。这还可能导致日志消息不清楚,因为日志记录器将对异常调用 str(),这并不总是产生合理的消息。
此规则检测到将当前异常处理程序捕获变量作为第一个参数的 exception() 调用。
失败的示例
try:
shuffle_deck()
except Exception as exc:
logger.exception(exc)
已纠正
try:
shuffle_deck()
except Exception:
logger.exception("Failed to shuffle deck")
LOG011 避免预格式化日志消息
日志记录器方法支持用于 日志变量数据 的字符串格式化,例如
logger.info("Couldn’t chop %s", vegetable)
日志聚合工具,如 Sentry,可以根据未格式化的消息模板分组消息。使用来自 f-string 的预格式化消息会阻止这种情况发生。工具必须依赖不完美的启发式方法,这可能导致重复的组。
此外,日志框架跳过不会记录的消息的格式化。使用来自 f-string 的预格式化字符串没有这样的优化。当您有大量通常会被跳过的日志时,这种开销可能会累积。
此规则检测到具有 msg 参数的日志记录器方法调用之一
一个 f-string
对 str.format() 的调用
用于模运算符(%)的字符串
非字符串与字符串的连接
错误示例
logging.error(f"Couldn’t chop {vegetable}")
logging.error("Couldn’t chop {}".format(vegetable))
logging.error("Couldn’t chop %s" % (vegetable,))
logging.error("Couldn’t chop " + vegetable)
已纠正
logging.error("Couldn’t chop %s", vegetable)
LOG012格式错误:存在<n> <style>占位符但缺少<m>参数
日志方法支持多种字符串格式化选项。如果消息中的参数数量与提供的参数不匹配,则调用将出错
>>> logging.info("Sent %s to %s", letter)
--- Logging error ---
Traceback (most recent call last):
File "/.../logging/__init__.py", line 1110, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
...
File "/.../logging/__init__.py", line 377, in getMessage
msg = msg % self.args
~~~~^~~~~~~~~~~
TypeError: not enough arguments for format string
Call stack:
File "<stdin>", line 1, in <module>
Message: ' %s to %s'
Arguments: ('Red Letter',)
这仅在日志器启用时才会发生,因为禁用日志器时日志器不执行字符串格式化。因此,配置更改可以揭示此类错误。
此外,如果没有提供参数,参数化消息将默默地未格式化
>>> logging.info("Sent %s to %s")
INFO:root:Sent %s to %s
此规则检测消息参数数量与提供的参数之间的不匹配。目前,它仅支持至少有一个参数的%样式格式化。
错误示例
logging.info("Blending %s")
logging.info("Blending %s", fruit.name, fruit.size)
已纠正
logging.info("Blending %s of size %r", fruit.name, fruit.size)
LOG013格式错误:缺少/未引用的键:<keys>
当使用命名的%样式格式化时,如果消息引用了一个缺少的键,则调用将出错
>>> logging.error("Hi %(name)s", {"nam": "hacker"})
--- Logging error ---
Traceback (most recent call last):
File "/.../logging/__init__.py", line 1160, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
...
File "/.../logging/__init__.py", line 392, in getMessage
msg = msg % self.args
~~~~^~~~~~~~~~~
KeyError: 'name'
Call stack:
File "<stdin>", line 1, in <module>
Message: 'Hi %(name)s'
Arguments: {'nam': 'hacker'}
这仅在日志器启用时才会发生,因为禁用日志器时日志器不执行字符串格式化。因此,配置更改可以揭示此类错误。
此规则检测消息参数名称与提供的参数之间的不匹配。它仅在存在参数的字典字面量参数时才有效。
失败的示例
logging.info("Blending %(fruit)s", {"froot": froot})
logging.info("Blending %(fruit)s", {"fruit": fruit, "colour": "yellow"})
已纠正
logging.info("Blending %(fruit)s", {"fruit": fruit})
LOG014避免在异常处理程序外使用exc_info=True
在异常处理程序外使用exc_info=True会将None附加为异常信息,导致消息混乱
>>> logging.warning("Uh oh", exc_info=True)
WARNING:root:Uh oh
NoneType: None
此规则检测在异常处理程序外使用exc_info=True的日志调用。
失败的示例
import logging
logging.warning("Uh oh", exc_info=True)
已纠正
import logging
logging.warning("Uh oh")
LOG015避免在根日志器上进行日志调用
使用根日志器意味着您的消息没有源信息,这使得它们对调试不太有用。最好始终创建一个日志器对象,通常使用
logger = logging.getLogger(__name__)
如果您确实需要根日志器,请使用logging.getLogger(None)。
此规则检测对logging模块对象直接调用日志方法。
错误示例
import logging
logging.info("hello world")
from logging import info
info("hello world")
已纠正
import logging
logger = logging.getLogger(__name__)
logger.info("hello world")
项目详细信息
下载文件
下载您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。
源分布
构建分布
flake8-logging-1.6.0.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 6ad85b386f3e5a65f44ea150dcc878920150813252cded0af7bc41f50be4082d |
|
MD5 | 2ac3694783db2456bb24dc80c679f435 |
|
BLAKE2b-256 | da1ca8b2ab535a6559c60ae925f829153c90145f6cd7380b2b18fb3d05b77c52 |