【Python】如何在FastAPI中使用UUID标记日志,以跟踪一个请求的完整生命周期

这篇具有很好参考价值的文章主要介绍了【Python】如何在FastAPI中使用UUID标记日志,以跟踪一个请求的完整生命周期。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为什么要使用uuid标记日志?

在分布式系统中,一个请求可能会经过多个服务,每个服务都会生成自己的日志。如果我们只使用普通的日志记录,那么很难将这些日志串联在一起,以至难以跟踪一个请求的完整生命周期。

如果能够使用uuid标记日志,为每个请求生成一个唯一的uuid,且这个日志可以在不同的系统间传递,就可以解决这个问题。

FastAPI和Loguru是两个非常流行的Python库。FastAPI是一个现代的Web框架,具有高性能和易于使用的特点。Loguru是一个灵活的日志记录库,具有简单、易用和强大的特点。在这篇文章中,我们将介绍如何使用FastAPI和Loguru进行日志记录,并使用同一个uuid标记来标记每个请求的日志链路。

安装FastAPI和Loguru

首先,我们需要安装FastAPI和Loguru。可以使用pip命令来安装它们:

pip install fastapi
pip install loguru

创建FastAPI应用程序

接下来,我们需要创建一个FastAPI应用程序。在这个例子中,我们将创建一个简单的应用程序,它对外提供一个根路径访问,我们在根路径被访问时会记录日志,并且会调用hello函数,hello函数自身也添加了日志记录。

import uvicorn
from fastapi import FastAPI
from loguru import logger

app = FastAPI()

logger.add("log.log")


@app.get("/{name}")
async def root(name):
    logger.info("Hello from the root path")
    hello()
    return {"message": f"Hello {name}"}


def hello():
    logger.info("hello() called!")


if __name__ == '__main__':
    uvicorn.run("main:app", port=10001, reload=True)

tips uvicorn.run("main:app", port=10001, reload=True) 中的 main 表示执行 main.py 文件中的 app

服务启动后,如果我们访问 http://localhost:10001/Bingo,会收到响应 {"message":"Hello Bingo"}, log.log文件中会记录

2023-06-01 21:24:13.471 | INFO     | main:root:19 - Root path is Visited!
2023-06-01 21:24:13.472 | INFO     | main:hello:26 - hello() called!
2023-06-01 21:24:13.472 | INFO     | main:root:21 - Bingo Visited!

添加uuid标记

上面的三条日志虽然都是因为 我们访问一个地址产生的,但是他们之间除了时间,没有其他的管理;如果同一时间有多个请求,我们就难以跟踪随调用了 hello()做了什么 ,那么如何才能将他们关联起来呢?

这需要用到 with 上下文管理函数 和** FastAPI中间件** 的概念。

with 上下文管理函数

with 是 Python 中的一个上下文管理器,它可以在代码块执行完毕后自动释放资源,避免资源泄漏等问题,提高代码的可读性和可维护性。
with 语句的语法如下:

with expression [as variable]:
    with-block

其中,expression 是一个上下文管理器对象,可以是一个函数或一个类,必须实现 __enter____exit__ 方法。as variable 是可选的,用来指定一个变量名,将 expression.__enter__() 方法的返回值赋值给该变量。with-block 是一个代码块,用来执行需要被管理的代码。

with 语句的执行流程如下:

  1. 执行 expression.__enter__() 方法,获取上下文管理器对象。
  2. 如果指定了 as variable,将 __enter__() 方法的返回值赋值给该变量。
  3. 执行 with-block 中的代码。
  4. 如果 with-block 中的代码执行过程中抛出异常,则执行 expression.__exit__(exc_type, exc_value, traceback) 方法,释放资源。
  5. 如果 with-block 中的代码执行完毕,则执行 expression.__exit__(None, None, None) 方法,释放资源。

with 语句可以用来管理文件、网络连接、锁等资源,例如:

with open('file.txt', 'r') as f:
    content = f.read()
    print(content)

上述代码使用 with 语句来管理文件资源,当代码块执行完毕后,会自动关闭文件句柄,释放资源。

FastAPI中间件

FastAPI中间件是一种机制,允许我们在请求到达应用程序之前或之后执行一些操作。它们可以用于添加请求头、验证身份、记录日志等。在FastAPI中,中间件是使用装饰器实现的。我们可以使用@app.middleware()装饰器来定义中间件。中间件函数接收一个Request对象和一个call_next函数作为参数。它可以在请求到达应用程序之前或之后执行一些操作,并调用call_next函数来继续处理请求。

代码实现

改造后的代码如下:

import uvicorn
import uuid
from fastapi import FastAPI
from loguru import logger
from starlette.responses import JSONResponse

