Python 日志(logging)全解析

Python 日志(logging)全解析

文章目录

日志是程序运行状态、错误、关键操作的核心记录方式,相比 print 具有 可配置级别、持久化存储、多输出目标、结构化记录等优势。Python 标准库 logging 是处理日志的核心工具,以下是其全量知识点。

一、核心概念(四大组件)

logging 模块的设计遵循模块化思想,核心由 4 个组件构成,组件间的关系:日志器(Logger)可添加多个处理器(Handler),每个处理器可绑定一个格式化器(Formatter)和多个过滤器(Filter)

组件作用
Logger(日志器)程序直接调用的入口,负责生成日志记录(如 logger.debug()),可设置日志级别。
Handler(处理器)决定日志的输出目标(控制台、文件、网络等),每个 Logger 可绑定多个 Handler。
Formatter(格式化器)定义日志的输出格式(如包含时间、级别、模块名等)。
Filter(过滤器)精细化控制日志的输出(如只允许特定模块 / 级别 / 关键词的日志通过)。

二、日志级别

日志级别用于区分日志的重要性,级别越高,记录的信息越关键。logging 定义了 5 个内置级别(可自定义),级别优先级:DEBUG < INFO < WARNING < ERROR < CRITICAL。

级别数值适用场景
DEBUG10调试信息(如变量值、函数执行步骤),仅开发 / 测试环境使用。
INFO20正常运行信息(如程序启动、关键操作完成),生产环境常用。
WARNING30警告信息(如配置缺失、资源不足,程序仍可运行)。
ERROR40错误信息(如函数调用失败、文件读取错误,部分功能受影响)。
CRITICAL50严重错误(如数据库连接失败、内存耗尽,程序无法继续运行)。

核心规则:只有日志的级别 ≥ 日志器 / 处理器设置的级别时,日志才会被输出。默认级别为 WARNING(即默认只输出 WARNING 及以上级别)。

三、基础使用

3.1 最简用法(默认配置)

直接调用 logging 模块的便捷方法,默认输出到控制台,格式为 级别:日志器名称:消息。

import logging # 基础日志输出(默认级别WARNING) logging.debug("调试信息:变量x=10")# 不输出(级别低于WARNING) logging.info("程序启动成功")# 不输出 logging.warning("警告:配置文件未找到,使用默认配置")# 输出 logging.error("错误:读取文件失败")# 输出 logging.critical("严重错误:数据库连接中断")# 输出

3.2 基本配置(logging.basicConfig)

通过 basicConfig 快速配置日志(单例模式,仅第一次调用生效),支持设置级别、输出文件、格式等。

常用参数说明

参数作用
level设置日志器级别(如 logging.INFO)。
filename日志输出到文件(指定路径),若不设置则输出到控制台。
filemode文件打开模式:a(追加,默认)、w(覆盖)。
format日志格式(支持占位符,见下文)。
datefmt时间格式(如 %Y-%m-%d %H:%M:%S)
encoding文件编码(如 utf-8,解决中文乱码)。
style格式字符串风格:%(默认)、{}、$。

示例:配置文件输出 + 自定义格式

import logging # 基本配置(仅第一次调用生效) logging.basicConfig( level=logging.DEBUG,# 日志器级别设为DEBUG filename="app.log",# 输出到文件 filemode="a",# 追加模式 encoding="utf-8",# 中文编码format="%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(message)s",# 格式 datefmt="%Y-%m-%d %H:%M:%S"# 时间格式)# 输出日志 logging.debug("调试:用户ID=1001") logging.info("信息:接口请求成功") logging.warning("警告:磁盘空间不足80%") logging.error("错误:接口返回码500") logging.critical("严重:服务器内存耗尽")

日志格式占位符(常用)

占位符含义
%(asctime)s日志记录时间(人性化格式)
%(name)s日志器名称
%(levelname)s日志级别名称(大写)
%(levelno)s日志级别数值
%(lineno)d日志调用行号
%(module)s日志所在模块名
%(funcName)s日志所在函数名
%(process)d进程 ID
%(thread)d线程 ID
%(message)s日志消息内容

