使用 Elastic APM 监控你的 C++ 应用程序

作者:来自 Elastic Haidar Braimaanie

在本文中,我们将使用 Opentelemetry CPP 客户端来监控 Elastic APM 中的 C++ 应用程序。

介绍

开发人员、SRE 和 DevOps 专业人员面临的主要挑战之一是缺乏能够为他们提供应用程序堆栈可见性的综合工具。市场上的许多 APM 解决方案提供了监控基于语言和框架(即 .NET、Java、Python 等)构建的应用程序的方法,但在 C++ 应用程序方面却存在不足。

幸运的是,Elastic 一直是可观察性领域的领先解决方案之一,也是 OpenTelemetry 项目的贡献者。 Elastic 的独特地位及其广泛的可观察性能力使得最终用户能够以多种方式监控用面向对象编程语言和框架构建的应用程序。

在这篇博客中,我们将探索使用 Elastic APM 通过 OpenTelemetry 客户端调查 C++ 跟踪。我们将提供有关如何为 C++ 应用程序实现 OpenTelemetry 客户端并连接到 Elastic APM 解决方案的全面指南。虽然 OTel 有自己的库,并且本博客回顾了如何使用 OTel CPP 库,但 Elastic 也有自己的 OpenTelemetry Elastic Distributions,它是为了提供商业支持而开发的,并且定期完全 upstream。

以下有一些资源可以帮助你入门:

  • 将 OpenTelemetry 与 APM 结合使用
  • OpenTelemetry C++ 客户端
  • OpenTelemetry C++ 文档

先决条件

  • 环境

        选择环境非常重要,因为对 OTEL 客户端的支持有限。我们尝试使用多种操作系统,并提出以下建议:

  • Ubuntu 22.04
  • Debian 11 Bullseye
  • 在本指南中,我们重点关注 Ubuntu 22.04。
    • 机器:2 vCPU,4GB 就足够了。
    • 图像:Ubuntu 22.04 LTS(x86_64)。
    • 磁盘:~30 GB 就足够了。

实现方法

我们尝试了多种方法,但发现最合适的方法是使用包管理器。经过广泛的测试,尝试运行 otel-cpp 客户端对于用户来说似乎相当具有挑战性。如果从业者希望使用 CMake 和 Bazel 等工具进行构建,这是一个可行的解决方案。因此,当我们测试这两种方法时,很明显我们花费了大部分的时间和精力来修复操作系统的兼容性和依赖性问题,而不是专注于将数据发送到我们的 APM。因此我们决定采用不同的方法。

我们在测试中遇到的主要问题是:

  • 包的兼容性。
  • 包裹的可用性。
  • 库和包的依赖关系。

在本指南中,我们将使用 vcpkg,因为它允许我们引入运行 Opentelemetry C++ 客户端所需的所有依赖项。

安装所需的操作系统工具

更新软件包列表

    sudo apt-get update

安装 build essentials、cmake、git 和 sqlite 开发库

    sudo apt-get install -y build-essential cmake git curl zip unzip sqlite3 libsqlite3-dev

sqlite3 和 libsqlite3-dev 允许我们在 C++ 代码中构建/运行 SQLite 查询。

设置 vcpkg

vcpkg 是 C++ 包管理器,我们将使用它来安装 opentelemetry-cpp 客户端。

    # Clone vcpkgcd ~git clone https://github.com/microsoft/vcpkg.git
    # Bootstrapcd ~/vcpkg./bootstrap-vcpkg.sh

使用 OTLP gRPC 设置 UInstall OpenTelemetry C++

在本指南中,我们重点介绍将跟踪导出到 Elastic。在撰写本文时,vcpkg 的 opentelemetry-cpp 版本 1.18.0 完全支持跟踪,但直接指标导出受到限制。

安装包

    cd ~/vcpkg./vcpkg install opentelemetry-cpp[otlp-grpc]:x64-linux