app = FastAPI()

logger.add(
    "log.log",
    format="{time:YYYY-MM-DD HH:mm:ss.ms} [{extra[request_id]}] | {level} | {module}.{function}:{line} : {message}"
)


@app.middleware("http")
async def request_middleware(request, call_next):
    request_id = str(uuid.uuid4())
    with logger.contextualize(request_id=request_id):
        logger.info("Request started")
        try:
            return await call_next(request)

        except Exception as ex:
            logger.error(f"Request failed: {ex}")
            return JSONResponse(content={"success": False}, status_code=500)

        finally:
            logger.info("Request ended")


@app.get("/{name}")
async def root(name):
    logger.info("Root path is Visited!")
    hello()
    logger.info(f"{name} Visited!")
    return {"message": f"Hello {name}"}


def hello():
    logger.info("hello() called!")


if __name__ == '__main__':
    uvicorn.run("main:app", port=10001, reload=True)

当我们再次访问 http://localhost:10001/Bingo,收到的响应 {"message":"Hello Bingo"}不会有变化, 但是log.log文件中会记录:

2023-06-01 21:35:55.3555 [b61c693f-97c0-4c84-9f44-3b855bea2568] | INFO | main.request_middleware:29 : Request started
2023-06-01 21:35:55.3555 [b61c693f-97c0-4c84-9f44-3b855bea2568] | INFO | main.root:43 : Root path is Visited!
2023-06-01 21:35:55.3555 [b61c693f-97c0-4c84-9f44-3b855bea2568] | INFO | main.hello:50 : hello() called!
2023-06-01 21:35:55.3555 [b61c693f-97c0-4c84-9f44-3b855bea2568] | INFO | main.root:45 : Bingo Visited!
2023-06-01 21:35:55.3555 [b61c693f-97c0-4c84-9f44-3b855bea2568] | INFO | main.request_middleware:38 : Request ended

再次请求时,uuid 会发生变化:

2023-06-01 21:35:55.3555 [b0e02e34-51ee-4cbb-838b-8222ec8f0483] | INFO | main.request_middleware:29 : Request started
2023-06-01 21:35:55.3555 [b0e02e34-51ee-4cbb-838b-8222ec8f0483] | INFO | main.root:43 : Root path is Visited!
2023-06-01 21:35:55.3555 [b0e02e34-51ee-4cbb-838b-8222ec8f0483] | INFO | main.hello:50 : hello() called!
2023-06-01 21:35:55.3555 [b0e02e34-51ee-4cbb-838b-8222ec8f0483] | INFO | main.root:45 : Bingo Visited!
2023-06-01 21:35:55.3555 [b0e02e34-51ee-4cbb-838b-8222ec8f0483] | INFO | main.request_middleware:38 : Request ended

实现原理是我们在每个请求的状态中存储uuid,并在日志记录中使用它。

代码解释:

  1. @app.middleware("http") 表示注册一个 HTTP 中间件,用于处理 HTTP 请求。
  2. request 是一个请求对象,call_next 是一个回调函数,用于调用下一个中间件或路由处理函数。
  3. uuid.uuid4() 生成一个唯一的请求 ID,用于在日志中标识该请求。
  4. logger.contextualize(request_id=request_id) 用于在日志中添加请求 ID 上下文。
  5. logger.info("Request started") 记录请求开始的日志信息。
  6. await call_next(request) 调用下一个中间件或路由处理函数,并返回响应对象。
  7. logger.error(f"Request failed: {ex}") 记录请求失败的日志信息,其中 ex 是捕获到的异常对象。
  8. JSONResponse(content={"success": False}, status_code=500) 返回一个 HTTP 500 错误响应,表示请求处理失败。
  9. logger.info("Request ended") 记录请求结束的日志信息。

这个中间件只用实现一次,对后续所有其他接口的开发没有任何的侵入,一劳永逸。

tips: 为什么使用uuid.uuid4()而不是uuid.uuid()?

uuid.uuid()生成的UUID是根据主机ID、序列号和当前时间生成的。这意味着在同一台计算机上生成的UUID可能会重复,尤其是在高负载情况下。为了避免这种情况,我们可以使用uuid.uuid4()生成随机UUID。uuid.uuid4()生成的UUID是完全随机的,几乎不可能重复。因此,它是生成唯一标识符的最佳选择。

总结

在这个例子中,我们使用FastAPI和Loguru进行了日志记录,并使用同一个uuid标记来标记每个请求的日志链路。这使得我们能够轻松地跟踪每个请求的日志,并识别它们的来源,如果上游也使用了这样方式记录日志, 那做到跟踪一个请求的完整生命周期就不难办到了。文章来源地址https://www.toymoban.com/news/detail-469993.html