四、进阶配置(手动构建组件)

basicConfig 仅适用于简单场景,复杂场景(多输出目标、自定义格式 / 过滤)需手动创建 Logger、Handler、Formatter。

4.1 手动配置流程

  1. 获取 Logger 实例(推荐用 name 作为日志器名,便于区分模块);
  2. 设置 Logger 级别;
  3. 创建 Handler(如控制台 Handler、文件 Handler),设置 Handler 级别;
  4. 创建 Formatter,绑定到 Handler;
  5. 将 Handler 添加到 Logger;
  6. (可选)添加 Filter 过滤日志。

4.2 示例:多输出目标(控制台 + 文件)

import logging # 1. 获取Logger实例(模块名作为日志器名) logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG)# Logger级别(需≤Handler级别才会传递) logger.propagate =False# 禁止向上传递到根日志器(避免重复输出)# 2. 创建控制台Handler(StreamHandler) console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO)# 控制台只输出INFO及以上# 3. 创建文件Handler(FileHandler) file_handler = logging.FileHandler("advanced.log", encoding="utf-8") file_handler.setLevel(logging.DEBUG)# 文件输出DEBUG及以上# 4. 创建格式化器 console_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") file_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s")# 5. 绑定格式化器到Handler console_handler.setFormatter(console_formatter) file_handler.setFormatter(file_formatter)# 6. 添加Handler到Logger logger.addHandler(console_handler) logger.addHandler(file_handler)# 测试日志 logger.debug("调试:数据库查询SQL=SELECT * FROM users")# 仅文件输出 logger.info("信息:用户登录成功,ID=1001")# 控制台+文件输出 logger.error("错误:数据库查询超时")# 控制台+文件输出

4.3 过滤器(Filter)

自定义 Filter 可精细化控制日志输出,例如:只允许特定模块的日志通过、过滤包含敏感信息的日志。
示例:过滤特定模块的日志

import logging # 自定义过滤器:只允许日志器名包含"api"的日志通过classApiFilter(logging.Filter):deffilter(self, record):# record是日志记录对象,包含name/levelno/message等属性return"api"in record.name # 获取Logger logger = logging.getLogger("api.user") logger.setLevel(logging.DEBUG)# 创建Handler并添加过滤器 console_handler = logging.StreamHandler() console_handler.addFilter(ApiFilter())# 绑定过滤器 logger.addHandler(console_handler)# 测试:日志器名包含"api",会输出 logger.info("API请求:/user/info")# 另一个日志器(名不含api),不会输出 logger2 = logging.getLogger("db") logger2.addHandler(console_handler) logger2.info("数据库连接成功")

五、日志轮转(解决日志文件过大问题)

当日志文件持续写入时,文件体积会不断增大,logging.handlers 提供了轮转处理器,自动分割日志文件。

5.1 按大小轮转(RotatingFileHandler)

当文件达到指定大小后,自动创建新文件,保留指定数量的备份。

import logging from logging.handlers import RotatingFileHandler logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG)# 配置轮转处理器:单个文件最大10MB,最多保留5个备份 rotating_handler = RotatingFileHandler("app_rotating.log", maxBytes=10*1024*1024,# 10MB backupCount=5,# 保留5个备份 encoding="utf-8") rotating_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logger.addHandler(rotating_handler)# 测试:循环输出日志,触发轮转for i inrange(10000): logger.debug(f"测试轮转日志:{i}")

5.2 按时间轮转(TimedRotatingFileHandler)

按时间间隔(时 / 天 / 周)分割日志文件,适合按周期归档。

