Django 从 0 到 1 打造完整电商平台:Django 日志与异常处理
本文介绍了如何在Django电商项目中构建完善的日志与异常处理系统。主要内容包括: 日志系统的重要性:作为系统的"黑匣子",记录关键信息便于排查问题;异常处理则保护用户体验。 Django日志配置详解: 开发环境输出到控制台 生产环境按日志级别分离存储(INFO/ERROR) 支持日志轮转和邮件告警 为不同模块配置专属Logger 日志记录规范: 使用模块级Logger 合理选择日志级别 记录关键
IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在公众号、今日头条持续发布最新文章,助你少走弯路。
前面 24 篇,我们把电商核心功能全部写完,性能优化也做了缓存和异步。但还有个关键模块一直被忽视:日志与异常处理。没有日志,系统就像在黑箱中运行,出了 Bug 只能靠猜;没有统一的异常处理,用户看到的可能就是丑陋的黄色错误页面或者空白页,体验极差。
今天我们就来搭建一套完善的日志体系,涵盖请求追踪、业务日志、异常捕获、邮件告警,并配置好生产环境下的日志轮转。同时,我们会定制友好的错误页面,让项目从“能用”升级到“好用、好维护”。
一、为什么日志和异常处理如此重要
想象一下这些场景:
-
用户支付成功,但订单状态未更新,你如何排查?
-
某个接口突然返回 500,却没有留下任何记录,运维一脸茫然。
-
恶意用户频繁访问不存在的地址,你不知道,服务器资源被白白消耗。
日志 就是系统的“黑匣子”,它记录了系统运行时的一切关键信息。异常处理 则是系统的“安全气囊”,在出错时保护用户体验,并收集必要的诊断信息。两者结合,才能真正做到可观测、可追溯。
二、Django 日志系统基础
Django 使用 Python 标准的 logging 模块,通过 settings.py 中的 LOGGING 字典进行配置。它由四个核心组件组成:
一条日志的流转路径:
代码调用 logger.info('xxx')
→ Logger 根据级别决定是否处理
→ 传递给 Handler
→ Handler 根据级别和 Filter 决定是否输出
→ 使用 Formatter 格式化
→ 输出到目标(控制台/文件/邮件)
三、配置项目日志
打开 django_ecommerce/settings.py,在文件末尾添加完整的 LOGGING 配置。我们将同时满足开发调试和生产运行的需求。
import os
# ==================== Django 日志配置 ====================
LOGS_DIR = BASE_DIR / 'logs'
if not os.path.exists(LOGS_DIR):
os.makedirs(LOGS_DIR)
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 保留 Django 默认 logger
# 格式化器
'formatters': {
'simple': {
'format': '[{asctime}] {levelname} [{name}] {message}',
'style': '{',
},
'verbose': {
'format': '[{asctime}] {levelname} [{name}:{lineno}] {message}',
'style': '{',
},
'request_format': {
'format': '[{asctime}] {levelname} [{name}] {message} | IP:{client_ip} Path:{path}',
'style': '{',
},
},
# 过滤器
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse', # 只在 DEBUG=False 时生效
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue', # 只在 DEBUG=True 时生效
},
},
# 处理器
'handlers': {
# 开发环境:控制台输出
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
'filters': ['require_debug_true'],
},
# 生产环境:所有 INFO 以上日志写入文件
'file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOGS_DIR / 'django.log',
'when': 'midnight', # 每天午夜轮转
'backupCount': 30, # 保留 30 天
'formatter': 'verbose',
'encoding': 'utf-8',
},
# 错误日志单独存放
'error_file': {
'level': 'ERROR',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOGS_DIR / 'error.log',
'when': 'midnight',
'backupCount': 90,
'formatter': 'verbose',
'encoding': 'utf-8',
},
# 请求日志
'request_file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOGS_DIR / 'request.log',
'when': 'midnight',
'backupCount': 30,
'formatter': 'request_format',
'encoding': 'utf-8',
},
# 严重错误邮件告警(生产环境配置 EMAIL 后生效)
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['require_debug_false'],
'include_html': True,
},
},
# 日志记录器
'loggers': {
# Django 主日志
'django': {
'handlers': ['console', 'file'],
'level': 'INFO',
'propagate': False,
},
# 请求日志
'django.request': {
'handlers': ['request_file', 'mail_admins'],
'level': 'INFO',
'propagate': False,
},
# 服务端错误
'django.server': {
'handlers': ['error_file'],
'level': 'ERROR',
'propagate': False,
},
# 支付模块日志
'payment': {
'handlers': ['console', 'file', 'error_file'],
'level': 'INFO',
'propagate': False,
},
# 订单模块日志
'orders': {
'handlers': ['console', 'file', 'error_file'],
'level': 'INFO',
'propagate': False,
},
# 用户模块日志
'users': {
'handlers': ['console', 'file'],
'level': 'INFO',
'propagate': False,
},
# 购物车日志
'cart': {
'handlers': ['console', 'file'],
'level': 'INFO',
'propagate': False,
},
},
}
配置解读:
-
开发环境(
DEBUG=True):日志输出到控制台,便于实时查看。 -
生产环境(
DEBUG=False):INFO 以上日志写入文件并按天轮转;ERROR 单独记录;严重错误发送邮件给管理员。 -
TimedRotatingFileHandler:每天午夜自动归档旧日志,保留指定天数,避免日志文件无限膨胀。 -
AdminEmailHandler:当DEBUG=False时,自动把 ERROR 级别的错误邮件发送给settings.ADMINS中配置的管理员。
四、在代码中记录日志
4.1 在各模块中获取 Logger
我们已经在第 21 篇的支付视图和订单模型中用了日志,现在统一规范:
import logging
# 在支付 views.py 中
logger = logging.getLogger('payment')
# 在订单 models.py 中
logger = logging.getLogger('orders')
# 在用户 views.py 中
logger = logging.getLogger('users')
# 在购物车 views.py 中
logger = logging.getLogger('cart')
4.2 记录关键业务操作
在 apps/orders/views.py 的 order_submit 中,增加详细日志:
logger.info(f'用户 {user.username} 提交订单,订单号={order.order_no},金额={total_amount},地址ID={address_id}')
支付成功时:
logger.info(f'订单 {out_trade_no} 支付成功,交易号={trade_no}')
支付失败/异常时:
logger.error(f'订单 {out_trade_no} 支付失败:{str(e)}', exc_info=True)
exc_info=True 会把完整的异常堆栈写入日志,对排查问题极其有用。
4.3 记录用户行为
在 apps/users/views.py 的注册、登录、修改密码等操作中加入日志:
# 注册
logger.info(f'新用户注册:{user.username},手机号={phone},邮箱={email}')
# 登录
logger.info(f'用户 {user.username} 登录成功')
# 密码修改
logger.info(f'用户 {user.username} 修改了密码')
4.4 记录 Celery 任务日志
在 apps/users/tasks.py 中,任务函数也使用 logging,日志会被 Celery 自动捕获并输出到 Worker 控制台,同时也写入我们在 settings 配置的文件中(如果 Worker 的进程能访问该日志文件)。不过 Celery 日志通常单独配置,这里我们在任务内使用 logger.info 即可。
logger = logging.getLogger('users')
@shared_task
def send_sms_task(phone, code):
logger.info(f'异步发送短信到 {phone},验证码={code}')
# ...
五、自定义错误页面
Django 默认在 DEBUG=False 时,404 会返回简陋的页面,500 会显示“服务器内部错误”。我们需要提供友好美观的错误页面。
5.1 创建错误模板
我们之前已经在第 5 篇建了 404.html 和 500.html,现在确认它们存在于 templates/ 目录下,并符合 Bootstrap 风格。如果需要更新,可调整内容。确保它们使用了 base.html 的继承结构(注意 500 页面可能无法加载静态文件,需内联样式)。
500 页面建议内联 CSS(因为服务器错误时静态文件可能无法访问):
templates/500.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>服务器错误 - 500</title>
<style>
body { padding-top: 100px; text-align: center; font-family: sans-serif; background-color: #f8f9fa; }
h1 { font-size: 80px; color: #dc3545; } p { color: #6c757d; }
</style>
</head>
<body>
<h1>500</h1>
<p>服务器开小差了,请稍后重试。</p>
<a href="/">返回首页</a>
</body>
</html>
5.2 在视图中触发 404/500
Django 会自动调用 handler404 和 handler500 视图,默认就是根据 DEBUG 显示模板。我们已经在 django_ecommerce/urls.py 中配置了 handler404 和 handler500(如果没有,可添加):
handler404 = 'django.views.defaults.page_not_found'
handler500 = 'django.views.defaults.server_error'
Django 会自动查找 404.html 和 500.html,无需额外配置。
5.3 在视图中主动记录异常
对于可预期的错误(如库存不足、支付失败),我们使用 logger.warning 或 logger.error,并给用户友好提示。对于不可预期的异常,使用 try/except 并记录:
try:
# 一些可能出错的操作
result = some_service.call()
except Exception as e:
logger.exception(f'操作失败:{request.user} - {request.path}')
messages.error(request, '系统繁忙,请稍后重试。')
return redirect('home')
logger.exception() 会自动附带异常堆栈,等同于 logger.error(exc_info=True)。
六、生产环境日志轮转与持久化
我们已经在 handlers 中使用了 TimedRotatingFileHandler,它是生产环境最常用的方案。配置文件中的 when='midnight' 表示每天零点轮转,backupCount=30 表示保留最近 30 天的日志,旧文件自动删除。
生成的文件名类似:
django.log
django.log.2026-05-25
error.log
error.log.2026-05-24
这样可以避免单一日志文件过大,也方便按日期检索问题。
七、邮件告警配置
当 DEBUG=False 且发生服务器错误(HTTP 500)时,Django 会自动通过 AdminEmailHandler 发送邮件给 ADMINS 配置中的人员。我们需要在 settings.py 中配置:
# 管理员列表,用于接收错误报告
ADMINS = [('IT策士', 'admin@example.com')]
# 生产环境邮件配置(使用真实 SMTP)
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'noreply@example.com'
EMAIL_HOST_PASSWORD = 'your-email-password'
EMAIL_USE_TLS = True
当发生 500 错误时,管理员会收到一封包含详细 Traceback 的邮件,便于第一时间发现问题。
八、集成 Celery 日志
Celery 本身的日志可以通过配置 CELERYD_HIJACK_ROOT_LOGGER = False 来与 Django 日志系统集成,避免 Celery 接管根日志。在 settings.py 中添加:
CELERYD_HIJACK_ROOT_LOGGER = False
CELERY_WORKER_LOG_FORMAT = '[%(asctime)s] %(levelname)s [%(name)s] %(message)s'
CELERY_TASK_LOG_FORMAT = '[%(asctime)s] %(levelname)s [%(task_name)s] %(message)s'
这样 Celery Worker 的日志也会使用我们配置的格式,并可以写入同一个文件(或单独的 Celery 日志文件)。我们可以再添加一个 celery_file handler 单独存储 Celery 日志:
'celery_file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOGS_DIR / 'celery.log',
'when': 'midnight',
'backupCount': 30,
'formatter': 'verbose',
},
并在 loggers 中加入:
'celery': {
'handlers': ['celery_file'],
'level': 'INFO',
'propagate': False,
},
九、测试日志输出
9.1 开发环境测试
启动开发服务器(DEBUG=True),访问任意页面,控制台会输出类似:
[2026-05-28 10:20:15] INFO [django.request] GET /products/list/ 200
[2026-05-28 10:20:18] INFO [users] 用户 13800138000 登录成功
执行一个会触发错误操作(如访问不存在的订单),观察 error.log 文件(如果配置了文件输出,开发环境也生效)或控制台的错误输出。
9.2 模拟生产环境
临时将 DEBUG=False,并配置好 EMAIL,然后访问一个不存在的页面,会触发 404 页面并记录到 request.log;触发 500 错误(例如在视图中故意 raise Exception),则 error.log 会记录,管理员会收到邮件。
查看日志文件:
cat logs/django.log
cat logs/error.log
内容示例:
[2026-05-28 10:30:22] ERROR [payment:85] 订单 20260528103022X7K9M2 支付失败:签名验证失败
Traceback (most recent call last):
File "/app/apps/payment/views.py", line 82, in payment_return
...
AlipaySignError: Sign verification failed
十、总结与下集预告
今天我们为项目搭建了一套完整的日志和异常处理体系:
-
使用 Django
LOGGING配置了控制台、文件、邮件等多种处理器; -
按模块划分了 Logger(payment、orders、users、cart),实现业务级追踪;
-
通过
TimedRotatingFileHandler实现了日志的自动轮转和留存; -
配置了友好的 404/500 错误页面,捕获异常并记录;
-
集成了 Celery 日志,统一管理。
日志到位后,项目就有了“诊断能力”。但还有一处性能隐患——数据库查询。频繁的 ORM 查询可能成为瓶颈。第 26 篇,我将带大家深度优化 数据库查询与索引,从 select_related、prefetch_related 到数据库索引设计,让每一步数据库访问都精确高效。
想了解更多还可以去公众号、今日头条搜索「IT策士」,一起升级 IT 思维 !
*本文为《Django 从 0 到 1 打造完整电商平台》系列第 25 篇。
*
更多推荐

所有评论(0)