Python logging 模块详解

news2024/11/26 13:57:59

pkYsjL8.jpg

Python 的 logging 模块提供了一个强大而灵活的日志系统。它是 Python 标准库的一部分,因此可以在任何 Python 程序中使用。logging 模块提供了许多有用的功能,包括日志消息的级别设置、日志消息的格式设置、将日志消息输出到不同的目标,以及处理复杂的日志系统配置。

logging 组成部分

在 logging 模块中主要有四部分:Logger、Handler、Filter 和 Formatter。下面分别对这四部分进行介绍。

Logger

Logger 就是程序可以直接调用的一个日志接口,可以直接向 Logger 写入日志信息。但是 Logger 并不是直接实例化使用的,而是通过 logging.getLogger(name) 来获取对象,事实上 Logger 对象是单例模式,并且 logging 是多线程安全的,也就是在程序的任何地方使用的同名的 Logger 都指向了一个实例。

Handler

Handler 作用于 Logger,是真正的日志处理程序,一个 Logger 可以配置多个 Handler。同样的,一个 Handler 也可以作用在多个 Logger 上。每个 Handler 同样有一个日志级别,也就是说 Logger 可以根据不同的日志级别将日志传递给不同的 Handler,当然也可以相同的级别传递给多个 Handler 这就根据需求来灵活的设置了。

Handler 既有系统定义的,比如将日志输出到标准输出 stdout。也可以自定义日志处理器,比如将日志发送到邮件或者第三方的日志存储介质等。

Filter

Filter 提供了更细粒度的判断,来决定日志是否需要打印。原则上 Handler 获得一个日志就必定会根据日志级别进行统一处理,但是如果 Handler 拥有一个 Filter 的时候就可以对日志进行额外的处理和判断。例如 Filter 能够对来自特定源的日志进行拦截甚至进行修改(包括日志级别的修改,修改后再进行级别判断)。

Logger 和 Handler 都可以安装 Filter 甚至可以安装多个 Filter 串联起来。

Formatter

Formatter 指定了最终某条记录打印的格式。Formatter 会将传递来的信息拼接成一条具体的字符串,默认情况下 Formatter 只会将信息 %(message)s 直接打印出来。Formatter 中有一些自带的属性可以使用,如下表格:

pkYsOQP.png

注意的是一个 Handler 只能拥有一个 Formatter。

日志级别

作用

日志级别是一个用于控制和过滤日志消息的重要工具。日志级别可以帮助更有效地管理日志,只看到需要的信息,而忽略不必要的细节。

这个功能在开发和运行大型程序时非常有用。例如,在开发过程中,你可能需要 DEBUG 级别的日志来找出问题。但是在生产环境中,这么详细的日志可能会占用大量的磁盘空间,而且大部分信息可能都是不必要的。因此,在生产环境中,你可能只需要 WARNING 或者 ERROR 级别的日志。

级别

在记录日志时, 日志消息都会关联一个级别(“级别”本质上是一个非负整数)。系统默认提供了6个级别,它们分别是:

级别对应值
CRITICAL50
ERROR40
WARNING30
INFO20
DEBUG10
NOTSET0

继承关系和处理流程

Logger 继承关系

Logger 对象是有父子关系的,当没有父 Logger 对象时它的父对象是 root,当拥有父对象时父子关系会被修正。举个例子,logging.getLogger("abc.xyz") 会创建两个 Logger 对象,一个是 abc 父对象,一个是 xyz 子对象,同时 abc 没有父对象,所以它的父对象是 root。但是实际上 abc 是一个占位对象(虚的日志对象),可以没有 Handler 来处理日志。但是 root 不是占位对象,如果某一个日志对象打日志时,它的父对象会同时收到日志,所以有些使用者发现创建了一个 Logger 对象时会打两遍日志,就是因为他创建的 Logger 打了一遍日志,同时 root 对象也打了一遍日志。

1)level的继承

子 Logger 写日志时,优先使用本身设置了的 level;如果没有设置,则逐层向上级父 Logger 查询,直到查询到为止。最极端的情况是,使用 root Logger 的默认日志级别 logging.WARNING。

参考源码:

def getEffectiveLevel(self):
    """
    Get the effective level for this logger.

    Loop through this logger and its parents in the logger hierarchy,
    looking for a non-zero logging level. Return the first one found.
    """
    logger = self
    while logger:
        if logger.level:
            return logger.level
        logger = logger.parent
    return NOTSET

2)Handler 的继承

先将日志对象传递给子 Logger 的所有 Handler 处理,处理完毕后,如果该子 Logger 的 propagate 属性没有设置为 False,则将日志对象向上传递给第一个父 Logger,该父 Logger 的所有 Handler 处理完毕后,如果它的 propagate 也没有设置为 False,则继续向上层传递,以此类推。最终的状态,要么遇到一个 Logger,它的 propagate 属性设置为了 False;要么一直传递直到 root Logger 处理完毕。

注意,Handler 不是真正的(类)继承,只是“行为上的继承”,也就是子 Logger 并没有没有绑定父类的 Handler。

参考源码:

def callHandlers(self, record):
    """
    Pass a record to all relevant handlers.

    Loop through all handlers for this logger and its parents in the
    logger hierarchy. If no handler was found, output a one-off error
    message to sys.stderr. Stop searching up the hierarchy whenever a
    logger with the "propagate" attribute set to zero is found - that
    will be the last logger whose handlers are called.
    """
    c = self
    found = 0
    while c:
        for hdlr in c.handlers:
            found = found + 1
            if record.levelno >= hdlr.level:
                hdlr.handle(record)
        if not c.propagate:
            c = None  # break out
        else:
            c = c.parent
    if (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:
        sys.stderr.write("No handlers could be found for logger"
                         " \"%s\"\n" % self.name)
        self.manager.emittedNoHandlerWarning = 1
日志记录的处理流程

pkYsXsf.png

在一个给定继承关系的日志配置中,如果其中任何一个 Logger 进行了日志记录,会经历下面几个步骤:

  • 首先这条记录会被 Logger 本身的 Handler 进行处理。
    • 检查日志级别是否满足最低要求
    • 是否有 Filter 进行更细粒度的处理
    • 用什么样的 Formatter 作为打印格式
  • 默认 Logger 的 propagate 值为 True,也就是在自己进行日志处理的同时,也会把这条日志代理到其父 Logger 进行处理,一直会被代理到 root Logger。

参考下面代码:

import logging

# root logger
logging.basicConfig(level=logging.INFO)

# parent logger
logger_parent = logging.getLogger("parent")
handler = logging.StreamHandler()
handler.setLevel(logging.CRITICAL)  # Set the handler's level to CRITICAL
logger_parent.addHandler(handler)
logger_parent.setLevel(level=logging.CRITICAL)

# child logger
logger_child = logging.getLogger("parent.child")
logger_child.addHandler(logging.StreamHandler())
logger_child.setLevel(level=logging.INFO)

if __name__ == "__main__":
    logger_child.info("Hi")

其输出结果为:

Hi
INFO:parent.child:Hi

在上面代码中有三个日志记录器,其继承关系分别是 logger_child 继承 logger_parent 继承 root。

每个日志记录器上设置的日志级别,比如代码 logger_parent.setLevel(level=logging.CRITICAL) 不会影响记录器对日志的代理关系,其影响的是这个记录器在使用时是否处理程序中的日志记录。比如 logger_parent 在使用的时候会忽略 CRITICAL 级别之下的日志。

在上述程序中:

  • Hi 这条日志首先会被 logger_child 的 Handler 进行处理。
  • 然后这条记录会被代理到 logger_parent,logger_parent 的 handler 会对这条日志做处理,由于 logger_parent 的 Handler 是 CRITICAL 级别的,因此忽略这条日志。
  • 接下来这条日志会继续被代理到 root,root 的 Handler 接下来会继续处理这条日志。

因此日志是否会被向上代理,取决于 Logger 上的 propagate 属性。

使用实例

基本日志打印

在下面的例子中,默认的日志级别是 WRANING,因此只有最后一个日志被打印出来。

import logging

logging.debug('this is debug message')
logging.info('this is info message')
logging.warning('this is warning message')

# 打印结果:WARNING:root:this is warning message
root 日志配置

在下面的例子中,root 日志记录器可以通过 logging.basicConfig 进行配置,比如配置其默认的日志级别,Handler 或者输出格式等。

import logging

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(message)s')

if __name__ == '__main__':
    logging.debug('this is debug message')
    logging.info('this is info message')
    logging.warning('this is warning message')

'''''
结果:
2024-06-06 17:10:43,873 - root - this is debug message
2024-06-06 17:10:43,873 - root - this is info message
2024-06-06 17:10:43,873 - root - this is warning message
'''
将日志同时输出到文件和 stdout

在下面的配置中,定义了一个文件 Handler 和标准输出 Handler,并将其应用到 logger 日志处理器上,这样日志会同时被输出到文件和标准输出中。

如果你用 FileHandler 写日志,文件的大小会随着时间推移而不断增大。为了避免这种情况出现,可以在你的生成环境中使用 RotatingFileHandler 替代 FileHandler 做日志回滚。

import logging

logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)