import logging from logging.handlers import TimedRotatingFileHandler logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG)# 配置时间轮转:每天轮转,保留7天备份,后缀为时间格式 time_handler = TimedRotatingFileHandler("app_timed.log", when="D",# 轮转单位:S(秒)/M(分)/H(时)/D(天)/W0(周一)/midnight(午夜) interval=1,# 间隔1天 backupCount=7,# 保留7个备份 encoding="utf-8", suffix="%Y-%m-%d.log"# 备份文件后缀(如app_timed.2025-12-29.log)) time_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logger.addHandler(time_handler) logger.info("按时间轮转的日志测试")

六、异常日志记录

记录异常信息是日志的核心场景,logging 提供了便捷方式:

6.1 logging.exception ()(推荐)

在 except 块中使用,自动记录异常堆栈信息,无需手动传参,级别为 ERROR。

import logging logging.basicConfig(level=logging.ERROR,format="%(asctime)s - %(levelname)s - %(message)s")try:1/0# 触发除零错误except Exception:# 自动记录堆栈信息 logging.exception("除零错误发生")

6.2 exc_info 参数

在 debug/info/error 等方法中设置 exc_info=True,手动记录异常堆栈。

try:open("nonexist.txt","r")except FileNotFoundError as e: logging.error("文件读取失败", exc_info=True)# 记录异常堆栈

七、多模块日志管理

大型项目通常分多个模块,需保证日志配置统一且可区分模块来源:

7.1 核心原则

  • 每个模块通过 logging.getLogger(name) 获取 Logger(name 为模块名,如 api.user);
  • 根日志器(logging.getLogger())统一配置,子日志器继承根日志器的配置(无需重复配置);
  • 避免重复添加 Handler(可通过判断 logger.handlers 是否为空)。

7.2 示例:多模块日志

# main.py(主模块)import logging import module_a # 配置根日志器defsetup_logging(): root_logger = logging.getLogger() root_logger.setLevel(logging.DEBUG)# 避免重复添加Handlerifnot root_logger.handlers: console_handler = logging.StreamHandler() formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") console_handler.setFormatter(formatter) root_logger.addHandler(console_handler)if __name__ =="__main__": setup_logging() logging.info("主程序启动") module_a.do_something()# module_a.py(子模块)import logging # 获取模块名对应的Logger logger = logging.getLogger(__name__)defdo_something(): logger.debug("子模块调试信息") logger.info("子模块执行操作")

八、结构化日志(JSON 格式)

默认日志为文本格式,不利于日志分析工具(如 ELK、Splunk)解析,可输出 JSON 格式日志:

8.1 自定义 Formatter 实现

import logging import json from datetime import datetime classJsonFormatter(logging.Formatter):defformat(self, record):# 构造JSON日志字段 log_data ={"time": datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S"),"logger": record.name,"level": record.levelname,"message": record.getMessage(),"lineno": record.lineno }# 若有异常,添加堆栈信息if record.exc_info: log_data["exc_info"]= self.formatException(record.exc_info)return json.dumps(log_data, ensure_ascii=False)# 配置Logger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) handler = logging.StreamHandler() handler.setFormatter(JsonFormatter()) logger.addHandler(handler)# 测试 logger.info("用户登录", extra={"user_id":1001})# extra可添加自定义字段 logger.error("接口调用失败", exc_info=True)

8.2 第三方库(推荐)

使用 python-json-logger 简化配置:

pip install python-json-logger 
import logging from pythonjsonlogger import jsonlogger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) handler = logging.StreamHandler() formatter = jsonlogger.JsonFormatter("%(asctime)s %(name)s %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") handler.setFormatter(formatter) logger.addHandler(handler) logger.info("结构化日志测试", extra={"request_id":"abc123"})

九、自定义扩展

9.1 自定义 Handler(如发送日志到钉钉 / 邮件)

