Observability:将 OpenTelemetry 添加到你的 Flask 应用程序

作者:来自 Elastic jessgarson

待办事项列表可以帮助管理与假期计划相关的所有购物和任务。使用 Flask,你可以轻松创建待办事项列表应用程序,并使用 Elastic 作为遥测后端,通过 OpenTelemetry 对其进行监控。

Flask 是一个轻量级的 Python Web 框架,可让你轻松创建应用程序。OpenTelemetry 是一个开源的、与供应商无关的可观察性框架,它提供跨不同服务和平台的统一监控功能,允许与各种后端系统无缝集成。

这篇文章将引导你使用 OpenTelemetry Python 的 Elastic Distribution 来监控 Flask 中内置的待办事项列表应用程序。本文中概述的示例的完整代码可以在此处找到。

如何将 Elastic 连接到 OpenTelemetry?

OpenTelemetry 的一大优点是它可以灵活地与你的应用程序集成。使用 Elastic 作为遥测后端。你有几个选择;你可以使用 OpenTelemetry 收集器(官方 OpenTelmetry 语言客户端)连接到 APM(AWS Lambda 收集器导出器)。我们的文档可让你详细了解将 OpenTelemetry 连接到 Elastic 的选项。

你将使用 OpenTelemetry Python 的 Elastic Distribution 作为本文中的示例。此库是 OpenTelemetry Python 的一个版本,具有附加功能并支持将 OpenTelemetry 与 Elastic 集成。需要注意的是,此包目前处于预览阶段,不应在生产环境中使用。

使用 Flask 创建待办事项列表应用程序

在监控应用程序之前,你必须先创建它。本文将指导你创建一个简单的待办事项列表应用程序来帮助你跟踪任务。完成应用程序后,它将如下所示:

开始之前,你需要创建一个虚拟环境。创建虚拟环境后,你需要安装所需的软件包。

pip install Flask Flask-SQLAlchemy

安装所需的软件包后,你必须导入必要的软件包和方法,配置 Flask 应用程序,并使用 SQLalachemy 设置 SQLite 数据库。之后,你将定义一个数据库模型来存储和列出任务项。你还需要初始化应用程序以使用 SQLalcamey,并通过创建所需的表来初始化数据库。

from flask import Flask, request, render_template_string, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import Mapped, mapped_columnapp = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///tasks.db"
db = SQLAlchemy()# Define a database model named Task for storing task data
class Task(db.Model):id: Mapped[int] = mapped_column(db.Integer, primary_key=True)description: Mapped[str] = mapped_column(db.String(256), nullable=False)# Initialize SQLAlchemy with the configured Flask application
db.init_app(app)# Initialize the database within the application context
with app.app_context():db.create_all()  # Creates all tables

你现在可以设置 HTML 模板来创建待办事项列表应用程序的前端,包括用于添加任务的内联 CSS 和表单内容。你还将定义用于列出现有任务和删除任务的其他功能。

HOME_HTML = """
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><title>To-Do List</title><style>body {font-family: Arial, sans-serif;background-color: #f4f4f9;margin: 40px auto;padding: 20px;max-width: 600px;box-shadow: 0 0 10px rgba(0,0,0,0.1);}h1 {color: #333;}form {margin-bottom: 20px;}input[type="text"] {padding: 10px;width: calc(100% - 22px);margin-bottom: 10px;}input[type="submit"] {background-color: #5cb85c;border: none;color: white;padding: 10px 20px;text-transform: uppercase;letter-spacing: 0.05em;cursor: pointer;}ul {list-style-type: none;padding: 0;}li {position: relative;padding: 8px;background-color: #fff;border-bottom: 1px solid #ddd;}.delete-button {position: absolute;right: 10px;top: 10px;background-color: #ff6347;color: white;border: none;padding: 5px 10px;border-radius: 5px;cursor: pointer;}</style>
</head>
<body><h1>To-Do List</h1><form action="/add" method="post"><input type="text" name="task" placeholder="Add new task"><input type="submit" value="Add Task"></form><ul>{% for task in tasks %}<li>{{ task.description }} <button class="delete-button" onclick="location.href='/delete/{{ task.id }}'">Delete</button></li>{% endfor %}</ul>
</body>
</html>
"""

现在,你可以创建路由,以便在加载时在应用程序上显示待办事项列表任务、添加新任务和删除任务。

