basicconfig无法实现多级日志分级存储,因其仅支持单个handler(默认streamhandler),需手动创建多个filehandler并分别设置level、formatter及levelfilter,同时配置logger级别为debug、propagate=false,并注意生产环境的路径、并发与日志轮转。

为什么直接用 basicConfig 存不了多级日志?
因为 basicConfig 只能绑定一个 Handler,默认是 StreamHandler(输出到控制台),即使你设了 level=logging.DEBUG,所有级别日志都会走同一个出口——没法按 DEBUG、INFO、WARNING 分开写入不同文件。
真正实现分级存储,得手动创建多个 FileHandler,每个绑定对应日志级别过滤器,并确保 Logger 本身级别足够低(比如设为 DEBUG),让所有日志都能“进来”再分流。
-
Logger的setLevel是“准入门槛”,必须 ≤ 最低要存的日志级别(如想存DEBUG,就得设DEBUG) - 每个
Handler的setLevel是“出口闸门”,决定它实际处理哪些级别 - 别忘了给
Handler加Formatter,否则文件里只有纯消息,没时间、级别、模块名等关键上下文
怎么给不同级别日志配不同 FileHandler?
核心是为每个目标级别(如 DEBUG、WARNING)单独实例化 FileHandler,并调用其 setLevel。注意:不能只靠 Filter,因为 Handler 自身的级别检查在过滤器之前就发生了。
import logging
<p>logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG) # 允许所有日志进入 logger</p><h1>DEBUG 级别专用 handler</h1><p>debug_handler = logging.FileHandler('debug.log')
debug_handler.setLevel(logging.DEBUG)
debug_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(debug_handler)</p><h1>WARNING 及以上专用 handler</h1><p>warn_handler = logging.FileHandler('warning.log')
warn_handler.setLevel(logging.WARNING) # 注意:这里设 WARNING,不是 DEBUG
warn_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(warn_handler)</p>
- 运行后,
logger.debug('test')只进debug.log;logger.warning('oops')同时进两个文件(因为warning.log的级别是WARNING,而debug.log的级别是DEBUG,都满足) - 如果只想让
WARNING日志只进warning.log,不在debug.log重复出现,得给debug_handler加个自定义Filter,拦截非DEBUG和INFO的记录 - 多个
Handler共用一个Logger时,Logger的propagate默认为True,若上层还有 root logger,可能造成日志重复输出,建议设logger.propagate = False
如何避免 DEBUG 日志污染 WARNING 文件?
默认行为下,只要 Handler 的 level ≤ 日志级别,就会处理——所以 WARNING 日志必然也会被 DEBUG-level 的 Handler 接收。解决办法是用 Filter 做精确拦截。
最简方式:继承 logging.Filter,重写 filter() 方法,只放行指定范围的日志级别:
class LevelFilter(logging.Filter):
def __init__(self, level):
self.level = level
def filter(self, record):
return record.levelno == self.level # 严格匹配(或用 >= 实现“及以上”)
<h1>绑定到 handler</h1><p>debug_handler.addFilter(LevelFilter(logging.DEBUG))
info_handler.addFilter(LevelFilter(logging.INFO))
warning_handler.addFilter(LevelFilter(logging.WARNING))</p>
- 用
==是为了严格分级:DEBUG 日志只进 debug.log,INFO 只进 info.log……适合审计或排查特定问题 - 若想让 WARNING 日志也出现在 warning.log 和 error.log 中(即“向上兼容”),把
==换成>=,并在error_handler上设setLevel(logging.ERROR) - 注意
Filter是对record对象操作,record.levelno是整数(DEBUG=10,INFO=20,WARNING=30,ERROR=40),比字符串比较更可靠
生产环境要注意的三个实际细节
本地跑通不等于线上能稳住。分级日志在真实部署中常因路径、权限、并发写入出问题。
- 文件路径必须是绝对路径或确保工作目录稳定,否则
FileHandler可能静默失败(尤其在 systemd 或 Docker 中)——建议用os.path.abspath('logs/debug.log')初始化 - 多个进程同时写同一个日志文件会损坏内容,
FileHandler不做进程锁;高并发场景应改用RotatingFileHandler或TimedRotatingFileHandler,并设置delay=True避免启动时检查文件是否存在 - 长时间运行的服务,日志文件会越来越大,务必配置
maxBytes和backupCount,否则磁盘可能被撑爆——比如RotatingFileHandler('app.log', maxBytes=10*1024*1024, backupCount=5)
分级日志真正的复杂点不在语法,而在级别语义是否对齐业务需求:比如 “API 调用失败” 应该记 ERROR 还是 WARNING?这个判断一旦错位,后续按级别查日志就全乱了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