import logging import requests # 自定义Handler:发送ERROR级别日志到钉钉群classDingTalkHandler(logging.Handler):def__init__(self, webhook_url):super().__init__() self.webhook_url = webhook_url self.setLevel(logging.ERROR)# 只处理ERROR及以上级别defemit(self, record):# 格式化日志消息 log_msg = self.format(record)# 发送钉钉消息 data ={"msgtype":"text","text":{"content":f"【系统错误】\n{log_msg}"}} requests.post(self.webhook_url, json=data)# 配置 logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG)# 控制台Handler console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logger.addHandler(console_handler)# 钉钉Handler ding_handler = DingTalkHandler("https://oapi.dingtalk.com/robot/send?access_token=你的token") ding_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(message)s")) logger.addHandler(ding_handler)# 测试:ERROR级别日志会发送到钉钉 logger.error("数据库连接失败,系统无法运行")

9.2 LoggerAdapter(添加上下文信息)

为日志添加全局上下文(如用户 ID、请求 ID),无需每次手动传 extra:

import logging # 自定义LoggerAdapterclassContextLoggerAdapter(logging.LoggerAdapter):defprocess(self, msg, kwargs):# 添加上下文信息returnf"[用户ID:{self.extra['user_id']}] {msg}", kwargs # 使用 logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler())# 绑定上下文 adapter = ContextLoggerAdapter(logger,{"user_id":1001}) adapter.info("登录成功") adapter.error("下单失败")

十、常见问题与解决方案

10.1 日志不输出

  • 原因 1:日志器 / 处理器级别设置过高(如 Logger 级别为 INFO,调用 debug ());
  • 原因 2:basicConfig 调用时机晚于第一次日志调用(basicConfig 仅第一次生效);
  • 原因 3:Logger 没有绑定 Handler(如手动创建 Logger 但未添加 Handler);
  • 解决方案:检查级别配置、确保 basicConfig 先调用、为 Logger 添加 Handler。

10.2 日志重复输出

  • 原因 1:多次添加同一个 Handler(如配置函数被多次调用);
  • 原因 2:子日志器向上传递到根日志器,根日志器和子日志器都有 Handler;
  • 解决方案:
# 方案1:判断Handler是否为空再添加ifnot logger.handlers: logger.addHandler(handler)# 方案2:禁止子日志器向上传递 logger.propagate =False

10.3 中文乱码

  • 原因:FileHandler 未指定编码为 utf-8;
  • 解决方案:
logging.FileHandler("app.log", encoding="utf-8")

10.4 级别不生效

  • 原因:Logger 和 Handler 都有级别,取更严格的(如 Logger 级别 DEBUG,Handler 级别 WARNING,最终只输出 WARNING+);
  • 解决方案:确保 Handler 级别 ≤ Logger 级别。

十一、最佳实践

  1. 统一配置:项目入口处统一配置根日志器,子模块通过 name 获取 Logger,避免重复配置;
  2. 级别区分环境:开发环境用 DEBUG,测试 / 预发用 INFO,生产环境用 WARNING/ERROR;
  3. 日志格式规范:包含时间、级别、模块名、行号、消息,异常日志必须包含堆栈;
  4. 日志轮转:生产环境必须启用日志轮转(按大小 / 时间),避免日志文件过大;
  5. 敏感信息脱敏:日志中不记录密码、token、手机号等敏感信息;
  6. 结构化输出:生产环境优先使用 JSON 格式日志,便于日志分析;
  7. 避免使用 root Logger:尽量使用模块名 Logger,便于区分日志来源;
  8. 异常必记录:所有 except 块必须记录异常日志(使用 exception() 或 exc_info=True)。

十二、第三方日志库(补充)

原生 logging 配置稍繁琐,可使用第三方库简化:

12.1 loguru(推荐)

开箱即用,无需复杂配置,支持自动轮转、结构化、异常捕获等:

pip install loguru 
from loguru import logger # 基本使用 logger.debug("调试信息") logger.info("正常信息") logger.error("错误信息", exc_info=True)# 配置文件轮转 logger.add("app.log", rotation="10 MB", retention="7 days", encoding="utf-8")

12.2 structlog

专注结构化日志,与 logging 兼容,支持多处理器、上下文绑定:

pip install structlog 

总结

