print 的问题
print("用户登录:", user.name)
写脚本可以,写应用就不行:
- 开发期想看,生产期不想看 → 没法关
- 错误和正常信息混在一起 → 没法过滤
- 没时间戳 / 模块名 / 行号 → 排查难
- 不能写到文件 → 重启就丢
logging 模块解决全部。
最快上手
import logging
logging.basicConfig(level=logging.INFO)
logging.info("应用启动")
logging.warning("配置缺失,使用默认")
logging.error("数据库连接失败")
输出:
INFO:root:应用启动
WARNING:root:配置缺失,使用默认
ERROR:root:数据库连接失败
五个级别
| 级别 | 何时用 |
|---|---|
DEBUG |
开发期排查细节 |
INFO |
关键事件(启动 / 用户登录 / 任务完成) |
WARNING |
异常但能继续(配置缺失、降级) |
ERROR |
单次失败(API 调用失败、文件读不到) |
CRITICAL |
系统崩了(无法继续运行) |
level=INFO 表示 INFO 及以上都会打印,DEBUG 不打。
用模块级 logger(推荐姿势)
import logging
logger = logging.getLogger(__name__) # 用模块名做 logger 名
def do_work():
logger.info("开始工作")
try:
...
except Exception:
logger.exception("工作失败") # 自动附 traceback
永远 getLogger(__name__),别直接用 logging.info()——前者能精确控制每个模块的级别。
漂亮的输出格式
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)-7s | %(name)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
输出:
2026-05-09 14:23:01 | INFO | myapp.web | 用户登录: WadeLy
日志写到文件 + 同时显示在终端
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
logging.FileHandler("app.log"),
logging.StreamHandler(), # 终端
],
)
日志轮转:避免文件无限大
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
"app.log", maxBytes=5_000_000, backupCount=5
)
# 单文件最大 5MB,保留 5 份历史
logging.basicConfig(handlers=[handler], level=logging.INFO)
logger.exception:错误自动带 traceback
try:
risky()
except Exception:
logger.exception("risky 失败")
# 输出会自带 traceback,不用手动拼
异常打印的"懒求值"
# 坏:永远计算字符串
logger.debug(f"data = {expensive_func()}")
# 好:只在 DEBUG 时计算
logger.debug("data = %s", expensive_func())
logging 内部判断级别后才格式化字符串——传 %s + 参数比 f-string 高效。
structlog:结构化日志(生产推荐)
pip install structlog
import structlog
log = structlog.get_logger()
log.info("user_login", user_id=42, ip="1.2.3.4")
# 2026-05-09 14:23 [info] user_login user_id=42 ip=1.2.3.4
字段化输出,配合 ELK / Loki 等日志系统超好用。
一个项目的标准 logging 入口
# logging_config.py
import logging
from logging.handlers import RotatingFileHandler
def setup():
fmt = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
logging.basicConfig(
level=logging.INFO,
format=fmt,
handlers=[
RotatingFileHandler("app.log", maxBytes=10_000_000, backupCount=5),
logging.StreamHandler(),
],
)
# main.py
from logging_config import setup
setup()
下一篇讲测试 pytest。