本文通过Django中间件的流程,实现操作日志记录的功能,模块化、拿来即用。
功能描述:通过中间件记录 请求时间、操作用户、请求URL、请求方法、请求IP、请求参数和响应数据、响应耗时等数据日志,而且可以自定义exclude_urls列表,访问列表中的url,不会保存操作日志。另外,通过设置的响应时间阈值(可配置化),将超过阈值的操作日志进行单独保存,便于分析。
说明:示例中是将数据记录在MySQL数据库中,如果你想写入log日志,只需将数据入库改为log格式写入即可(数据都放在self.data中,取用方便)。
创建中间件
在app下新建文件夹middlewares, 在文件夹下新建中间件文件LogMiddleware.py
在中间件文件中新建一个类, 继承MiddlewareMixin:
from django.utils.deprecation import MiddlewareMixin
class OpLogs(MiddlewareMixin):
def process_request(self, request):
pass
1
2
3
4
5
6
在settings中注册中间件:
自定义中间件
MIDDLEWARE += [
‘app01.middlewares.LogMiddleware.OpLogs’
]
1
2
3
4
功能实现
LogModdleware.py中间件:
获取需要记录的参数,并计算请求耗时,将参数入库。
-- coding:utf-8 --
“”"
@File : LogMiddleware.py
@Author : Python
@Date : 2021/7/20 14:00
“”"
import time
import json
from django.utils.deprecation import MiddlewareMixin
from app.models import OpLogs, AccessTimeOutLogs
class OpLogs(MiddlewareMixin):
__exclude_urls = ['index/'] # 定义不需要记录日志的url名单
def __init__(self, *args):
super(OpLog, self).__init__(*args)
self.start_time = None # 开始时间
self.end_time = None # 响应时间
self.data = {} # dict数据
def process_request(self, request):
"""
请求进入
:param request: 请求对象
:return:
"""
self.start_time = time.time() # 开始时间
re_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) # 请求时间(北京)
# 请求IP
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
# 如果有代理,获取真实IP
re_ip = x_forwarded_for.split(",")[0]
else:
re_ip = request.META.get('REMOTE_ADDR')
# 请求方法
re_method = request.method
# 请求参数
re_content = request.GET if re_method == 'GET' else request.POST
if re_content:
# 筛选空参数
re_content = json.dumps(re_content)
else:
re_content = None
self.data.update(
{
're_time': re_time, # 请求时间
're_url': request.path, # 请求url
're_method': re_method, # 请求方法
're_ip': re_ip, # 请求IP
're_content': re_content, # 请求参数
# 're_user': request.user.username # 操作人(需修改),网站登录用户
're_user': 'AnonymousUser' # 匿名操作用户测试
}
)
def process_response(self, request, response):
"""
响应返回
:param request: 请求对象
:param response: 响应对象
:return: response
"""
# 请求url在 exclude_urls中,直接return,不保存操作日志记录
for url in self.__exclude_urls:
if url in self.data.get('re_url'):
return response
# 获取响应数据字符串(多用于API, 返回JSON字符串)
rp_content = response.content.decode()
self.data['rp_content'] = rp_content
# 耗时
self.end_time = time.time() # 响应时间
access_time = self.end_time - self.start_time
self.data['access_time'] = round(access_time * 1000) # 耗时毫秒/ms
# 耗时大于3s的请求,单独记录 (可将时间阈值设置在settings中,实现可配置化)
if self.data.get('access_time') > 3 * 1000:
AccessTimeOutLogs.objects.create(**self.data) # 超时操作日志入库db
OpLogs.objects.create(**self.data) # 操作日志入库db
return response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
app01.models 数据库模型:
from django.db import models
class OpLogs(models.Model):
“”“操作日志表”“”
id = models.AutoField(primary_key=True)
re_time = models.CharField(max_length=32, verbose_name='请求时间')
re_user = models.CharField(max_length=32, verbose_name='操作人')
re_ip = models.CharField(max_length=32, verbose_name='请求IP')
re_url = models.CharField(max_length=255, verbose_name='请求url')
re_method = models.CharField(max_length=11, verbose_name='请求方法')
re_content = models.TextField(null=True, verbose_name='请求参数')
rp_content = models.TextField(null=True, verbose_name='响应参数')
access_time = models.IntegerField(verbose_name='响应耗时/ms')
class Meta:
db_table = 'op_logs'
class AccessTimeOutLogs(models.Model):
“”“超时操作日志表”“”
id = models.AutoField(primary_key=True)
re_time = models.CharField(max_length=32, verbose_name='请求时间')
re_user = models.CharField(max_length=32, verbose_name='操作人')
re_ip = models.CharField(max_length=32, verbose_name='请求IP')
re_url = models.CharField(max_length=255, verbose_name='请求url')
re_method = models.CharField(max_length=11, verbose_name='请求方法')
re_content = models.TextField(null=True, verbose_name='请求参数')
rp_content = models.TextField(null=True, verbose_name='响应参数')
access_time = models.IntegerField(verbose_name='响应耗时/ms')
class Meta:
db_table = 'access_timeout_logs'