91_Python日志logging模块详解

📅 2026/7/12 11:14:13
91_Python日志logging模块详解
Python日志logging模块详解文章目录Python日志logging模块详解前言一、日志级别二、Handler日志的输出目的地三、Formatter自定义日志格式四、使用配置文件管理日志五、日志最佳实践总结亮点总结适用场景扩展方向前言在软件开发过程中日志记录是一项不可或缺的功能。无论是调试程序、监控运行状态还是排查线上问题良好的日志系统都能大幅提升开发效率。很多初学者习惯使用print()输出调试信息但随着项目规模增长这种方式显得捉襟见肘——print()无法区分重要性级别无法自动记录时间戳和调用位置无法灵活切换输出目标文件/控制台/远程更无法在生产环境中按级别过滤。而且最致命的是你会忘记删除这些print语句导致生产环境控制台被大量无意义输出淹没。Python 标准库中的logging模块提供了一个灵活且强大的日志框架支持多级别输出、多种输出目标以及丰富的格式化选项。面试常见考点日志级别的含义和数值排序、logger的层级关系和propagate特性、如何配置滚动日志避免磁盘写满、为什么不用print而用logging模块。面试中常让你现场设计一个日志系统考察你对Handler和Formatter组合使用的理解。本文将深入讲解 logging 模块的核心概念与实战技巧。一、日志级别日志级别的设计遵循严重性递增原则越高级别表示越严重的问题。正确选择日志级别是使用logging的第一步也是最容易被用错的一步。常见错误包括把用户输入错误打到ERROR级别实际应该是WARNING或INFO、把DEBUG信息在生产环境全部输出严重影响性能、在循环中输出INFO级别日志导致日志爆炸。logging 模块定义了五个标准日志级别从低到高依次为级别数值用途DEBUG10详细的调试信息开发阶段使用INFO20确认程序按预期运行的信息WARNING30警告信息表明可能出现问题但程序仍在运行ERROR40错误信息某些功能未能正常执行CRITICAL50严重错误程序可能无法继续运行来看一个简单的示例importlogging# 配置日志级别为DEBUG低于此级别的日志将不会输出logging.basicConfig(levellogging.DEBUG)logging.debug(这是调试信息)logging.info(程序启动成功)logging.warning(内存使用率超过80%)logging.error(数据库连接失败)logging.critical(磁盘空间不足程序即将退出)运行上述代码控制台会输出除 DEBUG 之外的所有日志。这是因为basicConfig默认只输出WARNING及以上级别的日志而我们显式设置了levellogging.DEBUG因此所有级别的信息都会被显示。二、Handler日志的输出目的地Handler决定日志消息的最终去向。这是logging模块最灵活的部分——同一个logger可以挂载多个Handler实现一条日志、多处输出的效果。例如将INFO及以上日志输出到控制台供开发者实时查看同时将DEBUG及以上日志写入文件供事后排查。Handler之间完全独立互不影响这意味着你可以为不同Handler设置不同的级别和格式。这种设计的优雅之处在于logger只负责产生日志而日志去哪里、怎么展示完全由Handler和Formatter决定职责清晰分离。StreamHandler输出到控制台stdout/stderrFileHandler写入到文件RotatingFileHandler按文件大小自动轮转TimedRotatingFileHandler按时间自动轮转SMTPHandler通过邮件发送日志HTTPHandler通过 HTTP 发送日志下面的示例演示了如何同时将日志输出到控制台和文件importlogging# 创建 logger 对象loggerlogging.getLogger(my_app)logger.setLevel(logging.DEBUG)# 控制台 Handlerconsole_handlerlogging.StreamHandler()console_handler.setLevel(logging.INFO)# 文件 Handlerfile_handlerlogging.FileHandler(app.log,encodingutf-8)file_handler.setLevel(logging.DEBUG)# 将 Handler 添加到 loggerlogger.addHandler(console_handler)logger.addHandler(file_handler)logger.debug(文件记录变量 x 42)logger.info(控制台和文件都会看到这条信息)logger.error(发生了一个错误)RotatingFileHandler可以避免日志文件无限增长当文件达到指定大小时自动轮转。这是生产环境中的必备配置——我曾经见过一个线上服务的日志文件在无人关注的情况下增长到50GB最终写满了服务器磁盘。通过设置maxBytes和backupCount你可以精确控制磁盘占用上限例如 5MB × 3 15MB确保日志永远不会撑爆服务器fromlogging.handlersimportRotatingFileHandler rotating_handlerRotatingFileHandler(app.log,maxBytes5*1024*1024,backupCount3,encodingutf-8)# maxBytes5MB每个日志文件最大 5MB# backupCount3保留最近 3 个备份文件三、Formatter自定义日志格式Formatter决定每条日志记录的显示格式。一份好的日志格式至少应该包含时间、级别、位置文件名行号、消息内容。缺少任何一项都会在排查问题时感到无力——只看到数据库连接失败却不知道发生在哪个文件的第几行你不得不在代码中全局搜索这个字符串。特别提一句%(funcName)s在排查大函数内部问题时非常有用因为同一个函数的不同执行路径可能在不同位置触发同级别的日志。格式符含义%(asctime)s时间戳%(name)sLogger 名称%(levelname)s日志级别%(filename)s源文件名%(lineno)d行号%(funcName)s函数名%(message)s日志消息importlogging loggerlogging.getLogger(product_service)logger.setLevel(logging.DEBUG)consolelogging.StreamHandler()# 定义格式formatterlogging.Formatter(%(asctime)s [%(levelname)s] %(name)s - %(funcName)s:%(lineno)d - %(message)s,datefmt%Y-%m-%d %H:%M:%S)console.setFormatter(formatter)logger.addHandler(console)defprocess_order(order_id):logger.info(f开始处理订单{order_id})totalsum(range(1,101))logger.debug(f计算结果{total})logger.info(f订单{order_id}处理完成)process_order(10086)输出效果2026-05-29 10:30:15 [INFO] product_service - process_order:15 - 开始处理订单 10086 2026-05-29 10:30:15 [DEBUG] product_service - process_order:17 - 计算结果5050 2026-05-29 10:30:15 [INFO] product_service - process_order:18 - 订单 10086 处理完成四、使用配置文件管理日志在实际项目中硬编码日志配置不利于维护。试想一下如果你想在运行时切换日志级别比如临时开启DEBUG级别排查问题硬编码意味着你需要修改代码并重新部署。而使用字典配置你可以在不触碰代码的情况下通过修改配置来调整日志行为。此外不同的部署环境开发/测试/生产可能需要不同的日志策略字典配置天然支持多环境切换importlogging.config LOGGING_CONFIG{version:1,disable_existing_loggers:False,formatters:{verbose:{format:%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s,datefmt:%Y-%m-%d %H:%M:%S,},simple:{format:[%(levelname)s] %(message)s,},},handlers:{console:{class:logging.StreamHandler,level:INFO,formatter:simple,},file:{class:logging.handlers.RotatingFileHandler,level:DEBUG,formatter:verbose,filename:app.log,maxBytes:10485760,backupCount:5,encoding:utf-8,},},loggers:{myapp:{handlers:[console,file],level:DEBUG,propagate:False,},},root:{handlers:[console],level:WARNING,},}logging.config.dictConfig(LOGGING_CONFIG)loggerlogging.getLogger(myapp)logger.debug(这条信息只会出现在文件中)logger.info(这条信息同时出现在控制台和文件)五、日志最佳实践在实际开发中建议遵循以下原则选择合适的日志级别DEBUG 用于开发调试INFO 记录业务流程关键节点ERROR 记录异常但不终止程序CRITICAL 记录致命错误。避免在循环中输出过多日志高频日志会严重影响性能可通过判断级别避免不必要的字符串拼接。敏感信息脱敏不要将密码、Token 等明文写入日志。使用模块级 loggerlogger logging.getLogger(__name__)可以让每个模块拥有独立的 logger通过名称追溯日志来源。合理设置 propagate子 logger 的日志默认会向上传播到父 logger如果重复配置了 Handler 会造成日志重复输出。# 性能优化先判断级别避免不必要的字符串格式化iflogger.isEnabledFor(logging.DEBUG):logger.debug(f复杂计算的结果{expensive_computation()})# 使用 __name__ 创建模块级别 loggerloggerlogging.getLogger(__name__)总结本文从日志级别、Handler、Formatter、配置文件四个方面系统地介绍了 Python logging 模块的使用方法。logging模块功能强大且易于扩展能够满足从小型脚本到大型分布式系统的日志需求。合理配置日志系统不仅能帮助开发者快速定位问题还能通过日志聚合工具如 ELK、Sentry实现集中式日志管理与告警。建议读者在实际项目中尽快用 logging 替换 print让日志记录更加规范和可维护。亮点总结五大日志级别的完整讲解从 DEBUG 到 CRITICAL明确了每个级别的语义和适用时机指导读者告别 print 调试的随意性。多目标输出与日志轮转StreamHandler 输出到控制台FileHandler 写入文件RotatingFileHandler 和 TimedRotatingFileHandler 解决了日志文件无限增长的问题。Formatter 灵活定制通过格式字符串%(asctime)s、%(levelname)s、%(funcName)s 等组合出符合团队规范的日志格式精准定位问题来源。字典配置管理dictConfig将日志配置从代码中抽离支持多 Logger、多 Handler 的集中管理方便在不同环境间切换。性能优化与安全实践包括级别判断避免无效字符串格式化、敏感信息脱敏、模块级 Logger 和 propagate 的正确使用。适用场景生产环境日志系统搭建为 Web 应用、微服务或后台任务配置分级日志输出到文件和控制台并设置自动轮转防止磁盘写满。线上问题排查与调试通过精确的格式文件名行号函数名和级别过滤快速定位异常发生的上下文替代临时添加 print 的低效做法。与日志聚合平台对接将 FileHandler 输出的日志对接 ELKElasticsearch Logstash Kibana或 Sentry 等平台实现集中式日志监控与告警。扩展方向Python 命令行参数解析 argparse本系列第 92 篇通过 argparse 控制日志级别和输出路径实现动态切换日志行为。Python 配置文件处理 ConfigParser本系列第 93 篇将 dictConfig 字典配置写入 INI 文件结合环境变量实现多环境日志管理。Sentry / ELK 日志聚合实战学习如何将 Python 应用日志接入 Sentry 进行错误追踪或通过 Logstash 将日志发送到 Elasticsearch 进行可视化分析。