背景

在flask web中我们通常需要一个traceid作为调用参数传递给全链路各个调用函数

  1. 需要针对一次请求创建一个唯一的traceid:这里用uuid去简化代替
  2. 我们需要保证traceid不被污染,在每个请求期间存在,在请求结束销毁且线程独立:这里通过flask中的g对象来存储线程内的数据
  3. 由于我们使用g对象来存储,那么当接口中发起新的请求时候,新请求会创建新的g对象,此时g对象为空,我们需要让traceid可以在多个请求中共享数据:这里通过请求头中增加traceid来传递

实现

首先定义二个主要函数

  • 定义一个请求开始的时候需要调用的函数,用于初始化traceid或者获取上一个请求中的traceid以及其他一些相关请求参数
  • 定义一个请求结束的时候需要调用的函数,用于请求结束的日志响应报文收尾记录。
import requests
from flask import request, g
import time
from flask import Flask def get_uuid():
import uuid
return str(uuid.uuid4()).replace('-', '') def trace_add_log_record(event_des='',msg_dict={},remarks=''):
#trace_links_index每次调用+1
request.trace_links_index = request.trace_links_index + 1
logs = {
'traceid': g.traceid,
'trace_index': request.trace_links_index,
'event_des': event_des,
'msg_dict': msg_dict,
'remarks': remarks
}
print(logs) def trace_start_log_record_handler():
# 获取traceid,如果存在则使用,否则生成一个
if "traceid" in request.headers:
g.traceid = request.headers['traceid']
else:
g.traceid = get_uuid()
# 初始化trace_links_index
request.trace_links_index = 0
# 记录开始时间
request.start_time = time.time()
log_msg = {
'headers': request.headers,
'url': request.url,
'method': request.method,
"request_data": request.args if request.method == "GET" else request.get_json(),
'ip': request.headers.get("X-Real-IP") or request.remote_addr,
'start_time': request.start_time
}
# 记录日志
trace_add_log_record(event_des='start', msg_dict=log_msg) def trace_end_log_record_handler(reponse):
# 记录结束时间
request.end_time = time.time()
# 记录traceid到响应头
reponse.headers.add('traceid', g.traceid)
log_msg = {
"end_time" : request.end_time,
"cost_time": request.end_time - request.start_time,
"status_code": reponse.status_code,
"headers": reponse.headers,
"response_data": reponse.data.decode('utf-8')
}
# 记录日志
trace_add_log_record(event_des='end', msg_dict=log_msg)

接着,我们通过钩子函数去触发我们上述所写的两个重要函数

"""
写钩子函数在app中注册,还有一种写法
@app.before_request
def before_request2():
print('before_request2') @app.after_request
def after_request1(response):
print('after_request1') return response
"""
#写钩子函数在app中注册
def register_handler(response):
def before_request():
trace_start_log_record_handler()
def after_request(response):
trace_end_log_record_handler(reponse=response)
return response
# 注册钩子函数
response.before_request(before_request)
response.after_request(after_request)

最后写测试接口进行测试

app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!' @app.route('/test')
def test():
name = request.args.get('name')
hello_world = requests.get('http://127.0.0.1:5000/', headers={'traceid': g.traceid})
return name + hello_world.text @app.route('/test2', methods=['POST'])
def test2():
name = request.get_json().get('name')
hello_world = requests.get('http://127.0.0.1:5000/', headers={'traceid': g.traceid})
return name + hello_world.text if __name__ == '__main__': register_handler(app)
app.run(debug=True)

这样我们简单了完成了通过traceid把链路串联起来了