注意:有时在 Linux 上安装 opentelemetry-cpp 时它不会安装所有必需的软件包。如果遇到这种情况,请尝试再次运行,但传递一个标志以允许不受支持(allow-unsupported):

    ./vcpkg install opentelemetry-cpp[*]:x64-linux --allow-unsupported

验证

    ./vcpkg list | grep opentelemetry-cpp

输出应该是这样的:

opentelemetry-cpp:x64-linux 1.18.0

使用数据库跨度创建 C++ 项目

我们将在 ~/otel-app 中构建一个示例:

  • 使用 SQLite 执行基本的 CREATE/INSERT/SELECT 查询。这有助于展示使用 Elastic APM 上的数据库的应用程序捕获交易。
  • 生成随机跟踪以展示如何在 Elastic APM 上捕获它们。

该应用程序将生成随机查询,其中一些包含数据库事务,一些只是应用程序跟踪。每个查询都包含在一个子跨度中,因此它们在 APM 中显示为单独的数据库事务。

以下是我们项目的结构:

    otel-app/├── main.cpp└── CMakeLists.txt

创建应用项目

    cd ~mkdir otel-appcd otel-app

在这个项目中我们将创建两个文件:

  • main.cpp
  • CMakeLists.txt

请记住,main.cpp 是你要传递将要向 Elastic 集群发送数据的 otel 导出器(exporters)的地方。因此对于你的技术堆栈来说,它将是你的应用程序的源代码。

示例应用程序代码

    main.cpp// Below we declare required libraries that we will be using to ship// traces to Elastic APM#include <opentelemetry/exporters/otlp/otlp_grpc_exporter.h>#include <opentelemetry/sdk/trace/tracer_provider.h>#include <opentelemetry/sdk/trace/simple_processor.h>#include <opentelemetry/trace/provider.h>#include <sqlite3.h>#include <chrono>#include <iostream>#include <thread>#include <cstdlib>  // for rand(), srand()#include <ctime>    // for time()// Namespace aliasesnamespace trace_api = opentelemetry::trace;namespace sdktrace  = opentelemetry::sdk::trace;namespace otlp      = opentelemetry::exporter::otlp;// Below we are using a helper function to run SQLITE statement inside // child spanbool ExecuteSql(sqlite3 *db, const std::string &sql,trace_api::Tracer &tracer,const std::string &span_name){// Starting the child spanauto db_span = tracer.StartSpan(span_name);{auto scope = tracer.WithActiveSpan(db_span);// Here we mark Database attributes for clarity in APMdb_span->SetAttribute("db.system", "sqlite");db_span->SetAttribute("db.statement", sql);char *errMsg = nullptr;int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg);if (rc != SQLITE_OK){db_span->AddEvent("SQLite error: " + std::string(errMsg ? errMsg : "unknown"));sqlite3_free(errMsg);db_span->End();return false;}db_span->AddEvent("Query OK");}db_span->End();return true;}/*** DoNonDbWork - Simulate some other operation*/void DoNonDbWork(trace_api::Tracer &tracer, const std::string &span_name){auto child_span = tracer.StartSpan(span_name);{auto scope = tracer.WithActiveSpan(child_span);// Just sleep or do some "fake" workstd::cout << "[TRACE] Doing non-DB work for " << span_name << "...\n";std::this_thread::sleep_for(std::chrono::milliseconds(200 + rand() % 300));child_span->AddEvent("Finished non-DB work");}child_span->End();}int main(){// Seed random generator for examplesrand(static_cast<unsigned>(time(nullptr)));// 1) Create OTLP exporter for tracesotlp::OtlpGrpcExporterOptions opts;auto exporter = std::make_unique<otlp::OtlpGrpcExporter>(opts);// 2) Simple Span Processorauto processor = std::make_unique<sdktrace::SimpleSpanProcessor>(std::move(exporter));// 3) Tracer Providerauto sdk_tracer_provider = std::make_shared<sdktrace::TracerProvider>(std::move(processor));auto tracer = sdk_tracer_provider->GetTracer("my-cpp-multi-app");// Prepare an in-memory SQLite DB (for random DB usage)sqlite3 *db = nullptr;int rc = sqlite3_open(":memory:", &db);if (rc == SQLITE_OK){// Create a table so we can do inserts/readsExecuteSql(db, "CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, info TEXT);",*tracer.get(), "db_create_table");}// Create the following loop to generate multiple transactionsint num_transactions = 5;  // Change this variable to the desired number of transactionfor (int i = 1; i <= num_transactions; i++){// Each iteration is a top-level transactionstd::string transaction_name = "transaction_" + std::to_string(i);auto parent_span = tracer->StartSpan(transaction_name);{auto scope = tracer->WithActiveSpan(parent_span);std::cout << "\n=== Starting " << transaction_name << " ===\n";// Randomly select whether a transaction will interact with the database or not.bool doDb = (rand() % 2 == 0); // 50% chanceif (doDb && db){// Insert random datastd::string insert_sql = "INSERT INTO items (info) VALUES ('Item " + std::to_string(i) + "');";ExecuteSql(db, insert_sql, *tracer.get(), "db_insert_item");// Select from DBExecuteSql(db, "SELECT * FROM items;", *tracer.get(), "db_select_items");}else{// Do some random non-DB tasksDoNonDbWork(*tracer.get(), "non_db_task_1");DoNonDbWork(*tracer.get(), "non_db_task_2");}// Sleep a little to simulate transaction timestd::this_thread::sleep_for(std::chrono::milliseconds(200));}parent_span->End();}// Close DBsqlite3_close(db);// Extra sleep to ensure final flushstd::cout << "\n[INFO] Sleeping 5 seconds to allow flush...\n";std::this_thread::sleep_for(std::chrono::seconds(5));std::cout << "[INFO] Exiting.\n";return 0;}
