什么是分布式跟踪和遥测关联?

注意

以下文档依赖于 Application Insights 经典 API。 Application Insights 的长期计划是使用 OpenTelemetry 收集数据。 有关详细信息,请参阅为 .NET、Node.js、Python 和 Java 应用程序启用 Azure Monitor OpenTelemetry

现代云和 微服务 体系结构支持简单且可独立部署的服务,这些服务可提高可用性和吞吐量,同时降低成本。 不过,这也使总体系统更难推理和调试。 分布式跟踪提供了类似于云和微服务体系结构的调用堆栈的性能探查器,从而解决了此问题。

Azure Monitor 提供两种使用分布式跟踪数据的体验:单个事务/请求的 事务诊断 视图,和用于显示系统交互方式的 应用程序映射 视图。

Application Insights 可以单独监视每个组件,并使用分布式遥测关联以检测哪个组件导致故障或性能下降。 本文介绍了 Application Insights 使用的不同语言和平台中的数据模型、上下文传播技术、协议以及相关策略的实现。

启用分布式跟踪

要为应用程序启用分布式跟踪,请根据每个服务的编程语言向其添加正确的代理、SDK 或库。

使用自动检测或 SDK 通过 Application Insights 启用

适用于 .NET、.NET Core、Java、Node.js 和 JavaScript 的 Application Insights 代理和 SDK 都本机支持分布式跟踪。 每个 Application Insights SDK 的安装和配置说明可用于:

安装并配置适当的 Application Insights SDK 以后,系统就会通过 SDK 依赖项自动收集器自动收集常用框架、库和技术的跟踪信息。 依赖项自动收集文档中提供了受支持的技术的完整列表。

另外,任何技术都可通过在 TelemetryClient 上调用 TrackDependency 手动进行跟踪。

通过 OpenTelemetry 启用

Application Insights 现在支持通过 OpenTelemetry 进行的分布式跟踪。 OpenTelemetry 提供了一项独立于供应商的检测技术,用于向 Application Insights 发送跟踪、指标和日志。 OpenTelemetry 社区最初采用分布式跟踪。 指标和日志仍在完善中。

完整的可观测性故事包括所有三个支柱。 检查基于 Azure Monitor OpenTelemetry 的产品/服务的状态,以查看包含内容的最新状态、哪些产品/服务已正式发布,以及支持选项。

以下页面包含用于启用和配置 Microsoft 基于 OpenTelemetry 的产品/服务的语言指南。 重要的是,我们共享了每个产品/服务的可用功能和限制,以便你确定 OpenTelemetry 是否适用于你的项目。

通过 OpenCensus 启用

除了 Application Insights SDK,Application Insights 还可以通过 OpenCensus 来支持分布式跟踪。 OpenCensus 是库的单发行版,开源且不局限于供应商,可以针对服务进行指标收集和分布式跟踪。 它还允许开源社区针对 Redis、Memcached 或 MongoDB 之类的常用技术启用分布式跟踪。 Microsoft 与多个其他的监视项目和云项目合作伙伴进行 OpenCensus 协作

有关 OpenCensus for Python 的详细信息,请参阅为 Python 应用程序设置 Azure Monitor

OpenCensus 网站维护着 PythonGo 的 API 参考文档,以及有关使用 OpenCensus 的各种指南。

遥测关联的数据模型

Application Insights 定义了用于分配遥测关联的数据模型。 要将遥测与逻辑操作关联,每个遥测项都应包含名为 operation_Id 的上下文字段。 分布式跟踪中的每个遥测项都共享此标识符。 因此,即使失去单个层的遥测,也仍可关联其他组件报告的遥测。

分布式逻辑操作通常由一系列小规模操作(某个组件处理的请求)构成。 请求遥测 定义这些操作。 每个请求遥测项都具有自身的 id,用于对自身进行唯一全局标识。 与此请求关联的所有遥测项(例如跟踪和异常)应将 operation_parentId 设置为请求 id 的值。

依赖项遥测 表示每个传出操作,例如对另一个组件的 HTTP 调用。 它也定义了自身全局唯一的 id。 此依赖项调用发起的请求遥测将此 id 用作其 operation_parentId

可以结合 dependency.id 使用 operation_Idoperation_parentIdrequest.id,生成分布式逻辑操作的视图。 这些字段还定义了遥测调用的因果关系顺序。

在微服务环境中,来自组件的跟踪可能会进入不同的存储项。 每个组件可能在 Application Insights 中具有其自身的连接字符串。 为了获取逻辑操作的遥测数据,Application Insights 会查询每个存储项中的数据。

如果存储项的数目大,需要有关后续查找位置的提示。 Application Insights 数据模型定义了以下两个字段来解决此问题:request.sourcedependency.target。 第一个字段定义发起依赖项请求的组件。 第二个字段定义哪个组件返回依赖项调用的响应。

