杰瑞科技汇

python logging的基本使用

为什么使用 logging 而不是 print()

在学习 logging 之前,先理解它的优势:

python logging的基本使用-图1
(图片来源网络,侵删)
特性 print() logging
日志级别 只有一种输出方式 支持多种级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
性能 每次执行都会产生 I/O 开销 可以通过级别过滤日志,减少不必要的字符串操作和 I/O
可配置性 硬编码在代码中 可以在运行时通过配置文件或代码动态修改行为
输出目标 默认标准输出 可同时输出到控制台、文件、网络等多种目标
格式化 格式固定 可自定义日志格式,包含时间、模块名、行号等丰富信息
调试 需要手动删除或注释 可以轻松调整日志级别来控制输出信息量

日志的五个核心级别

logging 模块定义了五个标准的日志级别,从低到高依次是:

  1. DEBUG: 最详细的日志信息,通常用于调试问题。
  2. INFO: 正常的信息,表明程序按预期运行。
  3. WARNING: 警告信息,表示发生了意外,但程序仍在正常工作。
  4. ERROR: 错误信息,表示程序某部分功能无法正常工作。
  5. CRITICAL: 严重错误,表示程序本身可能无法继续运行。

重要规则:只记录级别大于或等于当前设置的日志级别的消息,如果级别设置为 INFOINFO, WARNING, ERROR, CRITICAL 的日志都会被记录,而 DEBUG 的日志会被忽略。


logging 的基本组件

要理解 logging 的工作方式,需要了解几个核心组件:

  • Logger (记录器):这是你直接打交道的对象,通过 logging.getLogger(name) 获取,你可以使用不同的 Logger 对象来组织日志(为应用的每个模块创建一个 Logger)。
  • Handler (处理器):将 Logger 生成的日志记录发送到指定的目标。
    • StreamHandler: 输出到控制台 (默认)。
    • FileHandler: 输出到文件。
    • RotatingFileHandler: 输出到文件,并在文件达到大小时进行轮转。
  • Formatter (格式化器):定义日志记录的最终输出格式,可以包含时间、日志级别、消息内容等。
  • Filter (过滤器):更精细地控制哪些日志记录应该被输出。

工作流程:你的代码调用 logger.info(...) -> Logger 处理 -> 如果级别匹配,则传递给所有已添加的 Handler -> Handler 使用 Formatter 格式化日志 -> 输出到目标。

python logging的基本使用-图2
(图片来源网络,侵删)

基本使用方法

快速上手(不推荐用于生产环境)

最简单的方式是直接使用 logging 模块的顶层函数,这种方式配置简单,但不够灵活。

import logging
# 默认情况下,日志级别为 WARNING,只显示 WARNING 及以上级别的日志
# 并且默认输出格式简单
logging.debug("这是一个调试信息") # 不会显示
logging.info("这是一个普通信息")   # 不会显示
logging.warning("这是一个警告")    # 会显示
logging.error("这是一个错误")      # 会显示
logging.critical("这是一个严重错误") # 会显示

输出:

WARNING:root:这是一个警告
ERROR:root:这是一个错误
CRITICAL:root:这是一个严重错误

可以看到,root 是默认的 Logger 名称,格式也比较简单。

基础配置(推荐)

使用 logging.basicConfig() 是一种简单且常用的配置方法,适用于小型脚本或快速原型开发。

python logging的基本使用-图3
(图片来源网络,侵删)
import logging
# 进行基本配置
logging.basicConfig(
    level=logging.DEBUG,      # 设置日志级别为 DEBUG,显示所有级别的日志
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 自定义格式
    datefmt='%Y-%m-%d %H:%M:%S', # 日期时间格式
    filename='app.log',       # 输出到文件
    filemode='w'              # 文件模式 ('w'为覆盖, 'a'为追加)
)
# 现在所有级别的日志都会被记录到 app.log 文件中
logging.debug("这是一个调试信息")
logging.info("这是一个普通信息")
logging.warning("这是一个警告")
logging.error("这是一个错误")
logging.critical("这是一个严重错误")

执行后,app.log 文件内容如下:

2025-10-27 15:30:00 - root - DEBUG - 这是一个调试信息
2025-10-27 15:30:00 - root - INFO - 这是一个普通信息
2025-10-27 15:30:00 - root - WARNING - 这是一个警告
2025-10-27 15:30:00 - root - ERROR - 这是一个错误
2025-10-27 15:30:00 - root - CRITICAL - 这是一个严重错误

面向对象的配置(最灵活、最推荐)

对于复杂的应用程序,推荐使用面向对象的方式配置 LoggerHandlerFormatter,这种方式提供了最大的灵活性。

import logging
# 1. 创建一个 logger 实例
# 通常使用 __name__ 作为 logger 的名称,这样可以方便地追踪日志来源的模块
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # 设置 logger 的日志级别
# 2. 创建 Handler
# 2.1 创建一个用于输出到控制台的 Handler
ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # 设置这个 Handler 的日志级别为 INFO
# 2.2 创建一个用于输出到文件的 Handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.DEBUG) # 设置这个 Handler 的日志级别为 DEBUG
# 3. 创建 Formatter 并绑定到 Handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# 4. 将 Handler 添加到 logger
logger.addHandler(ch)
logger.addHandler(fh)
# 5. 使用 logger 记录日志
logger.debug('这是一个调试信息,只会出现在文件中')
logger.info('这是一个普通信息,会同时出现在控制台和文件中')
logger.warning('这是一个警告,会同时出现在控制台和文件中')
logger.error('这是一个错误,会同时出现在控制台和文件中')