/ 路由允许你定义当有人访问应用程序主页时返回哪些数据;在这种情况下,你从数据库输入的所有待办事项列表任务都将显示在屏幕上。

对于添加新任务,当你在应用程序上填写表单以添加和提交新任务时,/add 路由会将此新任务保存在数据库中。保存任务后,它会将你送回主页,以便他们可以看到添加了新任务的列表。

你还将为 /delete 定义一个路由,它描述了当你删除任务时会发生什么(在本例中,当你单击任务旁边的删除按钮时)。然后,应用程序会从数据库中删除该任务。

最后,你将添加逻辑来运行应用程序。

# Define route for the home page to display tasks
@app.route("/", methods=["GET"])
def home():tasks = Task.query.all()  # Retrieve all tasks from the databasereturn render_template_string(HOME_HTML, tasks=tasks)  # Render the homepage with tasks listed# Define route to add new tasks from the form submission
@app.route("/add", methods=["POST"])
def add():task_description = request.form["task"]  # Extract task description from form datanew_task = Task(description=task_description)  # Create new Task instancedb.session.add(new_task)  # Add new task to database sessiondb.session.commit()  # Commit changes to the databasereturn redirect(url_for("home"))  # Redirect to the home page# Define route to delete tasks based on task ID
@app.route("/delete/<int:task_id>", methods=["GET"])
def delete(task_id: int):task_to_delete = Task.query.get(task_id)  # Get task by IDif task_to_delete:db.session.delete(task_to_delete)  # Remove task from the database sessiondb.session.commit()  # Commit the change to the databasereturn redirect(url_for("home"))  # Redirect to the home page# Check if the script is the main program and run the app
if __name__ == "__main__":app.run()  # Start the Flask application

要在本地运行你的应用程序,你可以在终端中运行以下命令。

flask run -p 5000

检测应用程序

检测是指向应用程序添加可观察性功能以收集遥测数据,例如跟踪、指标和日志。在数据中,你可以看到正在运行的依赖服务;例如,你可以看到正在构建的应用程序(在此示例中)使用 SQLite。你还可以跟踪请求在分布式系统中跨度移动的各种服务,并查看有关在分布式系统中运行的进程的定量信息。

自动检测与手动检测

你有两种检测应用程序的选项:自动检测手动检测

自动检测会修改应用程序类的字节码以将监控代码插入到应用程序中。使用自动检测,你可以轻松监控应用程序,而无需担心创建自定义监控。这是一种开始监控应用程序或向现有应用程序添加监控功能的好方法。

手动检测允许你向应用程序添加自定义代码段以收集和传输遥测数据。如果你正在寻找自定义或发现自动检测仅涵盖你需要的部分内容,则它很有用。

向你的待办事项列表应用程序添加自动检测

要向你的 Flask 应用程序添加自动检测,你无需添加任何其他监控代码。当你运行应用程序时,OpenTelemetry 将通过 Python 路径自动添加所需的代码。你可以按照以下步骤向你的应用程序添加自动检测。

步骤 1:安装所需的软件包

首先,你需要安装检测所需的软件包。

pip install elastic-opentelemetry opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-instrumentation-flask
opentelemetry-bootstrap --action=install

第 2 步:设置本地环境变量

现在,你可以设置环境变量以包含应用程序的服务名称、API 密钥和弹性主机端点。

export OTEL_RESOURCE_ATTRIBUTES=service.name=todo-list-app
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=ApiKey <your-api-key>"
export OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-elastic-url>

步骤 3:运行应用程序

要使用 OpenTelemetry 运行你的应用程序,你需要在终端中运行以下命令:

opentelemetry-instrument flask run -p 5000

此时,如果你查看 Kibana,其中显示 “observability”  紧接着 “services”,你应该会看到你的服务列为你在环境变量中设置的服务名称。

如果你点击 service 名称,你应该会看到一个仪表板,其中包含待办事项列表应用程序的可观察性数据。下面的 “Transactions” 部分中的屏幕截图显示了你可以采取的操作,例如在使用 GET 方法加载应用程序时加载所有待办事项列表项,以及使用 POST 方法将新项目添加到待办事项列表中。

向你的待办事项列表应用程序添加手动检测

由于手动检测允许你根据自己的喜好自定义检测,因此你必须将自己的监控代码添加到你的应用程序中才能启动并运行。本节的完整代码示例可在此处找到。

