告别print!Python logging日志记录使用从入门到精通
文章目录
告别print!Python logging日志记录使用从入门到精通
引言:日志记录的重要性与logging库的价值
在Python开发中,日志是调试程序、监控运行状态、排查线上问题的核心工具。相比简单的print()语句,标准库logging提供了分级记录、灵活输出(控制台/文件/网络)、格式化日志内容、动态调整日志级别等高级功能,能满足从简单脚本到大型应用的日志需求。本文将系统解析logging库的核心机制、使用方法、高级特性及最佳实践,帮助开发者构建规范、高效的日志系统。
一、logging库核心概念与基础组件
在使用logging前,需理解其核心组件和工作流程,这是灵活配置日志的基础。
1. 核心组件
- Logger(日志器):日志记录的入口,应用程序通过调用Logger的方法(如
debug()、info())生成日志。每个Logger有唯一名称,遵循层级结构(如app.web是app的子日志器)。 - Handler(处理器):决定日志的输出目的地(控制台、文件、数据库等),一个Logger可关联多个Handler(如同时输出到控制台和文件)。
- Formatter(格式器):定义日志的输出格式(如包含时间、级别、模块名等),每个Handler可单独设置Formatter。
- Filter(过滤器):对日志进行过滤(如仅记录特定模块或满足条件的日志),可作用于Logger或Handler。
2. 日志级别
logging定义了5种标准日志级别(从低到高),用于区分日志的重要性:
DEBUG(10):详细调试信息,仅开发环境使用。INFO(20):程序正常运行的关键信息(如“服务启动成功”)。WARNING(30):潜在问题(如“磁盘空间不足”),不影响程序运行。ERROR(40):错误导致功能失败(如“数据库连接失败”)。CRITICAL(50):严重错误,可能导致程序终止(如“内存耗尽”)。
默认级别:Logger默认级别为WARNING,即仅记录WARNING及以上级别的日志。
二、基础使用:快速上手与常用配置
1. 最简单的日志记录
通过logging模块直接调用日志方法(内部使用根日志器root):
import logging
# 记录不同级别的日志
logging.debug("这是DEBUG级别的日志(默认不输出)")
logging.info("这是INFO级别的日志(默认不输出)")
logging.warning("这是WARNING级别的日志(默认输出)")
logging.error("这是ERROR级别的日志")
logging.critical("这是CRITICAL级别的日志")
输出结果(默认格式:级别:模块名:消息):
WARNING:root:这是WARNING级别的日志(默认输出)
ERROR:root:这是ERROR级别的日志
CRITICAL:root:这是CRITICAL级别的日志
2. 基础配置:basicConfig()
使用basicConfig()可快速配置根日志器的级别、输出格式、输出目的地等,需在首次记录日志前调用:
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="a" # 文件模式:a(追加)/w(覆盖),默认a
)
# 测试日志
logging.debug("调试信息:用户输入了无效参数")
logging.info("程序启动成功,监听端口8080")
logging.warning("警告:磁盘空间剩余不足20%")
输出到app.log的内容:
2024-10-08 15:30:00 - root - DEBUG - 调试信息:用户输入了无效参数
2024-10-08 15:30:01 - root - INFO - 程序启动成功,监听端口8080
2024-10-08 15:30:02 - root - WARNING - 警告:磁盘空间剩余不足20%
3. 多处理器配置:同时输出到控制台和文件
通过手动创建Handler,可实现日志同时输出到多个目的地(如控制台用于实时查看,文件用于存档):
import logging
# 1. 获取日志器(指定名称,避免使用根日志器)
logger = logging.getLogger("my_app")
logger.setLevel(logging.DEBUG) # 日志器级别(低于Handler级别时以Handler为准)
# 2. 创建控制台处理器(输出到stdout)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 控制台仅输出INFO及以上
# 3. 创建文件处理器(输出到文件)
file_handler = logging.FileHandler("app.log")
file_handler.setLevel(logging.DEBUG) # 文件记录DEBUG及以上
# 4. 定义格式器并绑定到处理器
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s"
)
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 5. 日志器添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 测试日志
logger.debug("调试:数据库连接池初始化") # 仅文件输出
logger.info("信息:用户登录成功") # 控制台和文件均输出
logger.error("错误:API请求超时") # 控制台和文件均输出
说明:
- 日志器和处理器都有级别,最终输出级别取两者的较高值(如日志器
DEBUG+处理器INFO,实际输出INFO及以上)。 - 格式中
%(module)s:%(lineno)d表示日志所在模块和行号,便于定位代码。
三、高级特性:定制化与实战场景
1. 日志轮转:防止单个日志文件过大
当程序长期运行时,日志文件会不断增大,RotatingFileHandler(按大小轮转)和TimedRotatingFileHandler(按时间轮转)可自动切割日志:
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
# 1. 按大小轮转:每个文件最大1MB,保留5个备份
rotating_handler = RotatingFileHandler(
"app_rotating.log",
maxBytes=1024 * 1024, # 1MB
backupCount=5, # 最多5个备份文件(app_rotating.log.1 ~ .5)
encoding="utf-8" # 支持中文
)
# 2. 按时间轮转:每天凌晨轮转,保留7天日志
timed_handler = TimedRotatingFileHandler(
"app_timed.log",
when="midnight", # 轮转时间:midnight(凌晨)、H(每小时)、D(每天)等
interval=1, # 间隔1个单位(如when="H"则每小时轮转)
backupCount=7, # 保留7天
encoding="utf-8"
)
# 配置日志器
logger = logging.getLogger("rotating_demo")
logger.setLevel(logging.INFO)
logger.addHandler(rotating_handler)
logger.addHandler(timed_handler)
# 模拟大量日志输出
for i in range(1000):
logger.info(f"这是第{i}条日志,用于测试日志轮转")
2. 自定义过滤器:筛选特定日志
通过Filter可实现复杂的日志过滤逻辑(如仅记录特定用户的操作日志):
import logging
class UserFilter(logging.Filter):
"""仅记录指定用户的日志"""
def __init__(self, allowed_users):
self.allowed_users = allowed_users
def filter(self, record):
# 检查日志记录是否包含user属性,且在允许列表中
return hasattr(record, "user") and record.user in self.allowed_users
# 配置日志器
logger = logging.getLogger("user_log")
logger.setLevel(logging.INFO)
# 创建处理器并添加过滤器
console_handler = logging.StreamHandler()
console_handler.addFilter(UserFilter(allowed_users=["admin", "root"])) # 仅允许admin和root
logger.addHandler(console_handler)
# 测试日志(通过extra参数添加user属性)
logger.info("用户登录成功", extra={"user": "admin"}) # 会被记录
logger.info("用户修改配置", extra={"user": "guest"}) # 被过滤
logger.info("用户删除数据", extra={"user": "root"}) # 会被记录
3. 异常日志记录:自动捕获堆栈信息
使用logger.exception()在except块中记录异常,会自动添加堆栈跟踪信息,是调试的重要工具:
import logging
logging.basicConfig(
level=logging.ERROR,
format="%(asctime)s - %(levelname)s - %(message)s"
)
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
# 记录异常(自动包含堆栈信息)
logging.exception("除法运算错误") # 等价于logging.error(..., exc_info=True)
divide(10, 0)
输出结果(包含堆栈信息,清晰显示错误位置):
2024-10-08 16:00:00 - ERROR - 除法运算错误
Traceback (most recent call last):
File "demo.py", line 10, in divide
return a / b
ZeroDivisionError: division by zero
四、最佳实践与注意事项
1. 最佳实践
- 避免使用根日志器:优先通过
logging.getLogger(__name__)创建模块专属日志器(名称为模块路径,如myapp.utils),便于按模块控制日志级别。
# 在myapp/utils.py中
logger = logging.getLogger(__name__) # 日志器名称为"myapp.utils"
- 按环境调整日志级别:开发环境用
DEBUG,生产环境用INFO或WARNING,避免敏感信息泄露和性能损耗。
import os
level = logging.DEBUG if os.getenv("ENV") == "dev" else logging.INFO
logger.setLevel(level)
- 日志格式包含关键信息:至少包含时间、级别、模块/行号、消息,便于问题定位:
"%(asctime)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s"
- 异常日志必用
logger.exception():在except块中记录异常时,使用logger.exception()自动捕获堆栈信息,比手动记录更完整。
2. 注意事项
basicConfig()的调用时机:必须在首次记录日志前调用,否则配置不生效(因根日志器初始化后无法再通过basicConfig()修改)。- 避免重复添加Handler:多次调用添加Handler的代码会导致日志重复输出(如在循环或模块导入时),可通过
logger.handlers检查是否已添加:
if not logger.handlers: # 仅在无处理器时添加
logger.addHandler(console_handler)
- 性能考虑:
DEBUG级别日志在生产环境可能带来性能损耗(尤其是高频日志),需确保生产环境级别设置为INFO及以上。 - 线程安全:
logging库是线程安全的,但多进程写入同一日志文件可能导致内容错乱,需使用ConcurrentLogHandler(第三方库)或文件锁。
总结:构建可靠的日志系统
logging库作为Python的标准日志解决方案,通过分级、多目的地输出、格式化等功能,为程序提供了灵活且强大的日志记录能力。从简单脚本的basicConfig()到大型应用的多处理器+过滤器配置,logging能满足不同场景的需求。
掌握logging的核心在于理解Logger、Handler、Formatter的协作机制,结合最佳实践(如模块专属日志器、环境化级别配置、完整异常记录),可构建既便于调试又利于线上监控的日志系统。合理使用日志,能显著提升代码可维护性和问题排查效率,是专业开发不可或缺的技能。
更多推荐
所有评论(0)