Files
xs_system/HttpLog.py
T

64 lines
2.9 KiB
Python
Raw Normal View History

2026-09-23 09:28:16 +08:00
# ============================================================
# HTTP 请求日志中间件
# 功能:拦截每一次 API 请求,记录请求方信息、请求参数、
# 响应状态、响应数据以及接口耗时,统一写入 log.txt
# 工作流程:
# 请求进入 → 记录 method / url → 调用真实接口 → 记录耗时
# → 记录响应状态码 & 响应体 → 返回重新组装的 Response
# ============================================================
import time
from fastapi import Request
from fastapi.responses import Response
from Log import Log
async def m1(req: Request, call_next):
"""
FastAPI HTTP 中间件入口函数。
参数:
req: FastAPI 自动注入的 Request 对象,包含 method / url / body 等
call_next: 由中间件机制注入的回调函数,调用它会执行下一个中间件或真正的接口处理器
注意:
中间件里读取响应体后,必须手动重新构造一个 Response 返回;
否则 FastAPI 流式响应的 body_iterator 被读完就没了,前端会收到空内容。
"""
# 初始化日志器:日志名 ai0824,默认 INFO 级别
log = Log('ai0824', 'INFO')
# 将日志输出到 log.txt(会自动追加)
log.logFile('log.txt')
# —— 1. 请求前置记录 ——
log.message('Info', '请求方式:' + req.method) # GET / POST / PUT / DELETE ...
log.message('Info', '请求地址:' + str(req.url)) # 例如 http://127.0.0.1:12345/basic-information
# —— 2. 执行真正的接口,记录耗时 ——
beginTime = time.time() # 请求开始时间戳(秒)
r = await call_next(req) # 调用后续链:下一个中间件或路由处理器
endTime = time.time() # 响应返回时间戳
# —— 3. 读取原始请求体(JSON 参数等)并记录 ——
body = await req.body() # body() 是异步方法,必须 await;返回 bytes
log.message('Info', '请求参数:' + body.decode('utf-8', 'ignore'))
# —— 4. 读取流式响应体并记录 ——
# call_next 返回的是 StreamingResponse,响应体藏在 body_iterator 里,且只能遍历一次
resp_bytes = b''
async for chunk in r.body_iterator:
resp_bytes += chunk if isinstance(chunk, bytes) else chunk.encode()
log.message('Info', '响应状态码:' + str(r.status_code))
log.message('Info', '响应数据:' + resp_bytes.decode('utf-8', 'ignore'))
log.message('Info', '请求耗时:%.2f ms' % ((endTime - beginTime) * 1000))
# —— 5. 重新构造 Response 返回给前端 ——
# 原因:body_iterator 已经被遍历过一次,原始 Response 再返回会导致响应体为空
return Response(
content=resp_bytes,
status_code=r.status_code,
headers=dict(r.headers),
media_type=r.media_type,
)