有关使用 app 查询表达式从多个不同实例进行查询的信息,请参阅 Azure Monitor 查询中的 app() 表达式

示例

接下来举例说明。 名为 Stock Prices 的应用程序使用名为 Stock 的外部 API 显示某只股票的当前市价。 Stock Prices 应用程序有一个名为 Stock 的页面,可以由客户端 Web 浏览器通过 GET /Home/Stock 打开。 该应用程序使用 HTTP 调用 GET /api/stock/value 查询 Stock API。

可以运行一个查询来分析生成的遥测数据:

(requests | union dependencies | union pageViews)
| where operation_Id == "STYz"
| project timestamp, itemType, name, id, operation_ParentId, operation_Id

在结果中,所有遥测项都共享根 operation_Id。 从该页面发出 Ajax 调用后,会将新的唯一 ID (qJSXU) 分配给依赖项遥测,并将 pageView 的 ID 用作 operation_ParentId。 接着,服务器请求将 Ajax ID 用作 operation_ParentId

itemType name ID operation_ParentId operation_Id
pageView Stock page STYz STYz
dependency GET /Home/Stock qJSXU STYz STYz
请求 GET Home/Stock KqKwlrSt9PA= qJSXU STYz
dependency GET /api/stock/value bBrf2L7mm2g= KqKwlrSt9PA= STYz

在对外部服务发出 GET /api/stock/value 调用时,需要知道该服务器的标识,以便对 dependency.target 字段进行相应的设置。 如果外部服务不支持监视,则会将 target 设置为服务的主机名。 例如 stock-prices-api.com。 但是,如果该服务通过返回预定义的 HTTP 标头来标识自身,则 target 会包含服务标识,使 Application Insights 能够通过查询该服务中的遥测数据来生成分布式跟踪。

使用 W3C TraceContext 的关联标头

Application Insights 正在过渡到 W3C Trace-Context,该协议定义:

  • traceparent:承载调用的全局唯一操作 ID 和唯一标识符。
  • tracestate:承载系统特定的跟踪上下文。

最新版本 Application Insights SDK 支持 Trace-Context 协议,但你可能需要选择启用此协议。 (保持与 Application Insights SDK 支持的之前关联协议的后向兼容性。)

关联 HTTP 协议(也称为 Request-Id)即将弃用。 此协议定义两个标头:

  • Request-Id:承载调用的全局唯一 ID。
  • Correlation-Context:承载分布式跟踪属性的名称值对集合。

Application Insights 还为关联 HTTP 协议定义了扩展。 它使用 Request-Context 名称值对来传播直接调用方或被调用方使用的属性集合。 Application Insights SDK 使用此标头设置 dependency.targetrequest.source 字段。

W3C Trace-Context 和 Application Insights 数据模型按以下方式映射:

Application Insights W3C TraceContext
RequestDependencyId parent-id
Operation_Id trace-id
Operation_ParentId 此范围的父范围的 parent-id。 如果此字段是根跨度,则必须为空。

有关详细信息,请参阅 Application Insights 遥测数据模型

启用对 .NET 应用的 W3C 分布式跟踪支持

在所有最新的 .NET Framework/.NET Core SDK 中默认启用基于 W3C TraceContext 的分布式跟踪,并后向兼容旧 Request-Id 协议。

启用对 Java 应用的 W3C 分布式跟踪支持

Java 3.0 代理

Java 3.0 代理直接支持 W3C,不需要任何其他配置。

Java SDK

  • 传入配置

    对于 Java EE 应用,请将以下代码添加到 ApplicationInsights.xml 内的 <TelemetryModules> 标记中:

    <Add type="com.microsoft.applicationinsights.web.extensibility.modules.WebRequestTrackingTelemetryModule>
       <Param name = "W3CEnabled" value ="true"/>
       <Param name ="enableW3CBackCompat" value = "true" />
    </Add>
    

    对于 Spring Boot 应用,请添加以下属性:

    • azure.application-insights.web.enable-W3C=true
    • azure.application-insights.web.enable-W3C-backcompat-mode=true
  • 传出配置

    将以下代码添加到 AI-Agent.xml:

    <Instrumentation>
      <BuiltIn enabled="true">
        <HTTP enabled="true" W3C="true" enableW3CBackCompat="true"/>
      </BuiltIn>
    </Instrumentation>
    

    注意

    默认情况下启用后向兼容性模式,并且 enableW3CBackCompat 参数是可选的, 仅在要将后向兼容性关闭时使用。

    理想情况下,当所有服务都更新为支持 W3C 协议的较新版 SDK 时,需将此模式关闭。 强烈建议你尽快迁移到这些更新的 SDK。

必须确保传入和传出配置完全相同。

启用对 Web 应用的 W3C 分布式跟踪支持