{'traceid': 'a5637579351c477a80090a88f5347088', 'trace_index': 1, 'event_des': 'start', 'msg_dict': {'headers': EnvironHeaders([('User-Agent', 'Apifox/1.0.0 (https://apifox.com)'), ('Content-Type', 'application/json'), ('Accept', '*/*'), ('Host', '127.0.0.1:5000'), ('Accept-Encoding', 'gzip, deflate, br'), ('Connection', 'keep-alive')]), 'url': 'http://127.0.0.1:5000/test?name=yetangjian', 'method': 'GET', 'request_data': ImmutableMultiDict([('name', 'yetangjian')]), 'ip': '127.0.0.1', 'start_time': 1717311086.757312}, 'remarks': ''}
{'traceid': 'a5637579351c477a80090a88f5347088', 'trace_index': 1, 'event_des': 'start', 'msg_dict': {'headers': EnvironHeaders([('Host', '127.0.0.1:5000'), ('User-Agent', 'python-requests/2.25.1'), ('Accept-Encoding', 'gzip, deflate'), ('Accept', '*/*'), ('Connection', 'keep-alive'), ('Traceid', 'a5637579351c477a80090a88f5347088')]), 'url': 'http://127.0.0.1:5000/', 'method': 'GET', 'request_data': ImmutableMultiDict([]), 'ip': '127.0.0.1', 'start_time': 1717311086.7663064}, 'remarks': ''}
{'traceid': 'a5637579351c477a80090a88f5347088', 'trace_index': 2, 'event_des': 'end', 'msg_dict': {'end_time': 1717311086.7663064, 'cost_time': 0.0, 'status_code': 200, 'headers': Headers([('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '13'), ('traceid', 'a5637579351c477a80090a88f5347088')]), 'response_data': 'Hello, World!'}, 'remarks': ''}
{'traceid': 'a5637579351c477a80090a88f5347088', 'trace_index': 2, 'event_des': 'end', 'msg_dict': {'end_time': 1717311086.7683115, 'cost_time': 0.010999441146850586, 'status_code': 200, 'headers': Headers([('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '23'), ('traceid', 'a5637579351c477a80090a88f5347088')]), 'response_data': 'yetangjianHello, World!'}, 'remarks': ''}

通过钩子函数+Traceid实现Flask链路追踪的更多相关文章

  1. 11.Flask钩子函数

    在Flask中钩子函数是使用特定的装饰器的函数.为什么叫做钩子函数呢,是因为钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码,那么这种函数就叫做钩子函数. before_first_requ ...

  2. flask 钩子函数

    说明: before_request函数,就是一个装饰器,他可以把需要设置为钩子函数的代码放到视图函数执行之前执行 示例: from flask import Flask,url_for,redire ...

  3. 全链路追踪traceId,ThreadLocal与ExecutorService

    关于全链路追踪traceId遇到线程池的问题,做过架构的估计都遇到过,现在以写个demo,总体思想就是获取父线程traceId,给子线程,子线程用完移除掉. mac上的chrome时不时崩溃,写了一大 ...

  4. flask系列八之请求方法、g对象和钩子函数

    一.get方法 ,post方法 post请求在模板中要注意几点: (1)input标签中,要写name来标识这个value的key,方便后台获取. (2)在写form表单的时候,要指定method=' ...

  5. Flask入门flask-script 蓝本 钩子函数(三)

    1 flask-script扩展库 概念: 是一个flask终端运行的解析器 ,因为项目完成以后,代码改动会有风险,所以借助终端完成不同启动项的配置 安装 pip3 install flask-scr ...

  6. 21、Flask实战第21天:常用的Flask钩子函数

    在Flask中钩子函数是使用特定的装饰器装饰的函数.为什么叫钩子函数呢?是因为钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码.那么这种函数就叫做钩子函数. before_first_req ...

  7. Flask初学者:g对象,hook钩子函数

    Flask的g对象 作用:g可以可以看作是单词global的缩写,使用“from flask import g”导入,g对象的作用是保存一些在一次请求中多个地方的都需要用到的数据,这些数据可能在用到的 ...

  8. 【Flask】 python学习第一章 - 4.0 钩子函数和装饰器路由实现 session-cookie 请求上下文

    钩子函数和装饰器路由实现 before_request 每次请求都会触发 before_first_requrest  第一次请求前触发 after_request  请求后触发 并返回参数 tear ...

  9. 七十二:flask钩子函数之关于errorhandler的钩子函数

    errorhandler:在发生一些异常的时候,如404.500,如果要自定义处理这些错误,就可以使用errorhandler来处理,使用errorhandler需要注意几点: 1.在errorhan ...

  10. 七十:flask钩子函数之关于before_request的钩子函数

    在flask中钩子函数是使用特定的装饰器装饰的函数,用于在正常执行的代码中,插入一段自己想要执行的代码(hook) before_first_request:flask项目第一次部署后指向的钩子函数, ...

随机推荐

  1. native react 代码智能提示

    背景 在vscode 中,虽然有插件可以达到代码提示的效果但是不是很嗨. 所以加上这些: 全局安装typings: npm install typings -g 1 安装react和react-nat ...

  2. Beetl 源码解析:GroupTemplate 类

    本文首发于公众号:腐烂的橘子 前言 Beetl 是一款 Java 模板引擎,在公司的项目中大量运用,它的作用是写通用代码时,有一些差异化的逻辑需要处理,这时可以把这些差异化的逻辑写在模板里,程序直接调 ...

  3. Bilibili资深运维工程师:DCDN在游戏应用加速中的实践

    简介: bilibili资深运维工程师李宁分享<DCDN在游戏应用加速中的实践>从bilibili游戏应用的效果和成本入手,深入浅出地分享DCDN全站加速在游戏加速场景中的应用. 日前,云 ...

  4. CNCF 沙箱项目 OCM Placement 多集群调度指南

    ​简介:在这篇文章中,将介绍 Placement 如何选择到所需的集群,Placement 可以提供的调度功能,以及一些场景下的最佳实践,使用者可以参考示例来编写符合自己要求的 Placement.其 ...

  5. MySQL 深潜 - MDL 锁的实现与获取机制

    简介:本文将介绍在 MDL 系统中常用的数据结构及含义,然后从实现角度讨论 MDL 的获取机制与死锁检测,最后分享在实践中如何监控 MDL 状态. ​ 作者 | 泊歌 来源 | 阿里技术公众号 一 背 ...

  6. 打通JAVA与内核系列之一ReentrantLock锁的实现原理

    ​简介:写JAVA代码的同学都知道,JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁).其中synchronized锁是JAVA语言层面提供的能力 ...

  7. Apsara Stack 2.0技术百科(9宫格)

    ​ ​ ​​​​​​​​ 原文链接 本文为阿里云原创内容,未经允许不得转载. ​

  8. [Linux] 日志管理: rsyslogd 服务 (检测启动/自启动/日志位置)

    查看 rsyslogd 服务是否已启动: ps aux | grep rsyslogd 查看 rsyslogd 是否设置了自启动: systemctl status rsyslog 或者 servic ...

  9. [Trading] 日间交易中的成交量分析 - 使用成交量趋势来提高你的效果

    在交易中,成交量代表在特定时期内股票或期货合约的易手单位数量. 交易员将其作为一个关键指标,因为它让他们知道资产的流动性水平,以及他们在接近当前价格的情况下买入或卖出头寸的容易程度,这可能是一个移动的 ...

  10. [K8s] Kubernetes核心基础概念 Node, Pod, ReplicaSet, Deployment, Service, Ingress, ConfigMap

    Node 即 Kubernetes 集群中的一台工作机器,物理机或者虚拟机. https://kubernetes.io/zh/docs/concepts/architecture/nodes/ 通常 ...