Greg Kalapos

如何在 Elastic APM Agent 功能中结合 OpenTelemetry 监控

这篇文章将向您展示如何将 OpenTelemetry 追踪 API 与 Elastic APM Agent 结合使用。您将了解 OpenTelemetry span 如何成为 Elastic APM Agent 报告的追踪的一部分。

How to combine OpenTelemetry instrumentation with Elastic APM Agent features

Elastic APM 在多个层面支持 OpenTelemetry。在一个易于理解的场景中,我们之前博文中已经介绍过,APM 服务器直接支持 OpenTelemetry 协议 (OTLP)。这意味着您可以将任何 OpenTelemetry 代理连接到 Elastic APM 服务器,APM 服务器将愉快地接收这些数据,将其导入到 Elasticsearch® 中,您可以在 Kibana® 中的 APM 应用程序中查看这些 OpenTelemetry 数据。

这篇博文将展示一个不同的用例:在 Elastic APM 中,我们有我们自己的 APM 代理。其中一些的下载量达数千万,有些甚至早于 OpenTelemetry 出现。当然,我们意识到 OpenTelemetry 非常重要,并且会一直存在,因此我们希望使这些代理与 OpenTelemetry 兼容,并在这篇博文中使用OpenTelemetry 可视化来演示它们。

我们今天的大多数 Elastic APM 代理能够将 OpenTelemetry span 作为追踪的一部分进行发送。这意味着,如果您应用程序中的任何组件发出 OpenTelemetry span,它将成为 Elastic APM Agent 捕获的追踪的一部分。这可以是您使用的已经由 OpenTelemetry API 监控的库,也可以是应用程序开发人员添加到应用程序代码中进行手动监控的任何其他 OpenTelemetry span。

Elastic APM Agent 的此功能不仅报告这些 span,而且还正确维护所有 span 之间的父子关系,使 OpenTelemetry 成为这些代理的一等公民。例如,如果 Elastic APM Agent 通过自动监控启动特定操作的 span,然后在该 span 内 OpenTelemetry API 启动另一个 span,则 OpenTelemetry span 将是代理创建的外部 span 的子 span。这反映在 span 的 parent.id 字段中。反过来也是一样的:如果 span 由 OpenTelemetry API 创建,并且在该 span 内 Elastic APM 代理捕获另一个 span,则由 Elastic APM Agent 创建的 span 将是 OpenTelemetry API 创建的另一个 span 的子 span。

此功能存在于以下代理中:

在 Elastic .NET APM Agent 中捕获 OpenTelemetry span

作为第一个示例,让我们以一个 ASP.NET Core 应用程序为例。我们将 .NET Elastic APM Agent 放入此应用程序中,并将启用自动桥接 OpenTelemetry span 的功能,这样 Elastic APM Agent 将使这些 span 成为其报告的追踪的一部分。

以下代码片段显示了一个控制器

namespace SampleAspNetCoreApp.Controllers
{
	public class HomeController : Controller
	{
		private readonly SampleDataContext _sampleDataContext;
		private ActivitySource _activitySource = new ActivitySource("HomeController");
		public HomeController(SampleDataContext sampleDataContext) => _sampleDataContext = sampleDataContext;
		public async Task<IActionResult> Index()
		{
			await ReadGitHubStars();
			return View();
		}
		public async Task ReadGitHubStars()
		{
			using var activity = _activitySource.StartActivity();
			var httpClient = new HttpClient();
			httpClient.DefaultRequestHeaders.Add("User-Agent", "APM-Sample-App");
			var responseMsg = await httpClient.GetAsync("https://api.github.com/repos/elastic/apm-agent-dotnet");
			var responseStr = await responseMsg.Content.ReadAsStringAsync();
			// …use responseStr
		}
	}
}

Index 方法调用 ReadGitHubStars 方法,之后我们只需从方法返回相应的视图。

传入的 HTTP 调用和 HttpClient 发出的传出 HTTP 调用由 Elastic APM Agent 自动捕获 — 这是我们长期以来一直拥有的自动监控的一部分。

ReadGitHubStars 是我们使用 OpenTelemetry API 的地方。.NET 中的 OpenTelemetry 使用 ActivitySource 和 Activity API。_activitySource.StartActivity() 调用只需创建一个 OpenTelemetry span,通过使用CallerMemberNameAttribute C# 语言特性自动获取方法的名称,并且此 span 将在方法运行完成后结束。

