Become a sponsor

操作日志
操作日志中间件自动拦截写请求(POST/PUT/DELETE/PATCH)并记录操作日志,无需手动埋点。日志落库通过后台线程异步执行,不阻塞响应。源码位于 src/middleware/operation_log.py。
1. 请求进入 → operation_log 判断是否需要记录
2. 仅拦截写请求(POST/PUT/DELETE/PATCH),排除登录/验证码等路径
3. 记录请求参数、响应结果、耗时、操作类型
4. 日志落库通过后台线程异步执行
5. 不阻塞主请求,对性能影响极小以下路径不记录操作日志:
# src/middleware/operation_log.py
_EXCLUDED_PATH_SEGMENTS = ("/login", "/captcha", "/logout", "/sendSms")排除逻辑使用 endswith 匹配路径末段,而非精确匹配完整路径。
中间件根据 HTTP 方法和 URL 路径自动推断操作类型:
# src/middleware/operation_log.py
# HTTP 方法 → 日志类型
_METHOD_LOG_TYPE = {
"POST": 3, # 新增
"PUT": 4, # 修改
"DELETE": 5, # 删除
}
# URL 路径关键字 → 日志类型(优先级高于方法)
_PATH_ACTION_TYPE = {
"status": 4, # 状态变更
"import": 3, # 导入
"export": 5, # 导出
"batchDelete": 5, # 批量删除
}
def _infer_log_type(request) -> int:
"""从 HTTP 方法和 URL 路径推断操作类型"""
# 先按路径关键字匹配
for keyword, log_type in _PATH_ACTION_TYPE.items():
if keyword in request.path:
return log_type
# 再按 HTTP 方法匹配
return _METHOD_LOG_TYPE.get(request.method, 18) # 18=其它| 路径末段 | 操作类型 | 类型ID |
|---|---|---|
add | 新增 | 1 |
update | 修改 | 2 |
delete | 删除 | 3 |
batchdelete | 批量删除 | 4 |
status | 状态设置 | 5 |
resetpwd | 重置密码 | 6 |
updatepassword | 修改密码 | 7 |
import | 导入 | 8 |
export | 导出 | 9 |
kickout | 强退 | 10 |
generate | 生成代码 | 11 |
clear | 清空数据 | 12 |
auth | 授权 | 13 |
refresh | 缓存 | 14 |
openaccount | 开账号 | 15 |
| 其它 | 其它 | 0 |
| 字段 | 说明 |
|---|---|
title | 操作标题(从路由装饰器获取) |
type | 操作类型(自动推断) |
method | 路由方法名 |
url | 请求路径 |
param | 请求参数(截取前2000字符) |
result | 响应结果(截取前2000字符) |
consume_time | 耗时(毫秒) |
status | 状态:0-成功 1-失败 |
error | 错误信息 |
create_user | 操作人 |
GET /api/v1/oper/log/list?pageNo=1&pageSize=10
GET /api/v1/oper/log/detail/<id>1. 后台任务:日志落库通过后台线程异步执行,不阻塞响应
2. 前缀解析:仅解析响应体前 8KB,避免大响应体全量解析
3. 长度截断:参数和结果截取前 2000 字符,防止撑爆日志表
4. 路径排除:登录/验证码等高频路径不记录
5. 路径段边界匹配:排除前缀按 path == prefix 或 prefix + '/' 匹配,避免子串误伤# src/middleware/operation_log.py
from flask import request, g
import time
import threading
def register_operation_log(app):
"""注册操作日志中间件"""
@app.before_request
def _start_timer():
g.start_time = time.time()
@app.after_request
def _log_operation(response):
# 仅记录写请求
if request.method not in ('POST', 'PUT', 'DELETE', 'PATCH'):
return response
# 排除特定路径
if any(seg in request.path for seg in _EXCLUDED_PATH_SEGMENTS):
return response
# 计算耗时
consume_time = int((time.time() - g.start_time) * 1000)
# 获取响应内容
result = response.get_data(as_text=True)[:2000]
# 异步记录日志
def _save_log():
# 保存日志到数据库
pass
thread = threading.Thread(target=_save_log)
thread.start()
return response后台线程
Flask 使用后台线程异步记录操作日志,不阻塞主请求。日志记录失败不会影响正常业务流程。
操作日志模块具备以下特点:
1. 自动记录:中间件拦截写请求,无需手动埋点
2. 类型推断:根据 URL 路径自动推断操作类型
3. 异步落库:后台线程执行,不阻塞主请求
4. 性能友好:前缀解析 + 长度截断 + 路径排除
5. 完整信息:记录参数、结果、耗时、操作人等