Python3 log() 函数(保姆级教程)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在软件开发中,日志系统如同程序的“神经系统”,能够帮助开发者快速定位问题、追踪程序状态,并为后续的优化和调试提供关键依据。Python3 提供的 logging 模块(而非直接的 log() 函数)是实现日志记录的核心工具,它通过灵活的配置和丰富的功能,为开发者提供了一套高效、可扩展的日志解决方案。本文将深入讲解如何使用 Python 的 logging 模块,并通过实际案例帮助读者掌握日志记录的最佳实践。
1. Python3 logging 模块基础入门
1.1 日志记录的核心概念
Python 的 logging 模块基于“日志器-处理器-格式化器”三层架构设计,类似于一个完整的消息发布系统:
- Logger(日志记录器):负责收集日志信息,并决定是否将消息传递给下一个环节。
- Handler(处理器):负责将日志信息发送到指定目的地(如控制台、文件、网络等)。
- Formatter(格式化器):定义日志信息的输出格式,例如时间、日志级别、消息内容等。
形象比喻:
可以把 Logger 想象成报纸的编辑,它决定哪些新闻值得发布;Handler 是报纸的发行渠道(如印刷版、电子版);而 Formatter 则是排版设计师,负责将内容以美观的方式呈现。
1.2 基本用法示例
以下代码演示了 logging 模块的基础用法:
import logging
logger = logging.getLogger("example")
logger.setLevel(logging.DEBUG) # 设置最低日志级别为 DEBUG
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 控制台仅显示 INFO 及以上级别日志
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.debug("这是一条调试信息")
logger.info("程序正在运行")
logger.warning("检测到潜在问题")
logger.error("发生了一个错误")
logger.critical("系统崩溃!")
运行结果:
2023-10-01 10:00:00,000 - example - INFO - 程序正在运行
2023-10-01 10:00:00,001 - example - WARNING - 检测到潜在问题
2023-10-01 10:00:00,002 - example - ERROR - 发生了一个错误
2023-10-01 10:00:00,003 - example - CRITICAL - 系统崩溃!
关键点说明:
getLogger()
可以指定 logger 的名称,便于后续配置。setLevel()
设置日志级别时,只有达到或高于该级别的日志才会被记录。- 默认的日志级别从低到高为:DEBUG < INFO < WARNING < ERROR < CRITICAL。
2. 自定义日志配置与格式化
2.1 配置日志格式的细节
通过 Formatter
可以灵活定义日志的输出格式,常用参数如下:
参数 | 说明 | 示例值 |
---|---|---|
%(asctime)s | 日志生成的时间 | 2023-10-01 10:00:00,000 |
%(name)s | Logger 的名称 | example |
%(levelname)s | 日志级别名称(如 DEBUG、INFO) | INFO |
%(message)s | 日志的具体内容 | 程序正在运行 |
%(filename)s | 生成日志的源文件名 | example.py |
%(lineno)d | 生成日志的代码行号 | 42 |
示例代码:
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
2.2 使用配置文件优化代码
直接在代码中配置 logging 模块会导致代码冗余,推荐使用 logging.config
模块加载配置文件。例如,创建一个名为 logging.conf
的文件:
[loggers]
keys=root,sampleLogger
[handlers]
keys=consoleHandler
[formatters]
keys=sampleFormatter
[logger_sampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=sampleLogger
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=sampleFormatter
args=(sys.stdout,)
[formatter_sampleFormatter]
format=%(asctime)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S
在代码中加载配置:
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('sampleLogger')
logger.info("使用配置文件初始化日志!")
3. 日志输出到文件与多线程支持
3.1 将日志保存到文件
通过 FileHandler
和 RotatingFileHandler
,可以实现日志文件的持久化存储和自动轮转:
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.ERROR)
logger.addHandler(file_handler)
from logging.handlers import RotatingFileHandler
rotating_handler = RotatingFileHandler('app_rotating.log', maxBytes=1024*1024, backupCount=3)
logger.addHandler(rotating_handler)
3.2 多线程环境下的日志安全
多线程程序中,日志记录可能发生竞争条件。可通过以下方式解决:
from logging.handlers import QueueHandler, QueueListener
import queue
import threading
log_queue = queue.Queue(-1)
queue_handler = QueueHandler(log_queue)
listener = QueueListener(log_queue, console_handler, file_handler)
listener.start()
def worker():
logger = logging.getLogger("thread_worker")
logger.addHandler(queue_handler)
logger.info("线程任务完成")
thread = threading.Thread(target=worker)
thread.start()
thread.join()
listener.stop()
4. 实战案例:构建完整的日志系统
4.1 场景描述
假设开发一个用户登录系统,需要记录以下日志:
- 用户登录成功时记录
INFO
级别日志。 - 用户输入错误密码时记录
WARNING
级别日志。 - 系统内部错误(如数据库连接失败)记录
ERROR
级别日志。
4.2 完整代码实现
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("user_login")
logger.setLevel(logging.DEBUG)
file_handler = RotatingFileHandler(
'user_login.log',
maxBytes=1024*1024*10, # 10MB
backupCount=5
)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
def login(username, password):
if username == "admin" and password == "admin123":
logger.info(f"用户 {username} 登录成功")
elif username == "admin" and password != "admin123":
logger.warning(f"用户 {username} 密码错误,尝试次数剩余:2/3")
else:
logger.error("未知用户尝试登录!")
login("admin", "admin123") # 登录成功
login("admin", "wrong_password") # 密码错误
login("guest", "test123") # 未知用户
日志文件输出示例:
2023-10-01 10:00:00,000 - INFO - [main.py:42] - 用户 admin 登录成功
2023-10-01 10:00:00,001 - WARNING - [main.py:45] - 用户 admin 密码错误,尝试次数剩余:2/3
2023-10-01 10:00:00,002 - ERROR - [main.py:48] - 未知用户尝试登录!
5. 常见问题与最佳实践
5.1 避免日志污染
- 问题:多个模块使用相同 logger 名称可能导致日志混乱。
- 解决方案:通过
__name__
动态获取模块名称:logger = logging.getLogger(__name__)
5.2 性能优化
- 日志记录本身会消耗资源,建议在生产环境中关闭
DEBUG
级别日志。 - 使用惰性求值减少计算开销:
logger.debug(f"计算密集型操作结果:{heavy_computation()}") # 避免写法 logger.debug("计算结果:%s", heavy_computation()) # 推荐写法(仅在需要时执行)
结论
通过本文的学习,读者应该能够掌握 Python logging 模块的核心功能,并能够根据实际需求设计灵活的日志系统。日志记录不仅是调试的工具,更是程序健壮性和可维护性的基石。建议读者在项目中尽早引入日志模块,并通过配置文件和代码分离的方式提升代码的可维护性。
进阶方向:
- 探索第三方日志库(如
loguru
)的更简化用法。 - 结合监控系统(如 ELK 堆栈)实现日志集中化管理。
掌握 Python3 logging 模块 的使用,将为您的开发之路增添一份可靠的技术保障。