Django日志自动记录Request信息的两种方案对比
本文介绍在Django中打印Request请求信息的两种方法。旧方案通过logging extra手动添加,繁琐且不优雅;新方案结合Django中间件与logging Filter,利用threading.local存储请求数据,实现日志自动包含path、request_id等字段。
需求背景
需求
在View处理请求时,若需手动打印日志,通常希望其中包含Request的关键信息。虽然每个请求都记录信息可通过中间件统一解决,但本文仅讨论在特定位置手动打印日志的场景。
#统一附加日志内容
ADD_LOG = r'''{"username": request.user, "path": request.path, "request_id": request.id, "login_id": request.login_id}'''
旧方案:手动添加Extra
旧的解决办法
以往的做法是在每次调用logging记录日志时,通过extra参数手动添加额外信息。这种方式要求开发者在每一处需要打印日志的地方重复添加相同的extra,代码冗余且不优雅。

新方案:中间件与Filter结合
新的解决方案
Django自带的日志系统提供了更完善的官方文档支持,可参考相关章节。
首先需熟悉Python logging模块的基本结构,主要包括以下组件:
- Loggers:负责暴露应用程序代码可以直接使用的接口。
- Handlers:决定日志记录的哪些部分将被输出,例如控制台、文件或网络。
- Filters:用于过滤日志记录,决定哪些记录应被处理。
- Formatters:定义日志记录的最终输出格式。
中间件存储Request
利用Django中间件存储Request信息是实现自动记录的关键步骤。
class RequestLogMiddleware(MiddlewareMixin):
"""
将request的信息记录在当前的请求线程上
"""
def process_request(self, request):
# 统一附加日志内容
# ADD_LOG = r'''{"username": request.user, "path": request.path, "request_id": request.id, "login_id": request.login_id}'''
local.path = request.path
local.request_id = request.id
local.login_id = request.login_id
local.username = request.user.username
Filter注入日志
接着,通过logging的Filters模块添加Request信息,将中间件存储的数据注入到日志记录对象中。
import logging
class RequestLogFilter(logging.Filter):
"""
日志过滤器,将当前请求线程的request信息保存到日志的record上下文
"""
def filter(self, record):
record.request_id = getattr(local, 'request_id', "none")
record.path = getattr(local, 'path', "none")
record.login_id = getattr(local, 'login_id', "none")
record.username = getattr(local, 'username', "none")
record.appName = getattr(local, "appName", "none")
return True
代码实现与配置
以下是实现原理及具体代码示例。
核心思路是通过local = threading.local()来存储当前线程的Request数据。
中间件代码(middleware-waiwen文件)如下:
import threading
import logging
try:
from django.utils.deprecation import MiddlewareMixin # Django 1.10.x
except ImportError:
MiddlewareMixin = object # Django 1.4.x - Django 1.9.x
local = threading.local()
class RequestLogFilter(logging.Filter):
"""
日志过滤器,将当前请求线程的request信息保存到日志的record上下文
record带有formater需要的信息。
"""
def filter(self, record):
record.request_id = getattr(local, 'request_id', "none")
record.path = getattr(local, 'path', "none")
record.login_id = getattr(local, 'login_id', "none")
record.username = getattr(local, 'username', "none")
return True
class RequestLogMiddleware(MiddlewareMixin):
"""
将request的信息记录在当前的请求线程上。
"""
def process_request(self, request):
# 统一附加日志内容
# ADD_LOG = r'''{"username": request.user, "path": request.path, "request_id": request.id, "login_id": request.login_id}'''
local.path = request.path
local.request_id = request.id
local.login_id = request.login_id
local.username = request.user.username
在settings文件中进行相应配置,包括中间件注册和日志格式定义。
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'wcloud.middleware-waiwen.RequestLogMiddleware'
#使用该中间件
#将当前的request信息保存到当前线程供日志打印使用
]
LOGGING = {
# 日志相关
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '{"date": "%(created)f", "level": "%(levelname)s", "funcName": "%(module)s.%(funcName)s:%(lineno)d", "msg": "%(message)s"}'}, # 日志格式
'custom': {
#该格式化中包含有过滤器record新增的字段
'format': '{"date": "%(created)f", "level": "%(levelname)s", "funcName": "%(module)s.%(funcName)s:%(lineno)d", "request_id": "%(request_id)s","login_id": "%(login_id)s", "username": "%(username)s", "path": "%(path)s","msg": "%(message)s"}'
},
},
'filters': {
#注册该过滤器
'request_info': {'()': 'wcloud.middleware-waiwen.RequestLogFilter'}
},
'handlers': {
'log': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'custom',
#在该过handler中使用该过滤器
'filters': ['request_info'],
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'standard',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': False
},
'django.request': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': False
},
'django.db.backens': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False
},
'log': {
'handlers': ['log'],
'level': 'INFO',
'propagate': True
},
}
}
效果验证
配置完成后,查看实际运行效果,日志中应自动包含Request相关信息。

参考:
给Django日志加上request_id
总结
到此这篇关于Django日志默认打印Request请求信息的文章就介绍到这了,更多相关Django日志默认打印Request请求信息内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