首先,你需要通过设置以下环境变量来更新你的服务名称:

export OTEL_RESOURCE_ATTRIBUTES=service.name=mi-todo-list-app

现在你已经更新了 service 名称,你需要更新导入语句以包含更多功能,用于监控应用程序、解析环境变量以及确保标头格式正确。

import os
from flask import Flask, request, render_template_string, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import Mapped, mapped_column
from opentelemetry import trace, metrics
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter# Get environment variables
service_name = os.getenv("OTEL_RESOURCE_ATTRIBUTES", "service.name=todo-flask-app").split("=")[-1]
otlp_endpoint = os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
otlp_headers = os.getenv("OTEL_EXPORTER_OTLP_HEADERS")if not otlp_endpoint or not otlp_headers:raise ValueError("OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS must be set in environment variables")# Ensure headers are properly formatted for gRPC metadata
headers_dict = dict(item.split(":", 1) for item in otlp_headers.split(",") if ":" in item)

现在,你将需要配置你的应用程序以生成、批处理和发送跟踪数据到 Elastic,从而深入了解你的应用程序的运行和性能。

# Configure tracing provider and exporter
resource = Resource(attributes={"service.name": service_name
})
trace.set_tracer_provider(TracerProvider(resource=resource))
tracer_provider = trace.get_tracer_provider()otlp_trace_exporter = OTLPSpanExporter(endpoint=otlp_endpoint,headers=headers_dict,
)
span_processor = BatchSpanProcessor(otlp_trace_exporter)
tracer_provider.add_span_processor(span_processor)

你现在可以设置用于捕获遥测数据的框架。你需要创建跟踪器、计量器和计数器。跟踪器为分布式跟踪创建跨度,帮助了解分布式系统的流程和性能问题。计量器和计数器捕获操作指标(如计数请求)对于生产环境中的性能监控和警报至关重要。你的指标配置可确保这些指标得到适当批处理并发送到 Elastic 进行分析。

# Create a tracer
tracer = trace.get_tracer(__name__)# Configure metrics provider and exporter
otlp_metric_exporter = OTLPMetricExporter(endpoint=otlp_endpoint,headers=headers_dict,
)
metric_reader = PeriodicExportingMetricReader(otlp_metric_exporter)
meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
metrics.set_meter_provider(meter_provider)# Create a meter
meter = metrics.get_meter(__name__)
requests_counter = meter.create_counter(name="requests_count",description="Number of requests received",unit="1",
)

你将需要使用 Flask 和 SQLite 来获取有关可观察性后端(即 Elastic)中的这两项服务的信息。

FlaskInstrumentor().instrument_app(app)
with app.app_context():SQLAlchemyInstrumentor().instrument(engine=db.engine)

现在,你可以为每个应用程序路由配备跟踪和指标收集功能。这样你就可以定义向每个路由(GET、POST 和 DELETE)添加哪些跟踪和指标,从而让你能够了解运营绩效,同时还可以收集有关用户交互和系统效率的宝贵数据。

# Define route for the home page to display tasks
@app.route("/", methods=["GET"])
def home():with app.app_context():with tracer.start_as_current_span("home-request"):requests_counter.add(1, {"method": "GET", "endpoint": "/"})tasks = Task.query.all()  # Retrieve all tasks from the databasereturn render_template_string(HOME_HTML, tasks=tasks)  # Render the homepage with tasks listed# Define route to add new tasks from the form submission
@app.route("/add", methods=["POST"])
def add():with app.app_context():with tracer.start_as_current_span("add-task"):requests_counter.add(1, {"method": "POST", "endpoint": "/add"})task_description = request.form["task"]  # Extract task description from form datanew_task = Task(description=task_description)  # Create new Task instancedb.session.add(new_task)  # Add new task to database sessiondb.session.commit()  # Commit changes to the databasereturn redirect(url_for("home"))  # Redirect to the home page# Define route to delete tasks based on task ID
@app.route("/delete/<int:task_id>", methods=["GET"])
def delete(task_id: int):with app.app_context():with tracer.start_as_current_span("delete-task"):requests_counter.add(1, {"method": "GET", "endpoint": f"/delete/{task_id}"})task_to_delete = Task.query.get(task_id)  # Get task by IDif task_to_delete:db.session.delete(task_to_delete)  # Remove task from the database sessiondb.session.commit()  # Commit the change to the databasereturn redirect(url_for("home"))  # Redirect to the home page

