Python
Python Logging:从基础配置到工程实践
理解 Python logging 的 Logger、Handler、Formatter、Filter 以及日志轮转与性能实践。
logging 模块有四个核心对象:Logger(入口)、Handler(去哪)、Formatter(长什么样)、Filter(要不要)。一条日志从 logger.info() 出发,经 Filter 筛选,分发给多个 Handler,每个 Handler 再用自己的 Formatter 格式化后输出。
一、日志级别
从低到高:DEBUG(10) → INFO(20) → WARNING(30) → ERROR(40) → CRITICAL(50)
Logger 和 Handler 各自有独立的级别阈值,消息必须同时通过两道关卡才能输出。
二、最简用法(basicConfig)
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
filename="app.log", # 不写则输出到控制台
filemode="a"
)
logging.debug("调试信息")
logging.info("普通信息")
logging.warning("警告")
logging.error("错误")
logging.critical("严重错误")basicConfig 只在 root logger 没有 handler 时生效,适合脚本级快速使用。
三、生产级用法:自己创建 Logger
import logging
# 1. 创建 logger(名字通常用 __name__,形成层级关系)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 2. 创建 Handler
console_handler = logging.StreamHandler() # 控制台
file_handler = logging.FileHandler("app.log") # 文件
console_handler.setLevel(logging.INFO) # 控制台只看 INFO+
file_handler.setLevel(logging.DEBUG) # 文件记录所有
# 3. 创建 Formatter
fmt = logging.Formatter(
"%(asctime)s [%(levelname)-8s] %(name)s:%(lineno)d - %(message)s"
)
console_handler.setFormatter(fmt)
file_handler.setFormatter(fmt)
# 4. 绑定到 logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.info("服务启动")
logger.debug("详细调试数据")四、Formatter 常用字段
| 占位符 | 含义 |
|---|---|
%(asctime)s |
时间 |
%(levelname)s |
级别名(DEBUG/INFO…) |
%(name)s |
logger 名称 |
%(filename)s / %(lineno)d |
文件名 / 行号 |
%(message)s |
日志内容 |
%(funcName)s |
所在函数名 |
%(process)d / %(thread)d |
进程/线程 ID |
五、Logger 的层级与 propagate
Logger 名字里的 . 表示父子关系:
root
└── myapp
├── myapp.db
└── myapp.webchild = logging.getLogger("myapp.db")
# 默认 propagate=True,消息会向上传递到 myapp、再到 root
# 避免重复输出:
child.propagate = False六、日志轮转(RotatingFileHandler)
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
# 按大小轮转:最大 5MB,保留 3 个备份
handler = RotatingFileHandler(
"app.log", maxBytes=5*1024*1024, backupCount=3
)
# 按时间轮转:每天午夜切割,保留 7 天
handler = TimedRotatingFileHandler(
"app.log", when="midnight", interval=1, backupCount=7
)七、用字典配置(推荐生产使用)
import logging
import logging.config
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {
"format": "%(asctime)s [%(levelname)s] %(name)s - %(message)s"
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "standard",
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "DEBUG",
"formatter": "standard",
"filename": "app.log",
"maxBytes": 10485760,
"backupCount": 5,
},
},
"loggers": {
"myapp": {
"handlers": ["console", "file"],
"level": "DEBUG",
"propagate": False,
}
}
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("myapp")这种方式把配置和代码分离,可以轻松从文件(JSON/YAML)加载。
八、记录异常信息
try:
result = 1 / 0
except ZeroDivisionError:
logger.exception("发生除零错误") # 自动附带完整 traceback
# 等价于:logger.error("...", exc_info=True)九、一个常见坑
# ❌ 错误:字符串拼接在日志过滤前就执行了
logger.debug("用户数据: " + str(big_object))
# ✅ 正确:用 % 占位,只有真正要输出时才格式化
logger.debug("用户数据: %s", big_object)整体用下来,小脚本用 basicConfig,库/框架只创建 Logger 不加 Handler(交给调用者配置),应用程序用 dictConfig 统一管理。有哪一块想深入聊?比如多进程日志安全、结构化日志(JSON),或者怎么在 Django/FastAPI 里集成?