代码起什么作用?

我们创建 5 个顶级 “transaction_i” 跨度。

对于每笔 transaction,我们随机选择执行 DB 或非 DB 工作

- If DB: Insert a row, then select. Each is a child span.- If non-DB: We do two “fake tasks” (child spans).

一旦完成后,我们关闭数据库连接并等待 5 秒钟以刷新数据。

示例说明文件

CMakeLists.txt:此文件包含描述源文件和目标的说明。

    cmake_minimum_required(VERSION 3.10)project(OtelApp VERSION 1.0)set(CMAKE_CXX_STANDARD 11)set(CMAKE_CXX_STANDARD_REQUIRED ON)# Here we are pointing to use the vcpkg toolchainset(CMAKE_TOOLCHAIN_FILE "PATH-TO/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")find_package(opentelemetry-cpp CONFIG REQUIRED)add_executable(otel_app main.cpp)# Below we are linking the OTLP gRPC exporter, trace library, and sqlite3target_link_libraries(otel_app PRIVATEopentelemetry-cpp::otlp_grpc_exporteropentelemetry-cpp::tracesqlite3)

声明环境变量

在这里,我们将把 Elastic Cloud 端点导出为环境变量

你可以通过执行以下操作来获取此信息:

  • 登录到你的 Elastic Cloud
  • 进入你的 Deployment
  • 在左侧,点击汉堡菜单并向下滚动到 “Integrations”
  • 转到集成内的搜索栏并输入 “APM”

  • 点击 APM 集成
  • 向下滚动并单击最左侧的 OpenTelemetry 选项

  • 你应该能够看到类似于以下屏幕截图的值。复制要导出的值后,单击启动 APM。

    export OTEL_EXPORTER_OTLP_ENDPOINT="APM-ENDPOINT"export OTEL_EXPORTER_OTLP_HEADERS="KEY"export OTEL_RESOURCE_ATTRIBUTES="service.name=my-app,service.version=1.0.0,deployment.environment=dev"