由于你已将自定义监控代码应用到你的应用程序中,因此你可以像第一次创建它时一样在终端中运行。

flask run -p 5000

现在你应该可以在 “Services” 下看到你的数据,其方式与自动检测中相同。

结论

由于 OpenTelemetry 的一大特色是其可定制性,因此这只是你如何使用 Elastic 作为 OpenTelemetry 后端的开始。下一步,请探索我们的 OpenTelemetry 演示应用程序,以了解如何在更实际的应用程序中运用 Elastic。你也可以将此应用程序部署到服务器。

此示例的完整代码可在此处找到。如果你基于此博客构建了任何内容,或者你对我们的论坛和社区 Slack 频道有疑问,请告诉我们。

原文:Dec 6th, 2024: [EN] Adding OpenTelemetry to Your Flask Application - Advent Calendar - Discuss the Elastic Stack

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/728.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python双指针

双指针 双指针&#xff1a;在区间操作时&#xff0c;利用两个下标同时遍历&#xff0c;进行高效操作 双指针利用区间性质可以把 O ( n 2 ) O(n^2) O(n2) 时间降低到 O ( n ) O(n) O(n) 反向扫描 反向扫描&#xff1a; l e f t left left 起点&#xff0c;不断往右走&…

python无需验证码免登录12306抢票 --selenium(2)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 [TOC](python无需验证码免登录12306抢票 --selenium(2)) 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 就在刚刚我抢的票&#xff1a;2025年1月8日…

软件系统安全逆向分析-混淆对抗

1. 概述 在一般的软件中&#xff0c;我们逆向分析时候通常都不能直接看到软件的明文源代码&#xff0c;或多或少存在着混淆对抗的操作。下面&#xff0c;我会实践操作一个例子从无从下手到攻破目标。 花指令对抗虚函数表RC4 2. 实战-donntyousee 题目载体为具有漏洞的小型软…

解决nginx多层代理后应用部署后访问发现css、js、图片等样式加载失败

一般是采用前后端分离部署方式&#xff0c;被上一层ng代理后&#xff0c;通过域名访问报错&#xff0c;例如&#xff1a;sqx.com.cn/应用代理路径。 修改nginx配置&#xff0c;配置前端页面的路径&#xff1a; location / {proxy_pass http://前端页面所在服务器的IP:PORT;pro…

小程序textarea组件键盘弹起会遮挡住输入框

<textarea value"{{remark}}" input"handleInputRemark" ></textarea> 如下会有遮挡&#xff1a; 一行代码搞定 cursor-spacing160 修改后代码 <textarea value"{{remark}}" input"handleInputRemark" cursor-spacin…

1.CSS的复合选择器

1.1 什么是复合选择器 在CSS中&#xff0c;可以根据选择器的类型把选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基础选择器进行组合形成的。 复合选择器可以更精准、更高效的选择目标元素&#xff08;标签&#xff09; 复…

【微服务】面试 2、服务雪崩

服务雪崩概念 主要内容&#xff1a;在微服务项目中&#xff0c;微服务间存在远程调用。若某一服务&#xff08;如服务 d&#xff09;出现故障&#xff0c;调用它的服务&#xff08;如服务 a&#xff09;会失败。若调用方持续向故障服务发起请求&#xff0c;由于服务连接数有限且…

计算机视觉算法实战——车道线检测

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​​​​ ​​​​​​​​​​​​ ​​​​​ 车道线检测是计算机视觉领域的一个重要研究方向&#xff0c;尤其在自动驾驶和高级驾驶辅助…

腾讯云AI代码助手编程挑战赛 - 腾讯云AI代码助手小试

作品简介 本项目基于腾讯云AI代码助手&#xff0c;对平台提供的AI问答应用进行了功能拓展。页面显示采用Vue框架&#xff0c;对话部分使用TDesign组件&#xff0c;模型为hunyuan。主要新增了TDesign页面主题模式改变、页面主题颜色随机切换、文件上传模拟等功能&#xff0c;虽未…

【Web安全】SQL 注入攻击技巧详解:UNION 注入(UNION SQL Injection)