默认情况下,此功能会为 JavaScript 启用,并且当托管页域与请求发送到的域相同时(例如,托管页为 example.com,Ajax 请求发送到 example.com),会自动包含标头。 若要更改分布式跟踪模式,请使用 distributedTracingMode 配置字段。 默认情况下,会提供 AI_AND_W3C 用于向后兼容 Application Insights 检测的任何旧服务。

如果 XMLHttpRequest 或 Fetch Ajax 请求发送到其他域主机(包括子域),则默认情况下不会包括相关标头。 若要启用此功能,请将 enableCorsCorrelation 配置字段设置为 true。 如果将 enableCorsCorrelation 设置为 true,则所有 XMLHttpRequest 和 Fetch Ajax 请求都包含相关标头。 因此,如果调用的服务器上的应用程序不支持 traceparent 标头,则请求可能会失败,具体取决于浏览器/版本是否可以根据服务器将接受的标头来验证请求。 可使用 correlationHeaderExcludedDomains 配置字段 从跨组件关联标头注入中排除服务器的域。 例如,可使用 correlationHeaderExcludedDomains: ['*.auth0.com'],从发送到 Auth0 标识提供者的请求中排除关联标头。

重要

若要查看启用关联所需的所有配置,请参阅 JavaScript 关联文档

OpenCensus Python 中的遥测关联

OpenCensus Python 支持 W3C Trace-Context,无需额外配置。

有关参考,可以在此 GitHub 页面上找到 OpenCensus 数据模型。

传入请求关联

OpenCensus Python 将传入请求中的 W3C Trace-Context 标头关联到从请求本身生成的范围。 OpenCensus 会自动与以下热门 Web 应用程序框架的集成关联:Flask、Django 和 Pyramid。 只需使用正确的格式填充 W3C Trace-Context 标头,并通过请求发送即可。

浏览此示例 Flask 应用程序。 安装 Flask、OpenCensus 以及 Flask 和 Azure 的扩展。


pip install flask opencensus opencensus-ext-flask opencensus-ext-azure

需要将 Application Insights 连接字符串添加到环境变量。

APPLICATIONINSIGHTS_CONNECTION_STRING=<appinsights-connection-string>

示例 Flask 应用程序

from flask import Flask
from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.ext.flask.flask_middleware import FlaskMiddleware
from opencensus.trace.samplers import ProbabilitySampler

app = Flask(__name__)
middleware = FlaskMiddleware(
    app,
    exporter=AzureExporter(
        connection_string='<appinsights-connection-string>', # or set environment variable APPLICATION_INSIGHTS_CONNECTION_STRING
    ), 
    sampler=ProbabilitySampler(rate=1.0),
)

@app.route('/')
def hello():
    return 'Hello World!'

if __name__ == '__main__':
    app.run(host='localhost', port=8080, threaded=True)

此代码在本地计算机上运行示例 Flask 应用程序,并侦听端口 8080。 若要关联跟踪上下文,请向终结点发送一个请求。 在此示例中,可以使用 curl 命令:

curl --header "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" localhost:8080

查看 Trace-Context 标头格式,可以获得以下信息:

version: 00

trace-id: 4bf92f3577b34da6a3ce929d0e0e4736

parent-id/span-id: 00f067aa0ba902b7

trace-flags: 01

如果查看发送到 Azure Monitor 的请求条目,可以看到填充了跟踪标头信息的字段。 可以在 Azure Monitor Application Insights 资源中的“日志(分析)”下找到此数据。

屏幕截图显示“日志(分析)”中的请求遥测数据。

id 字段采用 <trace-id>.<span-id> 格式,其中的 trace-id 取自在请求中传递的跟踪标头,span-id 是针对该范围生成的 8 字节数组。

operation_ParentId 字段采用 <trace-id>.<parent-id> 格式,其中的 trace-idparent-id 取自在请求中传递的跟踪标头。

日志关联

OpenCensus Python 允许通过添加跟踪 ID、范围 ID 和采样标志进行日志记录,从而对日志进行关联。 可以通过安装 OpenCensus 日志记录集成来添加这些属性。 以下属性会添加到 Python LogRecord 对象:traceIdspanIdtraceSampled(仅适用于集成后创建的记录器)。

安装 OpenCensus 日志记录集成:

python -m pip install opencensus-ext-logging

示例应用程序

import logging

from opencensus.trace import config_integration
from opencensus.trace.samplers import AlwaysOnSampler
from opencensus.trace.tracer import Tracer

config_integration.trace_integrations(['logging'])
logging.basicConfig(format='%(asctime)s traceId=%(traceId)s spanId=%(spanId)s %(message)s')
tracer = Tracer(sampler=AlwaysOnSampler())

logger = logging.getLogger(__name__)
logger.warning('Before the span')
with tracer.span(name='hello'):
    logger.warning('In the span')
