为 .NET、Java、Node.js 和 Python 应用程序添加和修改 Azure Monitor OpenTelemetry

本指南提供有关在 Azure Monitor Application Insights 中集成和自定义 OpenTelemetry (OTel) 仪表的说明。

若要详细了解 OpenTelemetry 概念,请参阅 OpenTelemetry 概述OpenTelemetry 常见问题解答

自动数据收集

发行版通过捆绑 OpenTelemetry 检测库来自动收集数据。

包含的检测库

请求

  • Java 消息服务 (JMS) 使用者
  • Kafka 使用者
  • Netty
  • 石英
  • RabbitMQ
  • Servlet
  • Spring 计划

注意

Servlet 和 Netty 自动检测涵盖大多数 Java HTTP 服务,包括 Java EE、Jakarta EE、Spring Boot、Quarkus 和 Micronaut

依赖项(以及下游分布式跟踪传播)

  • Apache HttpClient
  • Apache HttpAsyncClient
  • AsyncHttpClient
  • Google HttpClient
  • gRPC
  • java.net.HttpURLConnection
  • Java 11 HttpClient
  • JAX-RS 客户端
  • Jetty HttpClient (Jetty HTTP客户端)
  • Java 消息服务 (JMS)
  • 卡 夫 卡
  • Netty 客户端
  • OkHttp
  • RabbitMQ

依赖项(无下游分布式跟踪传播)

  • 支持 Cassandra
  • 支持 Java 数据库连接 (JDBC)
  • 支持 MongoDB (异步和同步)
  • 支持 Redis (Lettuce 和 Jedis)

指标

  • Micrometer 指标,包括 Spring Boot Actuator 指标
  • Java 管理扩展 (JMX) 指标

日志

  • Logback(包含 MDC 属性) ¹
  • Log4j(包括 MDC/线程上下文属性) ¹
  • JBoss 日志记录功能(包括 MDC 属性) ¹
  • java.util.logging ¹

若要减少或增加 Azure Monitor 收集的日志数,请先在应用程序的日志记录库中设置所需的日志记录级别(例如 WARNINGERROR)。

默认集合

默认情况下,自动收集以下 Azure SDK 发出的遥测数据:

