Python logging 模块
Levels, handlers, formatters, and the difference between basicConfig and a real setup.
Key PointsLibrary code should only call `logger.info(...)` and never configure logging — configuration belongs to the application entry point.
Levels
Levels go DEBUG < INFO < WARNING < ERROR < CRITICAL. Setting INFO drops DEBUG records.
Python
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s %(message)s")
log = logging.getLogger("app")
log.debug("不可见")
log.info("启动完成")
log.warning("磁盘 85%")
log.error("连接失败")Output
INFO app 启动完成
WARNING app 磁盘 85%
ERROR app 连接失败A Real Setup
basicConfig is for scripts. Real services build handlers: console plus a rotating file, with one format.
Python
import logging
from logging.handlers import RotatingFileHandler
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s")
console = logging.StreamHandler()
console.setFormatter(fmt)
file_h = RotatingFileHandler("app.log", maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8")
file_h.setFormatter(fmt)
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
logger.addHandler(console)
logger.addHandler(file_h)
logger.info("service started")Output
2026-10-31 10:40:12,331 INFO app service startedRules
| Rule | Why |
|---|---|
| Use lazy formatting: log.info("%s", x) | Avoids building strings when the level is off |
| One logger per module: getLogger(__name__) | Filtering by module becomes possible |
| Never log secrets or full card numbers | Logs get shipped and stored |
| Log exceptions with exc_info=True | Keeps the traceback |
Python
try:
1 / 0
except ZeroDivisionError:
logger.exception("计算失败") # 等价于 error(..., exc_info=True)print has no level, no timestamp and cannot be turned off. Fine while debugging, logging in production.日志级别
级别从低到高是 DEBUG < INFO < WARNING < ERROR < CRITICAL。设成 INFO 就只输出 INFO 及以上,DEBUG 会被丢弃。
Python
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s %(message)s")
log = logging.getLogger("app")
log.debug("不可见")
log.info("启动完成")
log.warning("磁盘 85%")
log.error("连接失败")Output
INFO app 启动完成
WARNING app 磁盘 85%
ERROR app 连接失败生产配置
basicConfig 只适合脚本。真实服务要自己建 handler:控制台一份、文件一份(按大小切割),并统一格式。
Python
import logging
from logging.handlers import RotatingFileHandler
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s")
console = logging.StreamHandler()
console.setFormatter(fmt)
file_h = RotatingFileHandler("app.log", maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8")
file_h.setFormatter(fmt)
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
logger.addHandler(console)
logger.addHandler(file_h)
logger.info("service started")Output
2026-10-31 10:40:12,331 INFO app service started使用规范
| 规范 | 原因 |
|---|---|
| 用惰性格式化:log.info("%s", x) | 级别未开启时不必拼字符串 |
| 每个模块一个 logger:getLogger(__name__) | 可以按模块过滤 |
| 不要记录密钥与完整卡号 | 日志会被采集与长期保存 |
| 记录异常要带 exc_info=True | 保留调用栈 |
Python
try:
1 / 0
except ZeroDivisionError:
logger.exception("计算失败") # 等价于 error(..., exc_info=True)print 没有级别、没有时间戳、不能关。调试可以 print,上线必须 logging。