You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python日志格式化变量获取及Django中request_id提取问题

Python日志变量识别与Django请求ID故障展示方案

先说说Python日志里的变量怎么来的:

一、日志变量的识别逻辑

1. 内置变量(比如asctime)

Python的logging模块在生成日志时会自动创建一个LogRecord对象,这个对象自带一堆内置属性——asctime就是其中之一,它是日志记录的时间,会按照你配置的datefmt格式输出。像levelname(日志级别)、message(日志内容)这些也都是LogRecord自带的,不用额外配置,Formatter会自动从里面取值替换格式字符串里的%(xxx)s。

2. 自定义变量(比如request_id)

自定义变量需要你手动把值塞到LogRecord里,Python没有Java的ThreadContext,但有替代方案,核心是用线程局部存储(因为Django每个请求对应单独线程),结合日志过滤器或者适配器来实现:

做法1:用logging.LoggerAdapter手动传参

每次打日志的时候,把request_id作为上下文参数传给适配器:

import logging

logger = logging.getLogger(__name__)
# 把request_id封装到适配器里
adapter = logging.LoggerAdapter(logger, {'request_id': request.request_id})
adapter.info('用户登录操作完成')

这样格式里的%(request_id)s就会被替换成你传的具体值。

做法2:用线程局部存储+日志过滤器(模拟ThreadContext)

这个方法更省心,不用每次打日志都传参,自动给所有日志加上请求ID:

  • 第一步:创建线程局部存储对象,用来存当前请求的上下文
# utils/context.py
import threading

# 这个对象会为每个线程单独存一份数据,互不干扰
local_context = threading.local()
  • 第二步:写Django中间件,生成并存储request_id
# middleware/request_id.py
import uuid
from django.utils.deprecation import MiddlewareMixin
from utils.context import local_context

class RequestIDMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 生成唯一请求ID,也可以从请求头(比如X-Request-ID)里取
        request_id = uuid.uuid4().hex
        request.request_id = request_id
        # 把ID存到线程局部存储里
        local_context.request_id = request_id
  • 第三步:写日志过滤器,把request_id注入到LogRecord
# logging_filters.py
from utils.context import local_context

class RequestIDFilter(logging.Filter):
    def filter(self, record):
        # 从线程存储里取ID,没有就设默认值
        record.request_id = getattr(local_context, 'request_id', 'unknown')
        return True
  • 第四步:修改日志配置,绑定过滤器
# settings.py里的日志配置
'formatters': {
    'simple': {
        'format': '%(asctime)s SCORPIO [%(request_id)s] ' + os.environ['ENVIRONMENT'] + ': %(message)s',
        'datefmt': '%Y-%m-%dT%H:%M:%S',
    },
},
'filters': {
    'request_id': {
        '()': 'myapp.logging_filters.RequestIDFilter',
    },
},
'handlers': {
    'console': {
        'class': 'logging.StreamHandler',
        'formatter': 'simple',
        'filters': ['request_id'],  # 给处理器绑定过滤器
    },
},

这样所有日志都会自动带上当前请求的request_id,不用每次手动传。

二、故障时获取request_id并展示在页面

当Django抛500错误时,你可以这么做:

1. 自定义500错误处理器

在urls.py里指定自定义的错误视图:

# urls.py
handler500 = 'myapp.views.custom_500'

然后写对应的视图函数:

# views.py
from django.shortcuts import render
from utils.context import local_context

def custom_500(request):
    # 从线程存储里拿request_id
    request_id = getattr(local_context, 'request_id', 'unknown')
    # 把ID传给错误页面模板
    return render(request, '500.html', {'request_id': request_id}, status=500)

2. 在错误模板里展示

创建templates/500.html:

<!DOCTYPE html>
<html>
<head>
    <title>服务器出错了</title>
</head>
<body>
    <h1>服务器内部错误</h1>
    <p>麻烦记录这个请求ID,方便我们排查问题:<strong>{{ request_id }}</strong></p>
</body>
</html>

内容的提问来源于stack exchange,提问作者Madhav Kauntia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 09:45:45