目录
- 核心概念:
logging模块- 为什么不用
print? logging的四大核心组件
- 为什么不用
- 快速上手:一个简单的日志示例
- 进阶配置:让日志更强大
- 日志格式化
- 日志级别
- 输出到多个目的地
- Windows 特有的日志实践
- 写入文件(最常用、最推荐)
- 监控文件:使用 Windows 事件查看器
- 日志轮转:避免日志文件过大
- 写入 Windows 事件日志
- 优点与缺点
- 实现代码(
pywin32)
- 写入系统控制台
- 最佳实践:组合输出
- 写入文件(最常用、最推荐)
- 一个完整的、可复用的日志配置类
- 总结与建议
核心概念:logging 模块
Python 自带的 logging 模块是进行日志记录的标准工具,它比 print 函数强大得多。

为什么不用 print?
- 可配置性:
logging可以轻松配置日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL),而print无法区分信息的重要性。 - 输出目标:
logging可以同时将日志输出到控制台、文件、网络等多个地方,print只能输出到标准输出(通常是控制台)。 - 格式化:
logging可以高度自定义日志格式,包含时间戳、模块名、行号等上下文信息。 - 性能:在生产环境中,
logging在级别设置为 WARNING 或更高时,DEBUG 和 INFO 消息的生成开销非常小。
logging 的四大核心组件
- Logger (记录器):这是你直接交互的对象,你通过
logging.getLogger(__name__)获取一个记录器实例,它负责决定日志消息的“最终目的地”以及消息的“级别”。 - Handler (处理器):将 Logger 传来的日志发送到指定的目的地。
StreamHandler: 发送到控制台(如sys.stderr)。FileHandler: 发送到磁盘文件。RotatingFileHandler: 发送到文件,并在文件达到大小时进行轮转。NTEventLogHandler: 发送到 Windows 事件日志(需要pywin32)。
- Formatter (格式化器):定义日志消息的最终输出格式,可以包含时间、级别、消息、模块名等。
- LogRecord (日志记录):当一条日志被创建时,
logging模块会自动创建一个包含所有相关信息的 LogRecord 对象(如时间、路径、函数名、消息等)。
工作流程:你调用 logger.info("...") -> 创建一个 LogRecord -> Logger 根据其级别决定是否处理 -> 处理器将 LogRecord 发送给 Formatter -> Formatter 格式化消息 -> 处理器将格式化后的消息发送到最终目的地。
快速上手:一个简单的日志示例
这是最基础的用法,日志会输出到控制台。
import logging
# 获取一个名为 'my_app' 的 logger 实例
# 如果不提供 name,默认获取 root logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG) # 设置 logger 的最低级别为 DEBUG
# 创建一个处理器,输出到控制台
console_handler = logging.StreamHandler()
# 创建一个格式化器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
# 将处理器添加到 logger
logger.addHandler(console_handler)
# --- 现在开始记录日志 ---
logger.debug("这是一条调试信息,通常用于开发阶段。")
logger.info("应用程序启动成功。")
logger.warning("这是一个警告,可能预示着潜在的问题。")
logger.error("发生了一个错误,但程序仍在运行。")
logger.critical("发生了严重错误,程序可能无法继续运行!")
输出结果:
2025-10-27 10:30:00,123 - my_app - DEBUG - 这是一条调试信息,通常用于开发阶段。
2025-10-27 10:30:00,123 - my_app - INFO - 应用程序启动成功。
2025-10-27 10:30:00,123 - my_app - WARNING - 这是一个警告,可能预示着潜在的问题。
2025-10-27 10:30:00,123 - my_app - ERROR - 发生了一个错误,但程序仍在运行。
2025-10-27 10:30:00,123 - my_app - CRITICAL - 发生了严重错误,程序可能无法继续运行!
进阶配置
日志格式化
格式化字符串使用 %(<keyname>)s 的形式,常用 keyname 包括:
%(asctime)s: 日志发生的时间。%(name)s: Logger 的名字。%(levelname)s: 日志级别名称 (DEBUG, INFO, etc.)。%(message)s: 日志消息内容。%(filename)s: 调用日志的源文件名。%(funcName)s: 调用日志的函数名。%(lineno)d: 调用日志的源代码行号。
日志级别
级别从低到高:DEBUG < INFO < WARNING < ERROR < CRITICAL。
Logger 的级别设置是一个“门槛”,如果你设置 logger.setLevel(logging.INFO),那么所有 DEBUG 级别的消息都将被忽略,而 INFO 及以上级别的消息会被处理。
输出到多个目的地
你可以为同一个 Logger 添加多个 Handler,实现同时输出到文件和控制台。
import logging
logger = logging.getLogger('multi_output_app')
logger.setLevel(logging.DEBUG)
# 1. 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # 控制台只显示 WARNING 及以上
# 2. 文件处理器
file_handler = logging.FileHandler('app.log') # 写入 app.log 文件
file_handler.setLevel(logging.DEBUG) # 文件记录所有级别
# 创建格式化器
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# --- 记录日志 ---
logger.debug("这条消息只会出现在 app.log 文件中。")
logger.info("这条消息也只会出现在 app.log 文件中。")
logger.warning("这条消息会同时出现在控制台和 app.log 文件中。")
Windows 特有的日志实践
在 Windows 上,你有几个非常好的选择来存储和管理日志。
写入文件(最常用、最推荐)
这是最通用、最可靠的方式,日志文件可以方便地用文本编辑器查看,也可以被 Windows 内置工具监控。
监控文件:使用 Windows 事件查看器
虽然日志文件是文本,但你可以通过 Windows 的“事件查看器”来监控它,并在新日志写入时收到通知。
- 打开事件查看器:按
Win + R,输入eventvwr.msc并回车。 - 创建自定义视图:
- 在左侧窗格,右键点击“自定义视图”,选择“创建自定义视图...”。
- 在“事件日志”选项卡下,选择“无日志”。
- 在“事件级别”中,勾选你感兴趣的级别(如“错误”、“警告”、“信息”)。
- 切换到“XML”选项卡,点击“编辑查询字符串”。
- 在弹出的窗口中,选择“手动编辑查询”,然后输入 XPath 查询,要监控一个名为
my_app.log的文件:*[System[(EventID=4658)]] and *[EventData[Data[@Name='ProcessName']='python.exe']] and *[EventData[Data[@Name='ObjectName']='C:\path\to\your\app.log']]
注意:直接监控文件内容变化比较复杂,更简单的方法是使用
watchdog等库来监控文件,然后触发 Windows 事件,但对于简单的日志,直接打开文件查看即可。
一个更简单的方法是使用第三方工具如 Tail for Win32 来实时查看日志文件的末尾。
日志轮转:避免日志文件过大
长时间运行的服务会产生巨大的日志文件。RotatingFileHandler 可以解决这个问题,当日志文件达到指定大小时,它会自动备份旧文件并创建新文件。
from logging.handlers import RotatingFileHandler
import logging
logger = logging.getLogger('rotating_app')
logger.setLevel(logging.INFO)
# 创建一个 RotatingFileHandler
# 'app.log' 是文件名
# maxBytes=1MB (1024*1024) 表示当文件达到 1MB 时轮转
# backupCount=5 表示保留 5 个备份文件 (app.log.1, app.log.2, ...)
handler = RotatingFileHandler(
'app.log',
maxBytes=1024*1024,
backupCount=5,
encoding='utf-8' # 指定编码,避免中文乱码
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# 循环写入测试
for i in range(10000):
logger.info(f"这是第 {i} 条日志信息,用于测试日志轮转功能。")
写入 Windows 事件日志
如果你的应用程序是 Windows 服务或系统级工具,将其集成到 Windows 事件日志中是最佳实践。
优点与缺点
- 优点:
- 与系统集成,管理员可以通过“事件查看器”统一查看所有系统日志。
- 可以设置事件查看器对特定事件(如“错误”)发出警报。
- 日志是结构化的,易于查询和管理。
- 缺点:
- 需要安装第三方库
pywin32(pip install pywin32)。 - 配置相对复杂。
- 限制在 31224 个字符以内,超过会被截断。
- 需要安装第三方库
实现代码
import logging
import logging.handlers
import win32evtlogutil # pywin32 模块
# 定义应用程序名称,它会出现在事件日志中
APP_NAME = "My Python App"
# 创建 logger
logger = logging.getLogger(APP_NAME)
logger.setLevel(logging.DEBUG)
# 创建 NTEventLogHandler
# 可以指定事件类型类型,如 win32evtlogutil.NtEventLogType.EVENTLOG_INFORMATION_TYPE
handler = logging.handlers.NTEventLogHandler(
app_name=APP_NAME,
log_type='Application' # 通常是 'Application', 'System', 或 'Security'
)
# 格式化器
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# --- 记录日志 ---
logger.info("应用程序已成功启动。")
logger.warning("这是一个来自 Python 的警告事件。")
logger.error("一个错误发生了,请检查程序逻辑。")
# 注意:消息过长可能会被截断
long_message = "这是一个非常非常长的消息..." * 1000
logger.debug(long_message)
查看日志:打开“事件查看器”,导航到“Windows 日志” -> “应用程序”,你就能看到你的日志条目了。
写入系统控制台
这对于开发阶段的调试非常有用,特别是对于命令行工具或脚本,这其实就是 StreamHandler 的默认行为。
import logging
# 默认情况下,root logger 的级别是 WARNING
# 为了看到所有信息,需要设置级别
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
logging.debug("调试信息")
logging.info("普通信息")
最佳实践:组合输出
对于生产环境的应用程序,最健壮的日志配置通常是组合使用多种 Handler。
RotatingFileHandler: 持久化存储所有级别的日志,用于事后审计和问题排查。StreamHandler: 在控制台实时输出WARNING及以上级别的日志,方便开发或运维人员即时发现问题。NTEventLogHandler: 将ERROR和CRITICAL级别的事件同步到 Windows 事件日志,实现与系统的集成和报警。
一个完整的、可复用的日志配置类
为了避免在多个文件中重复配置 logging,可以创建一个配置函数或类。
import logging
import logging.handlers
import os
def setup_logger(app_name, log_file='app.log', log_level=logging.INFO):
"""
配置一个功能完善的 logger。
Args:
app_name (str): 应用程序名称,也用作 logger 的名字。
log_file (str): 日志文件名。
log_level (int): 日志级别,默认为 INFO。
"""
logger = logging.getLogger(app_name)
logger.setLevel(log_level)
# 防止重复添加 Handler
if logger.hasHandlers():
return logger
# 1. 文件处理器 (带轮转)
# 确保日志目录存在
log_dir = os.path.dirname(log_file)
if log_dir and not os.path.exists(log_dir):
os.makedirs(log_dir)
file_handler = logging.handlers.RotatingFileHandler(
log_file,
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG) # 文件记录所有级别
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s')
file_handler.setFormatter(file_formatter)
# 2. 控制台处理器 (只显示 WARNING 及以上)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
console_formatter = logging.Formatter('%(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
# 添加处理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
# --- 使用示例 ---
if __name__ == "__main__":
# 初始化 logger
my_logger = setup_logger('my_service', log_file='logs/my_service.log')
my_logger.debug("这是一个详细的调试信息,只在日志文件中可见。")
my_logger.info("服务初始化完成。")
my_logger.warning("检测到内存使用率较高。")
my_logger.error("无法连接到数据库!")
my_logger.critical("核心服务崩溃,正在尝试重启...")
总结与建议
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 开发/调试脚本 | logging.basicConfig() + StreamHandler |
简单快速,能在控制台看到所有信息。 |
| 长期运行的服务/应用 | RotatingFileHandler + StreamHandler |
文件保证日志不丢失,控制台方便实时监控,轮转防止磁盘空间耗尽。 |
| Windows 服务/系统工具 | RotatingFileHandler + NTEventLogHandler (仅限 ERROR+) |
文件用于详细审计,事件日志与系统集成,便于管理员统一管理和报警。 |
| Web 应用 (如 Flask/Django) | 使用框架的日志系统,但底层依然基于 logging 模块,配置方式类似。 |
框架已经提供了很好的集成,遵循其最佳实践即可。 |
最终建议:
- 总是使用
logging模块,而不是print。 - 为你的应用创建一个专用的
logger(logging.getLogger('your_app_name')),而不是使用root logger。 - 在生产环境中,优先使用
RotatingFileHandler来确保日志的可靠性和可管理性。 - 根据重要性选择日志级别,DEBUG 用于开发,INFO 用于记录关键流程,WARNING 用于潜在问题,ERROR 用于已发生的错误。
- 将日志配置代码封装起来,方便在整个项目中复用。
