!27 feat: 新增trace中间件强化日志链路追踪和响应头
* refactor: trace_log重命名为trace_middleware * refactor: 日志处理器重构为类式写法 * perf: 移除无用文件 * perf: 优化trace中间件部分写法 * style: 格式化代码 * Merge branch 'master' into develop * feature: 1.日志添加traceId链路追踪 2.response-header默认添加request-id与traceId对应
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
from fastapi import FastAPI
|
||||
from .ctx import TraceCtx
|
||||
from .middle import TraceASGIMiddleware
|
||||
|
||||
__all__ = ('TraceASGIMiddleware', 'TraceCtx')
|
||||
|
||||
__version__ = '0.1.0'
|
||||
|
||||
|
||||
def add_trace_middleware(app: FastAPI):
|
||||
"""
|
||||
添加trace中间件
|
||||
|
||||
:param app: FastAPI对象
|
||||
:return:
|
||||
"""
|
||||
app.add_middleware(TraceASGIMiddleware)
|
23
ruoyi-fastapi-backend/middlewares/trace_middleware/ctx.py
Normal file
23
ruoyi-fastapi-backend/middlewares/trace_middleware/ctx.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@author: peng
|
||||
@file: ctx.py
|
||||
@time: 2025/1/17 16:57
|
||||
"""
|
||||
|
||||
import contextvars
|
||||
from uuid import uuid4
|
||||
|
||||
CTX_REQUEST_ID: contextvars.ContextVar[str] = contextvars.ContextVar('request-id', default='')
|
||||
|
||||
|
||||
class TraceCtx:
|
||||
@staticmethod
|
||||
def set_id():
|
||||
_id = uuid4().hex
|
||||
CTX_REQUEST_ID.set(_id)
|
||||
return _id
|
||||
|
||||
@staticmethod
|
||||
def get_id():
|
||||
return CTX_REQUEST_ID.get()
|
47
ruoyi-fastapi-backend/middlewares/trace_middleware/middle.py
Normal file
47
ruoyi-fastapi-backend/middlewares/trace_middleware/middle.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@author: peng
|
||||
@file: middle.py
|
||||
@time: 2025/1/17 16:57
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
||||
from .span import get_current_span, Span
|
||||
|
||||
|
||||
class TraceASGIMiddleware:
|
||||
"""
|
||||
fastapi-example:
|
||||
app = FastAPI()
|
||||
app.add_middleware(TraceASGIMiddleware)
|
||||
"""
|
||||
|
||||
def __init__(self, app: ASGIApp) -> None:
|
||||
self.app = app
|
||||
|
||||
@staticmethod
|
||||
async def my_receive(receive: Receive, span: Span):
|
||||
await span.request_before()
|
||||
|
||||
@wraps(receive)
|
||||
async def my_receive():
|
||||
message = await receive()
|
||||
await span.request_after(message)
|
||||
return message
|
||||
|
||||
return my_receive
|
||||
|
||||
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
||||
if scope['type'] != 'http':
|
||||
await self.app(scope, receive, send)
|
||||
return
|
||||
|
||||
async with get_current_span(scope) as span:
|
||||
handle_outgoing_receive = await self.my_receive(receive, span)
|
||||
|
||||
async def handle_outgoing_request(message: 'Message') -> None:
|
||||
await span.response(message)
|
||||
await send(message)
|
||||
|
||||
await self.app(scope, handle_outgoing_receive, handle_outgoing_request)
|
52
ruoyi-fastapi-backend/middlewares/trace_middleware/span.py
Normal file
52
ruoyi-fastapi-backend/middlewares/trace_middleware/span.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@author: peng
|
||||
@file: span.py
|
||||
@time: 2025/1/17 16:57
|
||||
"""
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from starlette.types import Scope, Message
|
||||
from .ctx import TraceCtx
|
||||
|
||||
|
||||
class Span:
|
||||
"""
|
||||
整个http生命周期:
|
||||
request(before) --> request(after) --> response(before) --> response(after)
|
||||
"""
|
||||
|
||||
def __init__(self, scope: Scope):
|
||||
self.scope = scope
|
||||
|
||||
async def request_before(self):
|
||||
"""
|
||||
request_before: 处理header信息等, 如记录请求体信息
|
||||
"""
|
||||
TraceCtx.set_id()
|
||||
|
||||
async def request_after(self, message: Message):
|
||||
"""
|
||||
request_after: 处理请求bytes, 如记录请求参数
|
||||
|
||||
example:
|
||||
message: {'type': 'http.request', 'body': b'{\r\n "name": "\xe8\x8b\x8f\xe8\x8b\x8f\xe8\x8b\x8f"\r\n}', 'more_body': False}
|
||||
"""
|
||||
return message
|
||||
|
||||
async def response(self, message: Message):
|
||||
"""
|
||||
if message['type'] == "http.response.start": -----> request-before
|
||||
pass
|
||||
if message['type'] == "http.response.body": -----> request-after
|
||||
message.get('body', b'')
|
||||
pass
|
||||
"""
|
||||
if message['type'] == 'http.response.start':
|
||||
message['headers'].append((b'request-id', TraceCtx.get_id().encode()))
|
||||
return message
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_current_span(scope: Scope):
|
||||
yield Span(scope)
|
Reference in New Issue
Block a user