# 文件 Handler
file_handler = logging.FileHandler("log.txt")
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

# 标准输出 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 在 logger 中应用这两个 Handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
使用适配器扩展

在下面的例子中通过 logging 提供的 LoggerAdapter 方法可以对日志的字段进行扩展,输出的时候通过适当的 Formatter 进行扩展字段的输出。

import logging

format_str = '%(levelname)s %(filename)s %(document_id)s %(message)s'
logging.basicConfig(level=logging.INFO, format=format_str)

logger = logging.getLogger(__name__)

extra = {'document_id': 'doc id'}
logger = logging.LoggerAdapter(logger, extra)

logger.info({'name': 'Xiao Ming'})

# INFO example_use_adapter.py doc id {'name': 'Xiao Ming'}
捕捉异常并使用 traceback 记录

当程序出现错误的时候,在使用 Logger 进行记录的时候通过设置参数 exc_info=True 可以在日志中记录详细的报错信息。

import logging

logger = logging.getLogger(__name__)

try:
    open('/path/to/does/not/exist', 'rb')
except Exception as e:
    logger.error('Failed to open file', exc_info=True)

'''
Failed to open file
Traceback (most recent call last):
  File "/Users/crown/Projects/python101/playground/logging_watchtower/example_traceback.py", line 6, in <module>
    open('/path/to/does/not/exist', 'rb')
FileNotFoundError: [Errno 2] No such file or directory: '/path/to/does/not/exist'
'''
通过配置文件

如果日志的配置比较复杂,可以考虑通过外置配置文件的方式进行 logging 配置,logging 模块支持 json 格式和 yaml 两种格式的配置文件,通过配置文件可以更清晰的进行负责日志的配置。

下面是通过 json 配置文件的方式对 logging 进行配置。