【Web安全】SQL 注入攻击技巧详解&#xff1a;UNION 注入&#xff08;UNION SQL Injection&#xff09; 引言 UNION注入是一种利用SQL的UNION操作符进行注入攻击的技术。攻击者通过合并两个或多个SELECT语句的结果集&#xff0c;可以获取数据库中未授权的数据。这种注入技术要…

机器学习05-最小二乘法VS梯度求解

机器学习05-最小二乘法VS梯度求解 文章目录 机器学习05-最小二乘法VS梯度求解0-核心知识点梳理1-最小二乘法和梯度求解算法什么关系最小二乘法梯度求解算法两者的关系 2-最小二乘法可以求解非线性回归吗3-最小二乘法不使用梯度求解算法&#xff0c;给出一个简单的示例&#xff…

网络授时笔记

SNTP的全称是Simple Network Time Protocol&#xff0c;意思是简单网络时间协议&#xff0c;用来从网络中获取当前的时间&#xff0c;也可以称为网络授时。项目中会使用LwIP SNTP模块从服务器(pool.ntp.org)获取时间 我们使用sntp例程&#xff0c;sntp例程路径为D:\Espressif\…

HTTP-响应协议

HTTP的响应过程&#xff1f; 浏览器请求数据--》web服务器过程&#xff1a;请求过程 web服务器将响应数据-》到浏览器&#xff1a;响应过程 响应数据有哪些内容&#xff1f; 1.和请求数据类似。 2. 响应体中存储着web服务器返回给浏览器的响应数据。并且注意响应头和响应体之间…

【学习笔记】理解深度学习和机器学习的数学基础:数值计算

深度学习作为人工智能领域的一个重要分支&#xff0c;其算法的实现和优化离不开数值计算。数值计算在深度学习中扮演着至关重要的角色&#xff0c;它涉及到如何在计算机上高效、准确地解决数学问题。本文将介绍深度学习中数值计算的一些关键概念和挑战&#xff0c;以及如何应对…

JVM与Java体系结构

一、前言: Java语言和JVM简介: Java是目前最为广泛的软件开发平台之一。 JVM:跨语言的平台 随着Java7的正式发布&#xff0c;Java虚拟机的设计者们通过JSR-292规范基本实现在Java虚拟机平台上运行非Java语言编写的程序。 Java虚拟机根本不关心运行在其内部的程序到底是使用何…

计科高可用服务器架构实训(防火墙、双机热备,VRRP、MSTP、DHCP、OSPF)

一、项目介绍 需求分析&#xff1a; &#xff08;1&#xff09;总部和分部要求网络拓扑简单&#xff0c;方便维护&#xff0c;网络有扩展和冗余性&#xff1b; &#xff08;2&#xff09;总部分财务部&#xff0c;人事部&#xff0c;工程部&#xff0c;技术部&#xff0c;提供…

4.3.3 最优二叉树+二叉查找树

文章目录 基本概念哈夫曼方法应用&#xff1a;通信编码译码二叉查找树 基本概念 最优二叉树哈夫曼树 哈夫曼树&#xff1a;带权路径长度最短的树。 路径&#xff1a;一个结点到另一个结点的通路。 路径长度&#xff1a;路径上的分支数量。 树的路径长度&#xff1a;根到每个叶子…

Conda虚拟Python环境下安装包遇到的坑

明天下午要去参加Nvidia组织的一个开发者夏令营活动&#xff0c;按照2024 NVIDIA开发者社区夏令营环境配置指南(Win & Mac)_nvidia mac-CSDN博客提供的指引配置环境。里面建议的是用conda来配置Python虚拟环境&#xff0c;原本本机直接安装最直接&#xff0c;不过正好学习下…

MVC执行流程

&#xff08;1&#xff09;用户通过浏览器&#xff08;客户端&#xff09;向服务端&#xff08;后端&#xff09;发送请求&#xff0c;请求会被前端控制器DispatcherServlet拦截。 &#xff08;2&#xff09;DispatcherServlet拦截到请求后&#xff0c;会调用处理器映射器&…

Spring——依赖注入之p命名空间和c命名空间

p命名空间 其实就是Set注入 只不过p命名空间写法更简洁 p可以理解为 property标签的首字母p p命名空间依赖于set方法 依赖引入 使用前需要再配置文件头文件中引入p命名空间的依赖&#xff1a; ** xmlns:p“http://www.springframework.org/schema/p” ** 用法 在bean标签…