logger.warning('After the span')

运行此代码时,控制台中将输出以下内容:

2019-10-17 11:25:59,382 traceId=c54cb1d4bbbec5864bf0917c64aeacdc spanId=0000000000000000 Before the span
2019-10-17 11:25:59,384 traceId=c54cb1d4bbbec5864bf0917c64aeacdc spanId=70da28f5a4831014 In the span
2019-10-17 11:25:59,385 traceId=c54cb1d4bbbec5864bf0917c64aeacdc spanId=0000000000000000 After the span

请注意,范围中的日志消息有一个对应的 spanIdspanId 与属于名为 hello 的跨度的相同。

可以使用 AzureLogHandler 导出日志数据。 有关详细信息,请参阅为 Python 应用程序设置 Azure Monitor

我们还可以将跟踪信息从一个组件传递到另一个组件,以便进行适当关联。 例如,假设有两个组件:module1module2。 Module 1 调用 Module 2 中的函数。 若要在单次跟踪中从 module1module2 获取日志,我们可以使用以下方法:

# module1.py
import logging

from opencensus.trace import config_integration
from opencensus.trace.samplers import AlwaysOnSampler
from opencensus.trace.tracer import Tracer
from module_2 import function_1

config_integration.trace_integrations(["logging"])
logging.basicConfig(
    format="%(asctime)s traceId=%(traceId)s spanId=%(spanId)s %(message)s"
)
tracer = Tracer(sampler=AlwaysOnSampler())

logger = logging.getLogger(__name__)
logger.warning("Before the span")

with tracer.span(name="hello"):
    logger.warning("In the span")
    function_1(logger, tracer)
logger.warning("After the span")
# module_2.py
import logging

from opencensus.trace import config_integration
from opencensus.trace.samplers import AlwaysOnSampler
from opencensus.trace.tracer import Tracer

config_integration.trace_integrations(["logging"])
logging.basicConfig(
    format="%(asctime)s traceId=%(traceId)s spanId=%(spanId)s %(message)s"
)
logger = logging.getLogger(__name__)
tracer = Tracer(sampler=AlwaysOnSampler())


def function_1(logger=logger, parent_tracer=None):
    if parent_tracer is not None:
        tracer = Tracer(
            span_context=parent_tracer.span_context,
            sampler=AlwaysOnSampler(),
        )
    else:
        tracer = Tracer(sampler=AlwaysOnSampler())

    with tracer.span("function_1"):
        logger.info("In function_1")

.NET 中的遥测关联

加入应用时,默认情况下会处理相关性。 无需执行任何特殊操作。

.NET 运行时支持借助 ActivityDiagnosticSource 进行分发

Application Insights .NET SDK 使用 DiagnosticSourceActivity 收集和关联遥测数据。

Java 中的遥测关联

Java 代理 支持自动关联遥测。 对于所有在请求范围内发出的遥测(例如跟踪、异常、自定义事件),它会自动填充 operation_id。 对于通过 HTTP 进行的服务到服务调用,它还会传播关联标头(如前所述),前提是 Java SDK 代理已配置。

备注

Application Insights Java 代理自动收集 JMS、Kafka、Netty/Webflux 等的请求和依赖项。 对于 Java SDK,关联功能仅支持通过 Apache HttpClient 进行的调用。 该 SDK 不支持跨消息传送技术(例如,Kafka、RabbitMQ 和 Azure 服务总线)自动进行上下文传播。

若要收集自定义遥测,需要使用 Java 2.6 SDK 来检测应用程序。

角色名称

可能需要对组件名称在应用程序映射中的显示方式进行自定义。 为此,可执行以下操作之一来手动设置 cloud_RoleName

  • 对于 Application Insights Java,请按如下所示设置云角色名称:

    {
      "role": {
        "name": "my cloud role name"
      }
    }
    

    还可以使用环境变量 APPLICATIONINSIGHTS_ROLE_NAME 设置云角色名称。

  • 使用 Application Insights Java SDK 2.5.0 及更高版本时,可以通过将 <RoleName> 添加到 ApplicationInsights.xml 文件来指定 cloud_RoleName

    屏幕截图显示 Application Insights 概述和连接字符串。

    <?xml version="1.0" encoding="utf-8"?>
    <ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings" schemaVersion="2014-05-30">
       <ConnectionString>InstrumentationKey=00000000-0000-0000-0000-000000000000</ConnectionString>
       <RoleName>** Your role name **</RoleName>
       ...
    </ApplicationInsights>
    
  • 如果将 Spring Boot 与 Application Insights Spring Boot Starter 配合使用,则请在 application.properties 文件中为应用程序设置自定义名称:

    spring.application.name=<name-of-app>

还可以通过环境变量或系统属性设置云角色名称。 有关详细信息,请参阅配置云角色名称

后续步骤