美文网首页
Python 日志模块 logging

Python 日志模块 logging

作者: BeeBee生信 | 来源:发表于2024-04-21 23:43 被阅读0次

Python 日志模块 logging 是比较好用的,但似乎官方文档写得不是很好。这里分享一下我是怎么使用的。基础的就不介绍了,只介绍 2 个知识点:第一,日志信息如何从模块传递给 main 脚本;第二,如何使用 filter 选择性处理日志。
logging 日志包含几个互相关联的部分:

  • Logger - 产生 LogRecord (日志记录对象)
  • Handler - 处理日志,如输出到文件
  • Formatter - 将日志信息格式化
  • Filter - 按条件过滤或处理日志

由于 Logger 是会按层级传递日志记录的,比如名字为 A.B 的 Logger 会将日志传递给名字为 A 的 Logger,并且所有 Logger 都会将消息往上传递,直到 root Logger,利用这个机制可以将日志信息从模块传递给 main,而不是将 Logger 本身在不同模块传递。

下面是第一个知识点的示例代码,假如有个 main.py 它会调用 child.py 的函数并且 child.py 产生的日志由 main.py 处理。

$ ls
child.py  main.py  __pycache__

child.py 代码如下。

import logging

_logger = logging.getLogger(name=__name__)

def create_log():
    _logger.info("info from child.py")
    _logger.error("child.py has some problem")

if __name__ == "__main__":
    pass

child.py 使用 getLogger 函数创建了个 Logger 并使用文件名命名,这是推荐的创建 Logger 对象方法。之后在 create_log 函数创建了一条 INFO 和一条 ERROR 日志。

main.py 代码如下。

import logging

from child import create_log

logger = logging.getLogger()
logger.setLevel("DEBUG")
formater = logging.Formatter(fmt="[%(levelname)s %(asctime)s] %(message)s")
handler = logging.StreamHandler()
handler.setFormatter(formater)
handler.setLevel("DEBUG")
logger.addHandler(handler)

logger.info("info from main.py")
logger.debug("debug from main.py")
logger.warning("warning from main.py")

create_log()

main.py 同样使用 getLogger 函数创建 Logger 但是不输入名字,此时获取的是 root Logger,因此 child.py 的 Logger 会将日志记录传递过来。

使用 setLevel 设置了 Logger 日志等级,低于等级设置的日志将被忽略,比如设置为 ERROR 时那么由于 WARNING, INFO, DEBUG 都是低于 ERROR 等级的,这些日志记录会被忽略。

使用 Formatter 定义了日志信息格式,其格式为 [等级 时间] 信息
使用 StreamHandler 创建一个流 Handler 默认流为 stderr 因此会将日志信息输出到标准错误输出。将 Formatter 添加到这个 Handler 并设置 Handler 的日志等级,Handler 将会按照规定格式输出高于等级的日志信息到标准错误输出。
将 Handler 添加到 root Logger 就能处理所有的日志信息了。

运行 main.py 后得到以下屏幕输出。可以看到 child.py 的日志信息顺利传递给了 main.py 并得到处理。

$ python main.py
[INFO 2024-04-22 20:02:34,558] info from main.py
[DEBUG 2024-04-22 20:02:34,558] debug from main.py
[WARNING 2024-04-22 20:02:34,558] warning from main.py
[INFO 2024-04-22 20:02:34,558] info from child.py
[ERROR 2024-04-22 20:02:34,559] child.py has some problem

如果将 Formatter 格式修改为输出模块名 "[%(levelname)s %(module)s] %(message)s" 那么日志将显示为。

$ python main.py
[INFO main] info from main.py
[DEBUG main] debug from main.py
[WARNING main] warning from main.py
[INFO child] info from child.py
[ERROR child] child.py has some problem

第二个知识点示例代码如下。在 main.py 写一个自己的 Filter 类,继承自 logging.Filter 在里面覆盖 filter 方法,设置自己的过滤条件。在示例里设置输出 message 长度小于或等于 20 的日志。

import logging

class MyFilter(logging.Filter):
    def __init__(self, name: str = "") -> None:
        super().__init__(name)
        
    def filter(self, record: logging.LogRecord) -> bool:
        if len(record.msg) > 20:
            return False
        else:
            return True

logger = logging.getLogger()
logger.setLevel("DEBUG")
formater = logging.Formatter(fmt="[%(levelname)s %(module)s] %(message)s")
handler = logging.StreamHandler()
handler.setFormatter(formater)
handler.setLevel("DEBUG")
# 添加 Filter 到 handler
handler.addFilter(MyFilter())
logger.addHandler(handler)

logger.info("a long long long long long info")
logger.info("a short info")

运行输出如下。

$ python main.py
[INFO main] a short info

可以看到实现了想要的过滤效果。

使用 Filter 也可以修改日志,比如。

class MyFilter(logging.Filter):
    def __init__(self, name: str = "") -> None:
        super().__init__(name)

    def filter(self, record: logging.LogRecord) -> bool:
        if len(record.msg) > 20:
            record.msg = ">20"
        else:
            record.msg = "<=20"
        return True

那么 main.py 运行输出为。

$ python main.py
[INFO main] >20
[INFO main] <=20

相关文章

网友评论

      本文标题:Python 日志模块 logging

      本文链接:https://www.haomeiwen.com/subject/ifsbxjtx.html