此外,在此 span 中,我们使用 HttpClient 类型调用 GitHub API。对于此类型,.NET Elastic APM Agent 再次提供自动监控,因此 HTTP 调用也将由代理自动捕获为 span。

以下是 Kibana 中此事务的流程图

如您所见,代理能够将 OpenTelemetry span 捕获为追踪的一部分。

使用 Python Elastic APM Agent 在 Python 中桥接 OpenTelemetry span

让我们看看这在 Python 中是如何工作的。思路相同,因此前面介绍的所有概念也适用于此示例。

我们以一个非常简单的 Django 示例为例

from django.http import HttpResponse
from elasticapm.contrib.opentelemetry import Tracer
import requests


def index(request):
   tracer = Tracer(__name__)
   with tracer.start_as_current_span("ReadGitHubStars"):
       url = "https://api.github.com/repos/elastic/apm-agent-python"
       response = requests.get(url)
       return HttpResponse(response)

在 Python 中启用捕获 OpenTelemetry span 的第一步是从 elasticapm.contrib.opentelemetry 导入 Tracer 实现。

然后,您可以在此 Tracer 上启动一个新的 span — 在本例中,我们手动将 span 命名为 ReadGitHubStars。

与前面的示例类似,对http://127.0.0.1:8000/otelsample/ 的调用由 Elastic APM Python Agent 捕获,然后由 OpenTelemetry API 创建下一个 span,如您所见,该 span 会被代理自动捕获,最后对 GitHub API 的 HTTP 调用再次由代理的自动监控捕获。

以下是流程图

如前所述,代理维护所有 OTel span 的父子关系。让我们看一下 GET api.github.com 调用的 parent.id

如您所见,此 span 的 ID 为 c98401c94d40b87a。

如果我们查看 ReadGitHubStars OpenTelemetry span 的 span.id,则可以看到此 span 的 ID 正好是 c98401c94d40b87a — 因此 APM Agent 在内部维护 OpenTelemetry 和非 OpenTelemetry span 之间的父子关系,这使得 OpenTelemetry span 在 Elastic APM Agent 中成为一等公民。

其他语言

到此为止,我将停止在其他语言中复制完全相同的示例代码——我认为您已经明白了这一点:在上面列出的每种语言中,我们的 Elastic APM 代理都能够桥接 OpenTelemetry 追踪并在 Kibana 中将其显示为原生跨度。我们还撰写了关于在 Java 中使用相同 API 的博文,您可以在相应的代理文档(上面链接)中查看其他语言的示例。

何时使用此功能以及何时使用纯 OpenTelemetry SDK

这完全取决于您。如果您只想在您的应用程序中使用纯 OpenTelemetry,并且确实想要避免任何与供应商相关的软件,那么您可以直接使用 OpenTelemetry SDK——这是一个我们明确支持的用例。如果您选择此路径,此功能与您关系不大。

但是,我们的 Elastic APM 代理已经拥有非常庞大的用户群,并且它们提供 OpenTelemetry 中不存在的功能。其中一些功能包括跨度压缩集中配置推断跨度、具有多个 APM 服务器的分布式基于尾部的采样,以及更多其他功能。

如果您是众多 Elastic APM 代理用户的其中一位,或者您计划由于上述功能而使用 Elastic APM 代理,那么桥接 OpenTelemetry 跨度使您仍然可以使用 OpenTelemetry API,而无需依赖任何与供应商相关的 API 使用。这样,您的开发团队可以使用 OpenTelemetry 来检测您的应用程序,您还可以使用 OpenTelemetry 已检测到的任何第三方库,Elastic APM 代理将很乐意将这些跨度报告为其报告的追踪的一部分。通过这种方式,您可以结合 OpenTelemetry 的供应商独立性和功能丰富的 Elastic APM 代理。

如果您希望将遥测库从 Elastic APM 代理更改为 OpenTelemetry(反之亦然),OpenTelemetry 桥接功能也是一个不错的工具,因为它允许您同时使用这两个库并使用原子更改来切换它们。

后续步骤

在这篇博文中,我们讨论了如何将 OpenTelemetry 跨度与 Elastic APM 代理桥接。当然,OpenTelemetry 不仅仅是追踪。我们知道这一点,并且我们计划涵盖更多领域:目前,我们正在以非常相似的方式致力于在我们的 Elastic APM 代理中桥接 OpenTelemetry 指标。您可以在此处查看进度。

了解有关将 Elastic APM 作为 Elastic 可观测性部署一部分的更多信息.