深入理解 Python 中的日志 logging 模块

2023-11-17 11:09:46 浏览数 (4)

1. 介绍

日志记录是软件开发中的一个重要环节,它可以帮助我们监控程序运行过程中的状态、诊断问题以及分析性能。Python 中通常使用 logging 模块,让我们能够方便地记录日志信息。

2. 案例与日志级别

在举例子之前先介绍以下日志级别,日志级别的高低可以通过数值表示,数值越高,日志级别越高。打开logging的源码可以发现,这些日志等级实际上是整数常量,因此比如logging.ERROR40在代码里面也就是等价的:

由上可知:

DEBUG < INFO < WARNING < ERROR < CRITICAL

日志级别

数值

描述

DEBUG

10

详细的诊断信息,用于开发和调试阶段

INFO

20

一般性的信息,例如程序启动、关闭或完成某个操作

WARNING

30

潜在的问题或异常情况,但并不会导致程序中断

ERROR

40

导致程序中断或无法完成某个操作的错误

CRITICAL

50

非常严重的错误,可能导致整个系统崩溃或数据丢失

接着一个简单的例子:

代码语言:javascript复制
import logging

# 配置 logging 模块
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 记录日志信息
logging.debug("This is a debug message.")
logging.info("This is an info message.")
logging.warning("This is a warning message.")
logging.error("This is an error message.")
logging.critical("This is a critical message.")

在这个例子中,先使用 basicConfig() 进行初始化基本配置。我们设置了日志级别为 INFO,这意味着所有级别大于或等于 INFO的日志信息都会被输出(DEBUG不会输出)。

运行这个示例,将在控制台看到如下输出:

代码语言:javascript复制
2023-11-16 13:16:32,871 - INFO - This is an info message.
2023-11-16 13:16:32,873 - WARNING - This is a warning message.
2023-11-16 13:16:32,873 - ERROR - This is an error message.
2023-11-16 13:16:32,874 - CRITICAL - This is a critical message.

这里有一点需要注意,如果是在jupyter上测试。第一次运行完之后修改basicConfig()里面的日志等级并不会改变。因为logging.basicConfig() 只会在第一次调用时设置日志记录器的配置。后续对 basicConfig() 的调用将被忽略。这时候如果想要修改日志等级,则需要使用:

代码语言:javascript复制
logging.getLogger().setLevel(logging.ERROR)

3. 配置和自定义日志记录

在了解如何使用自定义 logging 之前,需要了解一些基本概念:

  • Logger:Logger 是 logging 模块的核心对象,用于记录日志信息。通常,我们会为每个模块或组件创建一个 Logger 对象。Logger 对象之间可以形成层次结构,以便更好地管理日志记录。
  • Handler:Handler 对象负责将日志信息输出到不同的目的地,例如控制台、文件或网络。一个 Logger 可以有多个 Handler,以实现不同级别的日志输出。
  • Formatter:Formatter 对象用于定义日志信息的格式。我们可以自定义 Formatter,以便按照需要展示日志信息。
  • Filter:Filter 对象用于对日志信息进行过滤,以便有选择地输出日志。

下面是一个例子:

代码语言:javascript复制
import logging

# 创建一个名为 'my_logger' 的 Logger 对象
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 创建一个 Handler,输出日志到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 创建一个 Handler,输出日志到文件
file_handler = logging.FileHandler('my_log.log')
file_handler.setLevel(logging.DEBUG)

# 创建一个 Formatter,定义日志信息的格式
console_formatter = logging.Formatter('我在控制台里面!%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_formatter = logging.Formatter('我在文件里面!%(asctime)s - %(name)s - %(levelname)s - %(message)s')


# 为 Handler 设置 Formatter
console_handler.setFormatter(console_formatter)
file_handler.setFormatter(file_formatter)

# 为 Logger 添加 Handler
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 记录日志信息
logger.debug("This is a debug message.")
logger.info("This is an info message.")
logger.warning("This is a warning message.")
logger.error("This is an error message.")
logger.critical("This is a critical message.")

logging.StreamHandler()是输出日志到控制台,logging.FileHandler('my_log.log')是输出日志到文件可以分别使用不同的formatter,最终展示效果如下。

4. 高级用法

logging 模块还提供了一些高级功能,例如日志过滤、日志旋转等。

4.1 日志过滤

我们可以使用 Filter 对象对日志信息进行过滤,有选择地输出日志:

代码语言:javascript复制
class CustomFilter(logging.Filter):
    def filter(self, record):
        # 只输出包含 'important' 关键字的日志信息
        return 'important' in record.msg

# 为 Logger 添加自定义 Filter
logger.addFilter(CustomFilter())

# 记录日志信息
logger.info("This is an important info message.")
logger.info("This is a regular info message.")

这里重写了 filter() 方法,使其只允许包含 'important' 关键字的日志信息通过。

4.2 日志旋转

在长时间运行的程序中,日志文件可能会变得非常大。为了避免这个问题,可以使用日志旋转功能,这个在平时开发中很常见。logging 模块提供了一个 RotatingFileHandler 类,用于实现日志文件的自动旋转。以下是一个示例:

代码语言:javascript复制
from logging.handlers import RotatingFileHandler

# 创建一个 RotatingFileHandler,最多保留 5 个日志文件,每个文件最大 1MB
rotating_handler = RotatingFileHandler('rotating_log.log', maxBytes=1024 * 1024, backupCount=5)
rotating_handler.setLevel(logging.DEBUG)
rotating_handler.setFormatter(formatter)

# 为 Logger 添加 RotatingFileHandler
logger.addHandler(rotating_handler)

# 记录日志信息
for i in range(10000):
    logger.debug(f"This is a debug message {i}.")

多次运行以上代码后,可以发现目录下生成多个文件,且rotating_log.log是最新文件。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