到了这里,关于【Python】如何在FastAPI中使用UUID标记日志,以跟踪一个请求的完整生命周期的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【python自动化实战-引入trace】在项目中引入trace跟踪每条testcase执行过程中的日志+日志推送es

    最近公司在跑自动化case过程中有很多异常case,有的很难依据现有的日志定位问题(日志多且乱,无关联性),时常需要本地debug才能发现问题,很影响写自动化的效率以及自动化运行的通过率。 因此领导下发了一个需求:需要给每一条case绑定一个trace,通过trace关联case执行过程

    2024年02月04日
    浏览(29)
  • Python 潮流周刊#15:如何分析 FastAPI 异步请求的性能?

    你好,我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容,大部分为英文。标题取自其中一则分享,不代表全部内容都是该主题,特此声明。 本周刊精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内

    2024年02月13日
    浏览(41)
  • Python - 【FastAPI】框架配置搭建基本使用

    之前在面试的时候面试官有提到过这个框架,但是个人在之前的项目中没有用到过,只是有听过,并没有实际的应用,因此,个人自己研究了一下,这是个新型的框架,使用起来相对挺简单。FastAPI是一个基于Python的现代Web框架,它具有快速构建高性能API的特点。 中文文档:

    2024年02月11日
    浏览(26)
  • FastAPI 库(Python 的 Web 框架)基本使用指南(一)

    参考文档: 中文文档 轻松上手Python的Web神器:FastAPI教程 FastAPI 是一个基于 Python 的现代 Web 框架,它具有快速构建高性能 API 的特点。 FastAPI 关键特性: 快速:可与 NodeJS 和 Go 并肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。 高效编码:提高功能开发

    2024年02月04日
    浏览(42)
  • FastAPI 库(Python 的 Web 框架)基本使用指南(二)

    FastAPI 中定义路由的方式主要有两种,一种是使用 FastAPI 应用实例的方法(例如 app.get() ),一种是使用装饰器(例如 @app.get() ),它们的用法和作用略有不同。 方式1:使用 FastAPI 应用实例的方法 app.get() 、 app.post() 等方法用于直接在应用实例上定义路由。 这些方法接受路径

    2024年02月05日
    浏览(31)
  • 【FastAPI】P1 安装与第一个 FastAPI 应用

    FastAPI 是用于快速构建 API 的 web 框架,依赖 Python 3.8 及更高版本。使用 pip 命令安装 fastapi : 安装异步处理 ASGI 的服务器 Uvicorn : 补充知识: Uvicorn 是 ASGI 的一个实现,它基于 Starlette 和 Uvicorn 的异步服务器,能够为 Django、FastAPI 等应用提供高性能的服务。 ASGI 异步处理服务

    2024年02月20日
    浏览(28)
  • 如何使用python删除一个文件?

    使用Python删除文件有多种方法,但是最好的方法如下: os.remove()删除文件 os.unlink()删除文件。它是remove()方法的Unix名称。 shutil.rmtree()删除目录及其下面所有内容。 pathlib.Path.unlink()在Python 3.4及更高版本中用来删除单个文件pathlib模块。 os.remove() 删除文件 Pyt

    2024年02月04日
    浏览(27)
  • python fastapi 入门教程,每个案例都使用postman进行测试写的接口

    官方示例代码 运行命令:uvicorn test:app --reload 注意:运行命令app前面那个是文件位置,官方的文件名叫main,要以实际的文件名为准,不然找不到app 测试无r参数情况 测试有r参数情况 2.1、错误写法 报错了 2.2、正确写法 2.3、嵌套多个请求参数(不明白的话看下面的postman的请求

    2023年04月08日
    浏览(28)
  • 解锁新技能《logback标记日志过滤器MarkerFilter》

    开源日志SDK(纯java版) 在logback-classic中存在一个全局过滤器TurboFilter,TurboFilter是与LoggerContext绑定,会在会在其它过滤器之前执行;MarkerFilter是TurboFilter的一个子类,其作用是标记日志是否记录入文件之中,可以指定标记的日志记录到文件中;也可以指定标记的日志拒绝记录到

    2024年02月15日
    浏览(30)
  • 如何使用pycharm创建一个python项目

    1.打开pycharm,点击new project 2.修改项目存放位置,点击第二个圆圈,点击add interpreter添加解释器 3.选择一个系统内置的解释器 4.点击创建按钮即可 5.打开该项目 6.在工程的地方点击鼠标右键–》选择new–》选择python file 7.给你的python文件取一个文件名,按回车键,创建成功

    2024年02月12日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包