Django 日志实战:settings.py 配置、请求日志与生产化落地
Django 日志怎么配?本文讲解 Django 基于标准库 logging 的 LOGGING 配置字典、内置 logger 体系(django.request 等)、自定义业务 logger、生产 JSON 结构化与多 handler 分流,并给出接入观测云实现集中检索与告警的路径。
Django 日志基于 Python 标准库 logging 模块实现,通过 settings.py 中的 LOGGING 配置字典统一定义格式、handler 与 logger,同时框架内置了 django.request、django.server 等一批按职责划分的 logger。 理解"Django 自己没有发明日志体系,而是给标准库包了一层配置约定",就抓住了全部关键。
核心要点速览
- Django 日志 = 标准库 logging + settings.py 里的
LOGGING字典(dictConfig 格式); - 内置 logger 各有分工:
django.request记 4xx/5xx、django.server记开发服务器请求、django.db.backends记 SQL; - 业务代码用
logging.getLogger(__name__)建模块 logger,配置集中在 settings.py; - 生产 JSON 化用 python-json-logger,采集上报交给 DataKit + 观测云。
内置 logger 都在记什么?
| Logger | 记录内容 |
|---|---|
django |
兜底父 logger,捕获未细分的事件 |
django.request |
HTTP 请求相关:5xx 记 ERROR、4xx 记 WARNING |
django.server |
runserver 开发服务器的请求行 |
django.db.backends |
数据库 SQL 语句(DEBUG 级) |
django.security.* |
安全事件(SuspiciousOperation 等) |
不给任何配置时,Django 默认只把 django logger 的 WARNING 及以上输出到控制台——所以默认状态下你看到的大多是 500 报错。
LOGGING 配置字典怎么写?
# settings.py
LOGGING = {
"version": 1,
"disable_existing_loggers": False, # 关键:别禁用 Django 内置 logger
"formatters": {
"verbose": {
"format": "{asctime} | {levelname} | {name} | {message}",
"style": "{",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "verbose",
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"filename": BASE_DIR / "logs" / "django.log",
"maxBytes": 10 * 1024 * 1024,
"backupCount": 5,
"formatter": "verbose",
},
},
"loggers": {
"django": {"handlers": ["console", "file"], "level": "INFO"},
"myapp": {"handlers": ["console", "file"], "level": "DEBUG", "propagate": False},
},
}
注意 disable_existing_loggers: False——设成 True 会把 Django 内置 logger 全部静默,是很多"配置后反而没日志"事故的根源。
业务代码中:
# myapp/views.py
import logging
logger = logging.getLogger(__name__)
def order_create(request):
logger.info("创建订单", extra={"user_id": request.user.id})
try:
...
except Exception:
logger.exception("订单创建失败")
生产化:JSON 输出与请求 ID
Web 服务的日志最终要进平台检索,JSON 是必选项:
"formatters": {
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"format": "%(asctime)s %(levelname)s %(name)s %(message)s",
"rename_fields": {"levelname": "level", "asctime": "time"},
},
},
请求级追踪可以加一个简单中间件,为每个请求生成 request_id 并通过 LoggerAdapter 或 extra 透传;需要更完整的方案(自动绑定 IP、UA、用户、request_id)可直接用 django-structlog(见本系列《Structlog 实战指南》)。
接入观测云
观测云落地:部署形态决定采集方式——容器化部署让 Django(经 Gunicorn/uWSGI)输出 JSON 到 stdout,DataKit 容器采集;虚拟机部署采集 logs/django.log 文件 。Pipeline 确认 time/status 标准字段映射(ERROR/CRITICAL → error,WARNING → warning)。随后:
- 日志查看器:按
name: django.request过滤框架日志,按自定义字段(user_id、request_id)追踪业务链路 ; - 监控器(日志检测):对"5xx 计数超阈值""
SuspiciousOperation出现"等配置告警,推送钉钉/企业微信/飞书 ; - 链路关联:接入观测云 APM(支持 OpenTelemetry/ddtrace 方式的 Python 探针)并把 trace_id 注入日志,实现 Trace 与日志双向跳转 。
总结
Django 日志三板斧:LOGGING 字典集中配置、disable_existing_loggers 保持 False、生产 JSON 输出交给采集器。框架自带的 django.request 已经帮你盯住 4xx/5xx,剩下的就是把业务 logger 建起来、把日志汇入观测云。
常见问题(FAQ)
Q:配了 LOGGING 后 Django 自带的报错日志不见了?
多半是设置了 disable_existing_loggers: True,它会禁用所有已存在的 logger(包括 Django 内置的)。改为 False 即可。
Q:如何在日志里记录每个请求的 ID?
轻量做法:写中间件生成 uuid 放入 request,视图里通过 extra={"request_id": ...} 传递;完整做法:使用 django-structlog,中间件自动绑定 request_id/IP/UA 到全部请求日志。
Q:Django 的 SQL 日志能在生产开吗?
不建议。django.db.backends 的 DEBUG 级 SQL 日志量巨大且可能含敏感参数。排查慢查询请用观测云 APM 的数据库追踪能力,而不是开 SQL 日志。
Q:Gunicorn 部署后日志去哪了?
Gunicorn 有自己的 access/error 日志配置(--access-logfile - 输出到 stdout)。容器化部署把所有输出(Django + Gunicorn)都指向 stdout,由 DataKit 统一采集即可。
系列阅读:Python 日志模块详解 | Flask 日志实战