前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于FastAPI/Celery/loguru实现全链路日志追踪功能

基于FastAPI/Celery/loguru实现全链路日志追踪功能

作者头像
明月AI
发布2023-08-26 15:08:08
1.8K0
发布2023-08-26 15:08:08
举报
文章被收录于专栏:野生AI架构师野生AI架构师

背景

在我们的系统里,已经记录了很多的日志,但是问题是这些日志很鸡肋,当需要定位问题的时候,根本很难区分,哪些日志是一起的,而且因为我们的系统大都是一些耗时的任务,不同请求的任务日志都交叉混在一起,更加加剧了这个问题。因此生产系统上,这些日志很难利用起来。

目标

目标主要有三个:
  1. 能够实现日志的全链路跟踪,这样出了问题之后,才能根据任务ID或者请求ID之类的快速找到相关的日志,提升定位问题的效率;
  2. 尽量是非侵入式的,尽量少改动系统原有的代码,不然就会变得很复杂;
  3. 能同时追踪FastAPI接口逻辑及Celery任务的日志。

实现

在网络上找了一些Python全链路日志实现方式,有一个和我想要的是比较接近的:https://zhuanlan.zhihu.com/p/432010113

总体实现logger.py

代码语言:javascript
复制
import os
from uuid import uuid4
from loguru import logger
from contextvars import ContextVar
# 使用任务request_id来实现全链路日志追踪
_trace_id: ContextVar[str] = ContextVar('x_trace_id', default="")           # 任务ID
_x_request_id: ContextVar[str] = ContextVar('x_request_id', default="")     # 请求ID


class TraceID:
    """全链路追踪ID"""

    @staticmethod
    def set(req_id: str) -> ContextVar[str]:
        """设置请求ID,外部需要的时候,可以调用该方法设置
        Returns:
            ContextVar[str]: _description_
        """
        if req_id:
            req_id = uuid4().hex
        _x_request_id.set(req_id)
        return _x_request_id

    @staticmethod
    def set_trace(id: str, title: str = "task") -> ContextVar[str]:
        """设置全链路追踪ID
        Returns:
            ContextVar[str]: _description_
        """
        id = f"{title}:{id}"
        _trace_id.set(id)
        return _trace_id


def _logger_filter(record):
    record['trace_msg'] = f"{_x_request_id.get()} | {_trace_id.get()}"
    return record['trace_msg']


# 日志配置  公共参数

params = {
    "rotation": "500 MB", "encoding": 'utf-8', "enqueue": True, "backtrace": True, 
    "filter": _logger_filter,
    "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {trace_msg} | {name}:{function}:{line} - {message}",
}
# 去除默认控制台输出
logger.remove()
logger.add("inf.log", level='INFO', retention="90 days", **params)    # info日志只保留90天

__all__ = ["logger"]

(以上只是示例代码)

实现要点:

  1. 基于contextvars这个包实现上下文功能;
  2. 利用loguru包的filter功能,实现非侵入式的动态参数的注入;
  3. 同时追踪web请求ID及业务ID。

不过这也不是完全非侵入式的,还是要做一些简单的改动,如在FastAPI接口入口处,增加中间件:

代码语言:javascript
复制
@app.middleware("http")
async def set_logger(request: Request, call_next):
    # 设置日志的全链路追踪
    REQUEST_ID_KEY = "X-Request-Id"
    _req_id_val = request.headers.get(REQUEST_ID_KEY, "")
    req_id = TraceID.set(_req_id_val)
    response: Response = await call_next(request)
    response.headers[REQUEST_ID_KEY] = req_id.get()
    return response

而在业务代码执行的源头(或者接近源头的位置)加上一句:

代码语言:javascript
复制
TraceID.set_trace(task_id)

可以根据业务的特征设置对应的追踪ID。

效果


这样使用logger记录日志的时候,就会自动将相应的ID带上,记录日志的时候也原来一样,基本上实现了非侵入式的全链路日志追踪,对定位问题是大为有利。

使用最简单的grep就能直接把相关日志找出来:

高级一点的,可以统一收集到ES进行跨服务的日志检索。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-08-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 野生AI架构师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档