请注意,弹性 OTEL_EXPORTER_OTLP_HEADERS 值通常以 “Authorization=Bearer” 开头,确保将授权中的大写 “A” 转换为小写 “a”。这是因为 otel 标头导出​​器需要小写 “a” 来表示授权。

构建并运行

一旦创建了这两个文件,我们就可以开始构建应用程序。

cd ~/otel-app mkdir -p build cd build

应用程序成功运行后:

./otel-app

你应该能够看到脚本执行并带有类似的控制台输出:

    Console outcome:=== Starting transaction_1 ===[TRACE] Doing non-DB work for non_db_task_1...[TRACE] Doing non-DB work for non_db_task_2...=== Starting transaction_2 ===[TRACE] Doing DB work for doDb_task_1...[TRACE] Doing DB work for doDb_task_2...=== Starting transaction_3 ===[TRACE] Doing non-DB work for non_db_task_1...[TRACE] Doing non-DB work for non_db_task_2...=== Starting transaction_4 ===[TRACE] Doing non-DB work for non_db_task_1...[TRACE] Doing non-DB work for non_db_task_2...=== Starting transaction_5 ===[TRACE] Doing non-DB work for non_db_task_1...[TRACE] Doing non-DB work for non_db_task_2...[INFO] Sleeping 5 seconds to allow flush...[INFO] Exiting.

一旦脚本执行,你应该能够在 Elastic APM 上观察到类似于下面的屏幕截图的跟踪。

在 Elastic APM 中观察

转到 Elastic Cloud,打开你的部署,然后导航到 Observability > APM。

在服务列表中查找应用程序名称(由OTEL_RESOURCE_ATTRIBUTES 定义)。

在该服务的 “Traces” 选项卡中,你会发现多个交易,例如 “transaction_1”,“transaction_2”等等。

展开每个事务会显示子跨度:

- Possibly db_insert_item and db_select_items if random DB path was taken.- Otherwise, non_db_task_1 and non_db_task_2.

你可以看到有些事务进行 DB 调用,有些则不进行,每个事务都有不同的跨度。

这种多样性展示了你的实际应用可能会产生多种不同的 “routes” 或 “operations”。

Service Map

如果一切运行正常,你应该能够查看你的服务并查看应用程序的服务地图。

Services

My Elastic App

App Transactions

Dependencies

Logs

导航到日志窗口/Discover 以查看传入的应用程序日志:

Patterns

日志模式分析可以帮助你在非结构化日志消息中查找模式,并让你更轻松地检查数据。

最后回顾

以下是我们所做工作的简要总结:

  • 在 Ubuntu 22.04 机器上配置。
  • 安装了 SQLite、dev libs 和 vcpkg 的构建工具。
  • 通过 vcpkg 安装了 opentelemetry-cpp 的客户端。
  • 创建了一个执行应用程序跟踪并捕获数据库操作的最小 C++ 项目。
  • 在 CMakeLists.txt 中连接数据库 sqlite3。
  • 将 Elastic OTLP 端点和令牌作为环境变量导出(小写的 authorization=Bearer key!)。
  • 运行应用程序并在 Elastic APM 中观察数据库交互和应用程序跟踪。
  • 在 Elastic 日志和 Discover 上观察应用程序日志和模式。

常见问题

  • 获取 “Could not find package configuration file provided by opentelemetry-cpp”?

        确保你通过:

-DCMAKE_TOOLCHAIN_FILE=... and -DCMAKE_PREFIX_PATH=... 

传递给 cmake,或者将其嵌入 CMakeLists.txt。

  • Crash: “validate_metadata: INTERNAL:Illegal header key”?

使用全部小写字母

OTEL_EXPORTER_OTLP_HEADERS, e.g. authorization=Bearer \<token>.
  • Missing otlp_grpc_metrics_exporter.h?

你的 vcpkg 版本的 opentelemetry-cpp (1.18.0) 缺少用于 OTLP 的直接指标导出器。对于指标,要么升级库,要么考虑采用 OpenTelemetry Collector 方法。

  • No data in Elastic APM?