{
  "version": 1,
  "disable_existing_loggers": false,
  "formatters": {
    "simple": {
      "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    }
  },
  "handlers": {
    "console": {
      "class": "logging.StreamHandler",
      "level": "DEBUG",
      "formatter": "simple",
      "stream": "ext://sys.stdout"
    },
    "info_file_handler": {
      "class": "logging.handlers.RotatingFileHandler",
      "level": "INFO",
      "formatter": "simple",
      "filename": "info.log",
      "encoding": "utf8"
    }
  },
  "loggers": {
    "my_module": {
      "level": "ERROR",
      "handlers": [
        "info_file_handler"
      ],
      "propagate": "no"
    }
  },
  "root": {
    "level": "INFO",
    "handlers": [
      "console",
      "info_file_handler"
    ]
  }
}

在 python 中读取配置文件:

{
  "version": 1,
  "disable_existing_loggers": false,
  "formatters": {
    "simple": {
      "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    }
  },
  "handlers": {
    "console": {
      "class": "logging.StreamHandler",
      "level": "DEBUG",
      "formatter": "simple",
      "stream": "ext://sys.stdout"
    },
    "info_file_handler": {
      "class": "logging.handlers.RotatingFileHandler",
      "level": "INFO",
      "formatter": "simple",
      "filename": "info.log",
      "encoding": "utf8"
    }
  },
  "loggers": {
    "my_module": {
      "level": "ERROR",
      "handlers": [
        "info_file_handler"
      ],
      "propagate": "no"
    }
  },
  "root": {
    "level": "INFO",
    "handlers": [
      "console",
      "info_file_handler"
    ]
  }
}

参考文档

[1] python基础学习十 logging模块详细使用 https://www.cnblogs.com/louis-w/p/8567434.html
[2] github django logging https://github.com/django/django/blob/1586a09b7949bbb7b0d84cb74ce1cadc25cbb355/django/utils/log.py#L18

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】GLM4-9B-Chat大模型/GLM-4V-9B多模态大模型概述、原理及推理实战

​​​​​​​ 目录 一、引言 二、模型简介 2.1 GLM4-9B 模型概述 2.2 GLM4-9B 模型架构 三、模型推理 3.1 GLM4-9B-Chat 语言模型 3.1.1 model.generate 3.1.2 model.chat 3.2 GLM-4V-9B 多模态模型 3.2.1 多模态模型概述 3.2.2 多模态模型实践 四、总结 一、引言…

《精通ChatGPT:从入门到大师的Prompt指南》大纲目录

第一部分&#xff1a;入门指南 第1章&#xff1a;认识ChatGPT 1.1 ChatGPT是什么 1.2 ChatGPT的应用领域 1.3 为什么需要了解Prompt 第2章&#xff1a;Prompt的基本概念 2.1 什么是Prompt 2.2 好Prompt的特征 2.3 常见的Prompt类型 第二部分&#xff1a;Prompt设计技巧 第…

SOA的发展历史

1.SOA的发展历程 回顾SOA发展历程&#xff0c;我们把其大致分为了三个阶段&#xff0c;下面将分别介绍每个阶段的重要标准和规范。 1.1.萌芽阶段 这一阶段以XML技术为标志&#xff0c;时间大致从20世纪90年代末到21世纪初。XML系W3C所建&#xff0c;源自流行的标准通用标记语…

多表连接查询和子查询

一、连接查询 连接查询是SQL语言最强大的功能之一&#xff0c;它可以执行查询时动态的将表连接起来&#xff0c;然后从中查询数据。 1.1、连接两表的方法 在SQL中连接两表可以有两种方法&#xff0c;一种是无连接规则连接&#xff0c;另一种是有连接规则连接。 无连接规则连…

多卡聚合智能融合通信设备在无人机无线视频传输应用

无人驾驶飞机简称“无人机”&#xff0c;是利用(无线电)遥控设备和自备的程序控制装置操纵的不载人飞行器&#xff0c;现今无人机在航拍、农业、快递运输、测绘、新闻报道多个领域中都有深度的应用。 无人机无线视频传输保证地面人员利用承载的高灵敏度照相机可以进行不间断的画…

Accelerate 笔记:保存与加载文件

保存和加载模型、优化器、随机数生成器和 GradScaler 使用 save_state() 将上述所有内容保存到一个文件夹位置使用 load_state() 加载之前通过 save_state() 保存的状态通过使用 register_for_checkpointing()&#xff0c;可以注册自定义对象以便自动从前两个函数中存储或加载 …

【推荐】用scss循环zoom缩放比例,解决可视化大屏在不同分辨率屏幕下的适配问题

方法1&#xff1a; 指定几种常规屏幕宽度&#xff08;用这种方式就必须要强制用户全屏查看页面&#xff0c;在固定的宽度下才能达到比较不错的显示效果&#xff09; // 适配不同分辨率的页面---------------------------------------- html {overflow: hidden;width: 1920px;…

AR眼镜定制开发_在AR眼镜中实现ChatGPT功能

AR眼镜定制方案中&#xff0c;需要考虑到强大的算力、轻巧的设计和更长的续航时间等基本要求。然而&#xff0c;AR眼镜的设计方案不仅仅需要在硬件和显示技术方面取得突破&#xff0c;还要在用户体验方面有所进展。 过去&#xff0c;由于造价较高&#xff0c;AR眼镜的普及和商业…

搜维尔科技:「案例」Faceware电影中面部动画的演变历程

面部动画是电影中角色表演的一个重要方面&#xff0c;尤其是在严重依赖电子动画、化妆效果和动作捕捉系统的奇幻电影中。在《龙与地下城&#xff1a;盗贼荣誉》电影中&#xff0c;龙裔角色的面部动画是一个复杂的系统&#xff0c;使该生物在大屏幕上栩栩如生。该系统依赖于一种…

逻辑回归及python实现

概述 logistic回归是一种广义线性回归&#xff08;generalized linear model&#xff09;&#xff0c;因此与多重线性回归分析有很多相同之处。它们的模型形式基本上相同&#xff0c;都具有 w‘xb&#xff0c;其中w和b是待求参数&#xff0c;其区别在于他们的因变量不同&#x…

zeppelin 未授权任意命令执行漏洞复现

一、命令执行复现 访问http://ip:8080&#xff0c;打开zeppelin页面&#xff0c;&#xff08;zeppelin默认监听端口在8080&#xff09; 点击Notebook->create new note创建新笔记 在创建笔记的时候选择Default Interpreter为sh&#xff0c;即可执行sh命令 如下图&#x…

2024 Q1企业级SSD市场暴涨,国产努力追赶!

在2024年第一季度&#xff0c;由于对高容量存储需求的激增&#xff0c;企业级固态硬盘&#xff08;SSD&#xff09;市场的收入实现了显著增长&#xff0c;达到了37.58亿美元&#xff0c;与上一季度相比增长了62.9%。这一增长主要得益于供应商减产导致的高容量订单需求未得到满足…

基础篇01——SQL的基本语法和分类

MySQL数据库安装与基本使用 安装教程参见&#xff1a;通过zip安装MySQL 通过命令行启动和停止MySQL服务命令 前提&#xff1a;安装MySQL成功之后 启动服务&#xff1a;net start mysql 停止服务&#xff1a;net stop mysql 通过命令行连接mysql 可以通过mysql的客户端命令行…

GPT、Claude、Perplexity等AI集体宕机罢工,全球打工人崩溃了

就在昨天&#xff01;一个看似平常的周三上午&#xff0c;三大顶尖AI居然集体罢工了&#xff01; 首先&#xff0c;网友们发现OpenAI的ChatGPT崩了&#xff0c;接着Claude和Perplexity也接连陷入崩溃状态。而Gemini也出现了短暂下线问题&#xff0c;整整三个小时&#xff0c;这…

【MySQL】sql语句之表操作(上)

序言 在上一篇的数据库操作的内容中&#xff0c;学习了两种属性和常用的七种操作&#xff0c;学习是循序渐进的&#xff0c;库的操作学完了&#xff0c;就要开始学习表的操作了&#xff0c;而表可与数据强相关&#xff0c;比如DDL&#xff0c;即数据定义语言&#xff0c;DML&am…

SIMBA方法解读

目录 预处理scRNA-seqscATAC-seq 图构建&#xff08;5种场景&#xff09;scRNA-seq分析scATAC-seq分析多模态分析批次整合多模态整合 图学习SIMBA空间中查询实体识别TF-target genes 预处理 scRNA-seq 过滤掉在少于三个细胞中表达的基因。原始计数按文库大小标准化&#xff0…

【2024】零基础Python 快速入门篇

2023年是AI的元年&#xff0c;AI的爆火不仅推动了科技领域的进步&#xff0c;更让 Python 语言成为了这一变革中的关键角色。 Python 语言简单易懂&#xff0c;语法清晰明了&#xff0c;懂一点英语的都能学得会。很适合在职场摸爬滚打多年的前端组长作为捅破天花板的语言&…

引用(C++)和内联函数

前言&#xff1a;本文主要讲解C语法中引用如何使用和使用时的一些技巧 基本语法 引用就是取别名 #include <iostream> using namespace std; int main() {int a 10;int& b a;//给a取别名为bcout << a << endl;cout << b << endl;return 0…

后端开发面经系列 -- 华为C++一面面经

HUAWEI – C一面面经 公众号&#xff1a;阿Q技术站 来源&#xff1a;https://www.nowcoder.com/feed/main/detail/b8113ff340d7444985b32a73c207c826 1、计网的协议分几层&#xff1f;分别叫什么&#xff1f; OSI七层模型 物理层 (Physical Layer): 负责物理设备之间的原始比…

【介绍下Spark MLlib机器学习】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…