工作原理分析:

  • logger.setLevel(logging.DEBUG): logger 接收所有级别的日志。
  • ch.setLevel(logging.INFO): 控制台处理器只处理 INFO 及以上的日志。
  • fh.setLevel(logging.DEBUG): 文件处理器处理所有级别的日志。

输出结果:

  • 控制台输出 (只显示 INFO 及以上):
    2025-10-27 15:35:00 - __main__ - INFO - 这是一个普通信息,会同时出现在控制台和文件中
    2025-10-27 15:35:00 - __main__ - WARNING - 这是一个警告,会同时出现在控制台和文件中
    2025-10-27 15:35:00 - __main__ - ERROR - 这是一个错误,会同时出现在控制台和文件中
  • app.log 文件内容 (显示所有级别):
    2025-10-27 15:35:00 - __main__ - DEBUG - 这是一个调试信息,只会出现在文件中
    2025-10-27 15:35:00 - __main__ - INFO - 这是一个普通信息,会同时出现在控制台和文件中
    2025-10-27 15:35:00 - __main__ - WARNING - 这是一个警告,会同时出现在控制台和文件中
    2025-10-27 15:35:00 - __main__ - ERROR - 这是一个错误,会同时出现在控制台和文件中

常用格式说明

Formatter 中,你可以使用以下占位符来定制日志格式:

占位符 含义
%(name)s Logger 的名称 (__main__)
%(levelname)s 日志级别名称 (DEBUG, INFO)
%(message)s
%(asctime)s 可读的时间,如 2025-10-27 15:30:00,123
%(filename)s 模块文件名
%(funcName)s 调用日志的函数名
%(lineno)d 调用日志的代码行号
%(process)d 进程ID
%(thread)d 线程ID

完整示例

下面是一个将所有概念结合起来的完整示例。

import logging
import time
def setup_logger():
    """配置并返回一个 logger 实例"""
    logger = logging.getLogger('my_app')
    logger.setLevel(logging.DEBUG)
    # 防止重复添加 Handler
    if not logger.handlers:
        # 1. 控制台 Handler (只显示 WARNING 及以上)
        ch = logging.StreamHandler()
        ch.setLevel(logging.WARNING)
        c_formatter = logging.Formatter('%(levelname)s - %(message)s')
        ch.setFormatter(c_formatter)
        # 2. 文件 Handler (记录所有级别)
        fh = logging.FileHandler('application.log', encoding='utf-8')
        fh.setLevel(logging.DEBUG)
        f_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s')
        fh.setFormatter(f_formatter)
        # 3. 添加 Handler 到 logger
        logger.addHandler(ch)
        logger.addHandler(fh)
    return logger
# --- 主程序 ---
if __name__ == '__main__':
    # 获取 logger 实例
    logger = setup_logger()
    logger.debug('应用启动,开始执行主逻辑...')
    logger.info('用户 "Alice" 登录成功')
    logger.warning('磁盘空间剩余不足 10%')
    try:
        result = 1 / 0
    except ZeroDivisionError:
        logger.error('发生除零错误!', exc_info=True) # exc_info=True 会记录完整的堆栈跟踪信息
    logger.critical('数据库连接丢失!')
    logger.info('应用准备关闭。')

执行后,控制台输出:

WARNING - 磁盘空间剩余不足 10%
ERROR - 发生除零错误!
Traceback (most recent call last):
  File "your_script_name.py", line 29, in <module>
    result = 1 / 0
ZeroDivisionError: division by zero
CRITICAL - 数据库连接丢失!
INFO - 应用准备关闭。

执行后,application.log 文件内容:

2025-10-27 16:00:00,123 - my_app - DEBUG - my_app:18 - 应用启动,开始执行主逻辑...
2025-10-27 16:00:00,124 - my_app - INFO - my_app:19 - 用户 "Alice" 登录成功
2025-10-27 16:00:00,124 - my_app - WARNING - my_app:20 - 磁盘空间剩余不足 10%
2025-10-27 16:00:00,124 - my_app - ERROR - my_app:29 - 发生除零错误!
Traceback (most recent call last):
  File "your_script_name.py", line 29, in <module>
    result = 1 / 0
ZeroDivisionError: division by zero
2025-10-27 16:00:00,124 - my_app - CRITICAL - my_app:35 - 数据库连接丢失!
2025-10-27 16:00:00,124 - my_app - INFO - my_app:37 - 应用准备关闭。
  • 对于简单脚本:使用 logging.basicConfig() 是最快的方式。
  • 对于任何非 trivial 的项目:强烈推荐使用面向对象的配置方法 (logger, handler, formatter),因为它灵活、可扩展且易于维护。
  • 始终使用 logger = logging.getLogger(__name__) 来获取 logger,这样可以清晰地知道日志的来源。
  • 合理设置日志级别,既能获得足够的信息用于调试,又不会被过多的日志淹没。
分享:
扫描分享到社交APP
上一篇
下一篇