Python 日志的核心是理解 Logger/Handler/Formatter/Filter 四大组件的协作关系,掌握级别控制、多输出目标、日志轮转、异常记录等核心功能。生产环境中需结合项目特点,选择原生 logging(灵活)或第三方库(便捷),确保日志可追溯、可分析、无敏感信息。

Read more

优选算法——双指针专题 3.快乐数 4.盛水最多的容器

优选算法——双指针专题 3.快乐数 4.盛水最多的容器

优选算法——双指针专题 3.快乐数 4.盛水最多的容器 一.快乐数 1.题目解析 [题目传送门](202. 快乐数 - 力扣(LeetCode)) 2.原理解析 第一种情况:数最后变成1 第二种情况:无限循环但不是1 但两种都可以抽象成一种,有点像之前做过的带环链表 解法:快慢双指针 1.定义快慢指针 2.慢指针每次向后移动一步,快指针每次向后移动两步 3.判断相遇时候的值 3.代码实现 classSolution{public:intBitSum(int n)//返回每一位数上的平方和{int sum=0;while(n){int m=n%10;

By Ne0inhk

Python 数据科学秘籍(五)

原文:annas-archive.org/md5/a4f348a4e11e27ea41410c793e63daff 译者:飞龙 协议:CC BY-NC-SA 4.0 第九章:生长树 本章我们将涵盖以下食谱: * 从树到森林——随机森林 * 生长极度随机化的树 * 生长旋转森林 介绍 在本章中,我们将看到更多基于树的算法的袋装方法。由于它们对噪声的鲁棒性以及对各种问题的普适性,它们在数据科学社区中非常受欢迎。 大多数这些方法的名声在于它们相比其他方法能够在没有任何数据准备的情况下获得非常好的结果,而且它们可以作为黑盒工具交给软件工程师使用。 除了前文提到的过高的要求外,还有一些其他优点。 从设计上看,袋装法非常适合并行化。因此,这些方法可以轻松应用于集群环境中的大规模数据集。 决策树算法在树的每一层将输入数据划分为不同的区域。因此,它们执行了隐式的特征选择。特征选择是构建良好模型中的一个重要任务。通过提供隐式特征选择,决策树相较于其他技术处于有利位置。因此,带有决策树的袋装法具备这一优势。 决策树几乎不需要数据准备。例如,考虑属性的缩放。属性的缩放对决策

By Ne0inhk

重新创建python3.10环境,与先创建python3.8环境然后conda install python=3.10,二者不同

重新创建python3.10环境,与先创建python3.8环境然后conda install python=3.10,二者不同 今天安装mem0ai: pip install mem0ai 安装前没有查看要求的python版本,装了3.8,然后运行如下指令报错: from mem0 import MemoryClient 然后发现mem0ai要求python版本3.10以上,于是通过conda install python=3.10升级,出现了新的报错。 删除当前环境后重新创建python3.10环境,就可以正常运行了。 问了AI原来二者是有区别的: “在已有 Conda 环境中通过 conda install python=3.10 升级 Python 版本,会导致依赖包(尤其是含 C 扩展的包)与新 Python

By Ne0inhk
Python-flask的企业合同管理系统-Pycharm django

Python-flask的企业合同管理系统-Pycharm django

目录 * Python Flask 企业合同管理系统技术要点 * Django 企业合同管理系统技术对比 * Pycharm 开发优化技巧 * 技术选型建议 * 开发技术路线 * 源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式! Python Flask 企业合同管理系统技术要点 Flask 框架核心模块 * 使用 Flask-SQLAlchemy 进行数据库模型设计,定义合同、客户、用户等数据表。 * 通过 Flask-WTF 实现表单验证,确保合同录入数据的合法性。 * 采用 Flask-Login 管理用户认证和权限控制,区分管理员与普通用户角色。 关键功能实现 * 合同增删改查(CRUD)功能,结合分页插件(Flask-Paginate)优化数据展示。 * 文件上传模块,支持 PDF/Word 格式合同附件存储,使用 Flask-Uploads 扩展。 * 合同状态跟踪(如待签署、

By Ne0inhk