在 APM 中仔细检查你的端点 URL、Bearer token、防火墙规则或服务名称

原文:Monitor your C++ Applications with Elastic APM — Elastic Observability Labs

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

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

相关文章

前端骨架怎样实现

前端骨架屏&#xff08;Skeleton Screen&#xff09;是一种优化页面加载体验的技术&#xff0c;通常在内容加载时展示一个简易的占位符&#xff0c;避免用户看到空白页面。骨架屏通过展示页面结构的骨架样式&#xff0c;让用户有页面正在加载的感觉&#xff0c;而不是等待内容加…

团结引擎 Shader Graph:解锁图形创作新高度

Shader Graph 始终致力于为开发者提供直观且高效的着色器构建工具&#xff0c;持续推动图形渲染创作的创新与便捷。在团结引擎1.4.0中&#xff0c;Shader Graph 迎来了重大更新&#xff0c;新增多项强大功能并优化操作体验&#xff0c;助力开发者更轻松地实现高质量的渲染效果与…

微信小程序地图标记点,安卓手机一次性渲染不出来的问题

问题描述&#xff1a; 如果微信小程序端&#xff0c;渲染的标记物太多&#xff0c;安卓手机存在标记物不显示的问题&#xff0c;原因初步判断是地图还没有渲染完&#xff0c;标记物数据已经加载完了&#xff0c;导致没有在地图上显示。 解决办法&#xff1a; 使用map组件的b…

AI前端开发的崛起与ScriptEcho的助力

近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术飞速发展&#xff0c;深刻地改变着软件开发的格局。尤其是在前端开发领域&#xff0c;AI的应用越来越广泛&#xff0c;催生了对AI写代码工具的需求激增&#xff0c;也显著提升了相关人才的市场价值。然而&#xff0c;…

安装并配置 MySQL

MySQL 是世界上最流行的开源关系型数据库管理系统之一&#xff0c;因其高性能、可靠性和易用性而被广泛应用于各种规模的企业级应用中。本文将详细介绍如何在不同的操作系统上安装和配置 MySQL&#xff0c;帮助你快速搭建起一个功能完善的数据库环境。 选择适合你的安装方式 …

《探秘Windows 10驱动开发:从入门到实战》

《探秘Windows 10驱动开发:从入门到实战》 为什么要在 Windows 10 编写驱动程序 在当今数字化时代,计算机已成为人们生活和工作中不可或缺的工具 ,而 Windows 10 作为一款广泛使用的操作系统,其生态系统的丰富性和复杂性不言而喻。在这个庞大的体系中,驱动程序扮演着举足…

【prompt示例】智能客服+智能质检业务模版

本文原创作者&#xff1a;姚瑞南 AI-agent 大模型运营专家&#xff0c;先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗&#xff1b;多年人工智能行业智能产品运营及大模型落地经验&#xff0c;拥有AI外呼方向国家专利与PMP项目管理证书。&#xff08;转载需经授权&am…

算法17(力扣217)存在重复元素

1、问题 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 &#xff0c;返回 true &#xff1b;如果数组中每个元素互不相同&#xff0c;返回 false 。 2、示例 &#xff08;1&#xff09; 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;…

使用 ffmpeg 给视频批量加图片水印

背景 事情是这样的……前两天突然接到 leader 给的一个任务&#xff1a;给视频加上图片 logo 水印。我这种剪映老司机当然迷之一笑了哈哈哈哈哈&#xff0c;沉浸在简单的任务中还没反应过来巴掌就如洪水般涌来&#xff0c;因为 leader 给了几十个视频……作为一个计算机人&…

CSS 属性选择器详解与实战示例

CSS 属性选择器是 CSS 中非常强大且灵活的一类选择器&#xff0c;它能够根据 HTML 元素的属性和值来进行精准选中。在实际开发过程中&#xff0c;属性选择器不仅可以提高代码的可维护性&#xff0c;而且能够大大优化页面的样式控制。本文将结合菜鸟教程的示例&#xff0c;从基础…