[//]: # "Azure Cosmos DB 4.22.0+ due to https://github.com/Azure/azure-sdk-for-java/pull/25571"
[//]: # "the remaining above names and links scraped from https://azure.github.io/azure-sdk/releases/latest/java.html"
[//]: # "and version synched manually against the oldest version in maven central built on azure-core 1.14.0"
[//]: # ""
[//]: # "var table = document.querySelector('#tg-sb-content > div > table')"
[//]: # "var str = ''"
[//]: # "for (var i = 1, row; row = table.rows[i]; i++) {"
[//]: # "  var name = row.cells[0].getElementsByTagName('div')[0].textContent.trim()"
[//]: # "  var stableRow = row.cells[1]"
[//]: # "  var versionBadge = stableRow.querySelector('.badge')"
[//]: # "  if (!versionBadge) {"
[//]: # "    continue"
[//]: # "  }"
[//]: # "  var version = versionBadge.textContent.trim()"
[//]: # "  var link = stableRow.querySelectorAll('a')[2].href"
[//]: # "  str += '* [' + name + '](' + link + ') ' + version + '\n'"
[//]: # "}"
[//]: # "console.log(str)"

脚注

  • ¹:支持未经处理的/未捕获的异常的自动报告
  • ²:支持 OpenTelemetry 指标

注意

Azure Monitor OpenTelemetry 发行版包括自定义映射和逻辑,用于自动发出 Application Insights 标准指标

提示

所有 OpenTelemetry 指标,无论是从检测库自动收集的还是从自动编码中收集收集的,现在都被认为是用于计费目的的 Application Insights“自定义指标”。 了解详细信息

添加社区检测库

从 OpenTelemetry 社区包含检测库时,可以自动收集更多数据。

警告

我们不支持也不保证社区检测库的质量。 要为我们的分发版推荐一个检测库,请在反馈社区中发帖或投票。 请注意,某些检测库基于实验性 OpenTelemetry 规范,可能会在将来引入中断性变更。

不能使用社区检测库来扩展 Java 发行版。 若要请求我们加入另一个检测库,请在 GitHub 页面上开启问题。 可以在后续步骤中找到我们的 GitHub 页面的链接。

收集自定义遥测数据

本部分介绍如何从应用程序收集自定义遥测数据。

根据语言和信号类型,可以通过不同的方式收集自定义遥测数据,包括:

  • 开放遥测 API
  • 特定于语言的日志记录/指标库
  • Application Insights 经典 API

下表显示了目前支持的自定义遥测类型:

语言 自定义事件 自定义指标 依赖项 异常 页面视图 请求 跟踪
ASP.NET Core
   开放遥测 API
    ILogger 应用程序接口
   AI 经典 API
爪哇岛
   开放遥测 API
   Logback,Log4j,JUL
   Micrometer 指标
   AI 经典 API
Node.js
   开放遥测 API
Python
   开放遥测 API
   Python 日志记录模块
   事件扩展

注意

Application Insights Java 3.x 和 Application Insights Node.js 3.x 从 Application Insights 经典 API 收集遥测数据。 此行为简化了升级,并暂时支持自定义遥测,直到 OpenTelemetry API 包含所有自定义遥测类型。

添加自定义指标

在此上下文中,自定义指标术语是指手动检测代码以收集超出 OpenTelemetry Instrumentation 库自动收集的额外指标。

OpenTelemetry API 提供六种指标“检测”来涵盖各种指标方案,在指标资源管理器中可视化指标时,需要选取正确的“聚合类型”。 使用 OpenTelemetry 指标 API 发送指标和使用检测库时,此要求是正确的。

下表显示了每个 OpenTelemetry 指标检测的建议聚合类型

OpenTelemetry 检测 Azure Monitor 聚合类型
计数器 总和
异步计数器 总和
直方图 Min、Max、Average、Sum 和 Count
异步仪表 平均值
UpDownCounter 总和
异步 UpDownCounter 总和

警告

在大多数情况下,其他聚合类型没有意义。

OpenTelemetry 规范介绍了这些检测,并提供了何时可以使用每种检测的示例。

提示

直方图是最通用的,也与 Application Insights GetMetric Classic API 最接近。 Azure Monitor 目前将直方图检测合并到五种受支持的聚合类型中,并且正在研发对百分位数的支持。 虽然通用性较低,但其他 OpenTelemetry 仪器对应用程序性能的影响较小。

直方图示例

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;

public class Program {

    public static void main(String[] args) {
        Meter meter = GlobalOpenTelemetry.getMeter("OTEL.AzureMonitor.Demo");
        DoubleHistogram histogram = meter.histogramBuilder("histogram").build();
        histogram.record(1.0);
        histogram.record(100.0);
        histogram.record(30.0);
    }
}

计数器示例

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;

public class Program {

    public static void main(String[] args) {
        Meter meter = GlobalOpenTelemetry.getMeter("OTEL.AzureMonitor.Demo");

        LongCounter myFruitCounter = meter
                .counterBuilder("MyFruitCounter")
                .build();

        myFruitCounter.add(1, Attributes.of(AttributeKey.stringKey("name"), "apple", AttributeKey.stringKey("color"), "red"));
        myFruitCounter.add(2, Attributes.of(AttributeKey.stringKey("name"), "lemon", AttributeKey.stringKey("color"), "yellow"));
        myFruitCounter.add(1, Attributes.of(AttributeKey.stringKey("name"), "lemon", AttributeKey.stringKey("color"), "yellow"));
        myFruitCounter.add(2, Attributes.of(AttributeKey.stringKey("name"), "apple", AttributeKey.stringKey("color"), "green"));
        myFruitCounter.add(5, Attributes.of(AttributeKey.stringKey("name"), "apple", AttributeKey.stringKey("color"), "red"));
        myFruitCounter.add(4, Attributes.of(AttributeKey.stringKey("name"), "lemon", AttributeKey.stringKey("color"), "yellow"));
    }
}

仪表示例

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;

public class Program {

    public static void main(String[] args) {
        Meter meter = GlobalOpenTelemetry.getMeter("OTEL.AzureMonitor.Demo");

        meter.gaugeBuilder("gauge")
                .buildWithCallback(
                        observableMeasurement -> {
                            double randomNumber = Math.floor(Math.random() * 100);
                            observableMeasurement.record(randomNumber, Attributes.of(AttributeKey.stringKey("testKey"), "testValue"));
                        });
    }
}

添加自定义异常

选择检测库会自动报告 Application Insights 的异常。 但是,你可能想要手动报告检测库报告的异常之外的异常。 例如,通常不会报告代码捕获的异常。 你可能希望报告这些异常,以便在相关体验中引起注意,包括故障部分和端到端事务视图。

可以使用 opentelemetry-api 来更新跨度的状态并记录异常。

  1. opentelemetry-api-1.0.0.jar(或更高版本)添加到应用程序:

    <dependency>
      <groupId>io.opentelemetry</groupId>
      <artifactId>opentelemetry-api</artifactId>
      <version>1.0.0</version>
    </dependency>
    
  2. 将状态设置为 error,并记录代码中的异常:

     import io.opentelemetry.api.trace.Span;
     import io.opentelemetry.api.trace.StatusCode;
    
     Span span = Span.current();
     span.setStatus(StatusCode.ERROR, "errorMessage");
     span.recordException(e);
    

添加自定义范围

你可能想要在两种情况下添加自定义跨度。 首先,当存在检测库尚未收集的依赖项请求时。 其次,当你希望在端到端事务视图上将应用程序进程建模为跨度时。

  • 使用 OpenTelemetry 注释

    添加自己的范围的最简单方法是使用 OpenTelemetry 的 @WithSpan 注释。

    范围会填充 Application Insights 中的 requestsdependencies 表。

    1. opentelemetry-instrumentation-annotations-1.32.0.jar(或更高版本)添加到应用程序:

      <dependency>
          <groupId>io.opentelemetry.instrumentation</groupId>
          <artifactId>opentelemetry-instrumentation-annotations</artifactId>
          <version>1.32.0</version>
      </dependency>
      
    2. 每次执行你的方法时,使用 @WithSpan 注释发出一个跨度:

      import io.opentelemetry.instrumentation.annotations.WithSpan;
      
      @WithSpan(value = "your span name")
      public void yourMethod() {
      }
      

    默认情况下,跨度会显示在 dependencies 表中,依赖项类型为 InProc

    对于表示自动检测未捕获的后台作业的方法,我们建议将 kind = SpanKind.SERVER 特性应用于 @WithSpan 批注,以确保它们会显示在 Application Insights requests 表中。

  • 使用 OpenTelemetry API

    如果上述 OpenTelemetry @WithSpan 注释不能满足你的需求,可使用 OpenTelemetry API 添加范围。

    1. opentelemetry-api-1.0.0.jar(或更高版本)添加到应用程序:

      <dependency>
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-api</artifactId>
          <version>1.0.0</version>
      </dependency>
      
    2. 使用 GlobalOpenTelemetry 类创建 Tracer

      import io.opentelemetry.api.GlobalOpenTelemetry;
      import io.opentelemetry.api.trace.Tracer;
      
      static final Tracer tracer = GlobalOpenTelemetry.getTracer("com.example");
      
    3. 创建一个范围,使其成为当前范围,然后结束该范围:

      Span span = tracer.spanBuilder("my first span").startSpan();
      try (Scope ignored = span.makeCurrent()) {
          // do stuff within the context of this 
      } catch (Throwable t) {
          span.recordException(t);
      } finally {
          span.end();
      }
      

发送自定义事件

本部分提供有关检测应用程序以捕获和发送自定义事件的指导。

若要使用 Java 代理发送 customEvent,请在 OpenTelemetry 日志记录上设置 "microsoft.custom_event.name" 属性。

根据是否使用 Application Insights Java 代理或自动配置 SDK,获取 OpenTelemetry 记录器的方式会略有不同。 以下示例进一步介绍了此详细信息。

对于 Application Insights Java 代理:

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.logs.Severity;
Logger logger = GlobalOpenTelemetry.get().getLogsBridge().get("opentelemetry-logger");

logger.logRecordBuilder() 
	  .setAttribute(AttributeKey.stringKey("microsoft.custom_event.name"),"test-event-name") 
      .setSeverity(Severity.INFO)
      .emit();

对于自动配置 SDK:

import com.azure.monitor.opentelemetry.autoconfigure.AzureMonitorAutoConfigure;
import com.azure.monitor.opentelemetry.autoconfigure.AzureMonitorAutoConfigureOptions;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder;
AutoConfiguredOpenTelemetrySdkBuilder sdkBuilder = AutoConfiguredOpenTelemetrySdk.builder();
AzureMonitorAutoConfigureOptions options = new AzureMonitorAutoConfigureOptions();
options.connectionString("<your connection string>");
     
AzureMonitorAutoConfigure.customize(sdkBuilder, options);
OpenTelemetry openTelemetry = sdkBuilder.build().getOpenTelemetrySdk();
      
Logger logger = openTelemetry.getLogsBridge().get("opentelemetry-logger");
logger.logRecordBuilder() 
	  .setAttribute(AttributeKey.stringKey("microsoft.custom_event.name"),"test-event-name") 
      .setSeverity(Severity.INFO)
      .emit();

若要可靠地发出自定义事件,请直接使用 OpenTelemetry API。 某些日志记录框架不支持追加或分析自定义事件属性。

修改遥测

本部分介绍如何修改遥测。

添加范围属性

这些属性可能包括向遥测添加自定义属性。 还可以使用属性来设置 Application Insights 架构中的可选字段,如客户端 IP。

将自定义属性添加到范围

你添加到范围的任何属性都将导出为自定义属性。 它们将填充请求、依赖项、跟踪或异常表中的 customDimensions 字段。

可以使用 opentelemetry-api 向范围中添加属性。

添加一个或多个范围属性会填充 customDimensionsrequestsdependenciestraces 表中的 exceptions 字段。

  1. opentelemetry-api-1.0.0.jar(或更高版本)添加到应用程序:

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
        <version>1.0.0</version>
    </dependency>
    
  2. 在代码中添加自定义维度:

    import io.opentelemetry.api.trace.Span;
    import io.opentelemetry.api.common.AttributeKey;
    
    AttributeKey attributeKey = AttributeKey.stringKey("mycustomdimension");
    Span.current().setAttribute(attributeKey, "myvalue1");
    

设置用户 IP

可以通过设置范围的属性来填充请求的 client_IP 字段。 Application Insights 使用 IP 地址生成用户位置属性,然后默认放弃它

Java 会自动填充此字段。

设置用户 ID 或经过身份验证的用户 ID

可以使用以下指导填充请求的 user_Iduser_AuthenticatedId 字段。 用户 ID 是匿名用户标识符。 经过身份验证的用户 ID 是已知的用户标识符。

重要

在设置经过身份验证的用户 ID 之前,请参考适用的隐私法律。

填充 user IDrequestsdependencies 表中的 exceptions 字段。

  1. opentelemetry-api-1.0.0.jar(或更高版本)添加到应用程序:

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
        <version>1.0.0</version>
    </dependency>
    
  2. 在代码中设置 user_Id

    import io.opentelemetry.api.trace.Span;
    
    Span.current().setAttribute("enduser.id", "myuser");
    

添加日志属性

会自动检测 Logback、Log4j 和 java.util.logging。 可通过以下方式将自定义维度附加到日志:

获取跟踪 ID 或范围 ID

可以使用以下步骤获取当前处于活动状态的范围的 Trace IDSpan ID

可以使用 opentelemetry-api 获取跟踪 ID 或范围 ID。

  1. opentelemetry-api-1.0.0.jar(或更高版本)添加到应用程序:

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
        <version>1.0.0</version>
    </dependency>
    
  2. 在代码中获取请求跟踪 ID 和范围 ID:

    import io.opentelemetry.api.trace.Span;
    
    Span span = Span.current();
    String traceId = span.getSpanContext().getTraceId();
    String spanId = span.getSpanContext().getSpanId();
    

后续步骤