flask请求时间记录和日志处理

时间记录

在Python中,如果需要记录一个函数执行的时间,可以通过装饰器的方式来实现,避免在每个函数中进行重复编码。

python 复制代码
def time_summary(func):
    def wrapper(*args, **kwargs):
        # 以秒为单位的时间戳
        start = time.time()
        result = func(*args, **kwargs)
        logger.info(f"function use time {(time.time() - start) * 1000:.5f}ms")
        return result

    return wrapper

@time_summary
def hello_world():
    return "hello world"

flask日志处理

在logging日志处理中,每次日志输出都是一个全新的LogRecord事件,所以每次的输出之间是相互不关联的。但是在实际的项目中,我们希望能够在日志中获取每一次请求所对应的输出时间。

为实现上述需求,我们需要将同一次请求的事件关联起来,添加相同的标识打印到日志中。在日志的格式化输出中,通过LogRecord属性打印标识,但是每次日志输出都是一个全新的LogRecord事件。flask中是存在上下文的,所以考虑在一次请求的上下文中添加指定标识,然后在每次输出日志时,LogRecord获取该标识进行输出,管理同一请求。

全局上下文实现

python 复制代码
import logging
import time
import uuid

from flask import Flask, g, has_app_context

app = Flask(__name__)


class RequestFilter(logging.Filter):
    def filter(self, record):
        if has_app_context() and hasattr(g, "trace_id"):
            record.trace_id = g.trace_id
        else:
            record.trace_id = None
        return record


handler = logging.StreamHandler()
handler.setFormatter(
    logging.Formatter(
        '[%(asctime)s] - [%(trace_id)s] - [%(levelname)s] - [%(message)s]')
)
handler.addFilter(RequestFilter())

logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.INFO)


def time_summary(func):
    def wrapper(*args, **kwargs):
        # 以秒为单位的时间戳
        start = time.time()
        result = func(*args, **kwargs)
        logger.info(f"function use time {(time.time() - start) * 1000:.5f}ms")
        return result

    return wrapper


@app.before_request
def add_trace_id():
    g.trace_id = str(uuid.uuid4())


@app.route("/", methods=["GET"])
@time_summary
def hello_world():
    logger.info("hello world")
    return "hello world"
# [2024-02-20 18:21:14,605] - [60b03671-10e4-4143-922e-2c336ff6df44] - [INFO] - [hello world]
# [2024-02-20 18:21:14,606] - [60b03671-10e4-4143-922e-2c336ff6df44] - [INFO] - [function use time 0.16093ms]

在上述示例中,通过@app.before_request拦截器在请求前向全局上下文中添加了trace_id属性,通过Filter在每次输出日志时获取请求全局上下文的trace_id属性,使每一次日志输出的请求通过uuid进行关联。

实现优化

随着微服务的发展,我们希望在多个微服务之间能够关联相同的uuid,但是通过全局上下文的方式只能保证每一次请求的uuid相同,原因在于我们的uuid是直接生成的,若是能在多个服务之间关联起uuid,当请求时,则可根据关联的uuid获取相同的日志内容。

考虑添加参数,但存在参数值时,以该值为同一请求日志追踪标识,不存在则生成。

这里在方法的请求头中添加参数实现,若在每个flask请求上添加参数实现,需要多次重复实现且当实现变更时,容易漏改

python 复制代码
from wsgiref.headers import Headers

@app.before_request
def add_trace_id():
    if request.headers.get("X-Trace-Id") is None:
        headers = Headers()
        for key, value in request.headers.items():
            headers[key] = value
        headers["X-Trace-Id"] = str(uuid.uuid4())
        request.headers = headers
    g.trace_id = request.headers.get("X-Trace-Id")

在flask中,headers是不可变对象,所以不可以直接修改headers对象。

虽然headers是不可变对象,但是headers指针却不是固定的,所以通过获取原来的headers内容,修改指针的方式来解决不可变对象问题(headers不可拷贝,所以以迭代遍历的方式获取)

相关推荐
泡海椒3 小时前
JQuick-Excel FORMAT 导出格式实战:日期、金额显示与 TRANSFORM 的边界
前端·python·excel
承渊政道3 小时前
星空组网+Python实战:在另一台电脑上查看CSV报表
运维·开发语言·python·csv·星空组网
LOVE️YOU8 小时前
Python 中 Tuple 为什么有时可 Hash,有时不可 Hash?
开发语言·python·哈希算法
小oo呆8 小时前
【学习心得】迭代器和可迭代对象
开发语言·python
AC赳赳老秦8 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
迅猛龙办公室8 小时前
Python输出当前计算机的系统日期和时间
开发语言·前端·python
XiaoMaqqqq10 小时前
知名的IP驱动产业新场景新工具
网络·python·网络协议·tcp/ip
言乐610 小时前
Python实现语音审核模型
开发语言·python·django·virtualenv·pygame
步行cgn10 小时前
Spring 全注解开发详解
java·后端·spring
程序员清风11 小时前
Python 数据分析环境搭建:从 Jupyter 到 pandas
python·jupyter·数据分析