基于SpringBoot和PostGIS的省域“地理难抵点(最纵深处)”检索及可视化实践

目录 前言 1、研究背景 2、研究意义 一、研究目标 1、“地理难抵点”的概念 二、“难抵点”空间检索实现 1、数据获取与处理 2、计算流程 3、难抵点计算 4、WebGIS可视化 三、成果展示 1、华东地区 2、华南地区 3、华中地区 4、华北地区 5、西北地区 6、西南地…

计算机毕业设计——Springboot的校园新闻网站

&#x1f4d8; 博主小档案&#xff1a; 花花&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 花花在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于java、python等技术。近年来&#xff0c;花花更…

PyCharm 批量替换

选择替换的内容 1. 打开全局替换窗口 有两种方式可以打开全局替换窗口&#xff1a; 快捷键方式&#xff1a; 在 Windows 或 Linux 系统下&#xff0c;按下 Ctrl Shift R。在 Mac 系统下&#xff0c;按下 Command Shift R。菜单操作方式&#xff1a;点击菜单栏中的 Edit&…

深度剖析责任链模式

一、责任链模式的本质&#xff1a;灵活可扩展的流水线处理 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是行为型设计模式的代表&#xff0c;其核心思想是将请求的发送者与接收者解耦&#xff0c;允许多个对象都有机会处理请求。这种模式完美解决了以下…

服务器使用centos7.9操作系统前需要做的准备工作

文章目录 前言1.操作记录 总结 前言 记录一下centos7.9操作系统的服务器在部署业务服务之前需要做的准备工作。 大家可以复制到自己的编辑器里面&#xff0c;有需求的注释一些步骤。 备注&#xff1a;有条件的项目推荐使用有长期支持的操作系统版本。 1.操作记录 # 更换阿里云…

Aitken 逐次线性插值

Aitken 逐次线性插值 用 Lagrange 插值多项式 L n ( x ) L_n(x) Ln​(x)计算函数近似值时&#xff0c;如需增加插值节点&#xff0c;那么原来算出的数据均不能利用&#xff0c;必须重新计算。为克服这个缺点&#xff0c;可用逐次线性插值方法求得高次插值。 令 I i 1 , i 2…

HARCT 2025 分论坛9:专用设备和机器人系统

会议名称&#xff1a;机电液一体化与先进机器人控制技术国际会议 会议简称&#xff1a;HARCT 2025 大会时间&#xff1a;2025年3月28日-30日 大会地点&#xff1a;中国桂林 主办单位&#xff1a;桂林航天工业学院、广西大学、桂林电子科技大学、桂林理工大学 协办单位&…

建筑兔零基础自学python记录18|实战人脸识别项目——视频检测07

本次要学视频检测&#xff0c;我们先回顾一下图片的人脸检测建筑兔零基础自学python记录16|实战人脸识别项目——人脸检测05-CSDN博客 我们先把上文中代码复制出来&#xff0c;保留红框的部分。 ​ 然后我们来看一下源代码&#xff1a; import cv2 as cvdef face_detect_demo(…

图书管理项目(spring boot + Vue)

想要该项目的话&#xff0c;就 jia 我&#xff0c;并在评论区给我说一下&#xff0c;只需要1元&#xff0c;我把整个项目发给你 jia微&#xff1a;18439421203&#xff08;名字叫&#xff1a;Bingo&#xff09; 运行图片&#xff1a;

Kubernetes 最佳实践:Top 10 常见 DevOps/SRE 面试问题及答案

1. 如何在 Kubernetes 中设置资源请求和限制&#xff1f; 资源请求确保容器有最小资源量&#xff08;CPU/内存&#xff09;&#xff0c;而限制则强制容器消耗的最大资源量。这有助于高效资源分配并防止资源争用。 示例&#xff1a; resources:requests:memory: "256Mi&…