Django 从 0 到 1 打造完整电商平台:Django 日志与异常处理 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配置。我们将同时满足开发调试和生产运行的需求。importos# Django 日志配置 LOGS_DIRBASE_DIR /logsifnot 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,# 只在 DEBUGFalse 时生效},require_debug_true:{():django.utils.log.RequireDebugTrue,# 只在 DEBUGTrue 时生效},},# 处理器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,},},}配置解读开发环境DEBUGTrue日志输出到控制台便于实时查看。生产环境DEBUGFalseINFO 以上日志写入文件并按天轮转ERROR 单独记录严重错误发送邮件给管理员。TimedRotatingFileHandler每天午夜自动归档旧日志保留指定天数避免日志文件无限膨胀。AdminEmailHandler当DEBUGFalse时自动把 ERROR 级别的错误邮件发送给settings.ADMINS中配置的管理员。四、在代码中记录日志4.1 在各模块中获取 Logger我们已经在第 21 篇的支付视图和订单模型中用了日志现在统一规范importlogging# 在支付 views.py 中loggerlogging.getLogger(payment)# 在订单 models.py 中loggerlogging.getLogger(orders)# 在用户 views.py 中loggerlogging.getLogger(users)# 在购物车 views.py 中loggerlogging.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_infoTrue)exc_infoTrue会把完整的异常堆栈写入日志对排查问题极其有用。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即可。loggerlogging.getLogger(users)shared_task def send_sms_task(phone, code): logger.info(f异步发送短信到 {phone}验证码{code})# ...五、自定义错误页面Django 默认在DEBUGFalse时404 会返回简陋的页面500 会显示“服务器内部错误”。我们需要提供友好美观的错误页面。5.1 创建错误模板我们之前已经在第 5 篇建了404.html和500.html现在确认它们存在于templates/目录下并符合 Bootstrap 风格。如果需要更新可调整内容。确保它们使用了base.html的继承结构注意 500 页面可能无法加载静态文件需内联样式。500 页面建议内联 CSS因为服务器错误时静态文件可能无法访问templates/500.html!DOCTYPE htmlhtmllangzh-CNheadmetacharsetUTF-8title服务器错误 -500/titlestylebody{padding-top: 100px;text-align: center;font-family: sans-serif;background-color:#f8f9fa; }h1{font-size: 80px;color:#dc3545; } p { color: #6c757d; }/style/headbodyh1500/h1p服务器开小差了请稍后重试。/pahref/返回首页/a/body/html5.2 在视图中触发 404/500Django 会自动调用handler404和handler500视图默认就是根据DEBUG显示模板。我们已经在django_ecommerce/urls.py中配置了handler404和handler500如果没有可添加handler404django.views.defaults.page_not_foundhandler500django.views.defaults.server_errorDjango 会自动查找404.html和500.html无需额外配置。5.3 在视图中主动记录异常对于可预期的错误如库存不足、支付失败我们使用logger.warning或logger.error并给用户友好提示。对于不可预期的异常使用try/except并记录try:# 一些可能出错的操作resultsome_service.call()except Exception as e: logger.exception(f操作失败{request.user} - {request.path})messages.error(request,系统繁忙请稍后重试。)returnredirect(home)logger.exception()会自动附带异常堆栈等同于logger.error(exc_infoTrue)。六、生产环境日志轮转与持久化我们已经在 handlers 中使用了TimedRotatingFileHandler它是生产环境最常用的方案。配置文件中的whenmidnight表示每天零点轮转backupCount30表示保留最近 30 天的日志旧文件自动删除。生成的文件名类似django.log django.log.2026-05-25 error.log error.log.2026-05-24这样可以避免单一日志文件过大也方便按日期检索问题。七、邮件告警配置当DEBUGFalse且发生服务器错误HTTP 500时Django 会自动通过AdminEmailHandler发送邮件给ADMINS配置中的人员。我们需要在settings.py中配置# 管理员列表用于接收错误报告ADMINS[(IT策士,adminexample.com)]# 生产环境邮件配置使用真实 SMTPEMAIL_BACKENDdjango.core.mail.backends.smtp.EmailBackendEMAIL_HOSTsmtp.example.comEMAIL_PORT587EMAIL_HOST_USERnoreplyexample.comEMAIL_HOST_PASSWORDyour-email-passwordEMAIL_USE_TLSTrue当发生 500 错误时管理员会收到一封包含详细 Traceback 的邮件便于第一时间发现问题。八、集成 Celery 日志Celery 本身的日志可以通过配置CELERYD_HIJACK_ROOT_LOGGER False来与 Django 日志系统集成避免 Celery 接管根日志。在settings.py中添加CELERYD_HIJACK_ROOT_LOGGERFalse CELERY_WORKER_LOG_FORMAT[%(asctime)s] %(levelname)s [%(name)s] %(message)sCELERY_TASK_LOG_FORMAT[%(asctime)s] %(levelname)s [%(task_name)s] %(message)s这样 Celery Worker 的日志也会使用我们配置的格式并可以写入同一个文件或单独的 Celery 日志文件。我们可以再添加一个celery_filehandler 单独存储 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 开发环境测试启动开发服务器DEBUGTrue访问任意页面控制台会输出类似[2026-05-2810:20:15]INFO[django.request]GET /products/list/200[2026-05-2810:20:18]INFO[users]用户13800138000登录成功执行一个会触发错误操作如访问不存在的订单观察error.log文件如果配置了文件输出开发环境也生效或控制台的错误输出。9.2 模拟生产环境临时将DEBUGFalse并配置好 EMAIL然后访问一个不存在的页面会触发 404 页面并记录到request.log触发 500 错误例如在视图中故意raise Exception则error.log会记录管理员会收到邮件。查看日志文件catlogs/django.logcatlogs/error.log内容示例[2026-05-2810:30:22]ERROR[payment:85]订单 20260528103022X7K9M2 支付失败签名验证失败 Traceback(most recent call last): File/app/apps/payment/views.py, line82,inpayment_return... AlipaySignError: Sign verification failed十、总结与下集预告今天我们为项目搭建了一套完整的日志和异常处理体系使用 DjangoLOGGING配置了控制台、文件、邮件等多种处理器按模块划分了 Loggerpayment、orders、users、cart实现业务级追踪通过TimedRotatingFileHandler实现了日志的自动轮转和留存配置了友好的 404/500 错误页面捕获异常并记录集成了 Celery 日志统一管理。日志到位后项目就有了“诊断能力”。但还有一处性能隐患——数据库查询。频繁的 ORM 查询可能成为瓶颈。第 26 篇我将带大家深度优化数据库查询与索引从select_related、prefetch_related到数据库索引设计让每一步数据库访问都精确高效。想了解更多还可以去公众号、今日头条搜索「IT策士」一起升级 IT 思维 *本文为《Django 从 0 到 1 打造完整电商平台》系列第 25 篇。*