跳到主要内容

分布式追踪中的上下文传播

分布式追踪允许您的企业应用程序追踪消息从发布者经过事件网格到接收应用程序的流动路径。有关详细概述,请参阅分布式追踪。关于版本要求的信息,请参阅分布式追踪版本兼容性

关于如何为代码添加分布式追踪的工具,请参阅以下内容:


为 JCSMP 添加分布式追踪工具

手动工具化涉及对您的企业应用程序的源代码进行更改,并允许您将额外的上下文(如行李和追踪状态)注入消息中。上下文传播使调试和优化应用程序变得更加容易。关于 Solace 事件消息中上下文传播的更多信息,请参阅分布式追踪上下文传播。以下示例展示了如何使用 OpenTelemetry API 创建追踪范围(span)。

上下文传播如何在 JCSMP PubSub+ API 中启用分布式追踪

在客户端应用程序中,您可以使用 OpenTelemetry API 创建一个追踪范围(span),其中包含分布式系统中操作的元数据。此范围与一个上下文相关联,上下文中包含一个唯一的 TraceID。接下来,当您使用 PubSub+ 消息生产者发布消息时,Solace OTel 集成包会将包含 TraceID 的上下文注入消息中。随着消息通过事件代理并被消费应用程序接收,每个步骤都会生成范围,并且这些范围中包含原始消息上下文中的相同 TraceID。当发布或消费应用程序中的每个范围关闭时,Java OpenTelemetry API 会将其发送到 OpenTelemetry 收集器,后者收集、处理并将范围导出到后端应用程序。后端应用程序使用这些范围的唯一 TraceID 将它们关联起来,创建一个追踪(trace),这是一个端到端的快照,详细记录了消息如何通过分布式系统流动。如果您不使用上下文传播,后端应用程序将无法使用唯一的 TraceID 将范围关联起来,从而难以追踪消息在分布式系统中的流动路径。

依赖项

要启用分布式追踪的上下文传播,您必须首先将 Solace PubSub+ OpenTelemetry 集成库添加到您的应用程序中。关于 OpenTelemetry 版本兼容性,请参阅分布式追踪版本兼容性。添加库后,您将能够访问以下两个接口:

  • SolaceJCSMPTextMapSetter — 此接口允许 TextMapPropagator 将上下文注入消息中。
  • SolaceJCSMPTextMapGetter — 此接口允许 TextMapPropagator 从消息中提取上下文。

本指南假设您熟悉 OpenTelemetry 类实例的配置。有关配置 OpenTelemetry 对象的说明,请参阅 OpenTelemetry 文档中的Java 手动工具化

在消息发布时生成发送范围

您的发布应用程序可以生成一个发送范围,并将其导出到 OpenTelemetry 收集器。以下步骤展示了如何将上下文注入消息并为发布的消息生成发送范围:

  1. 创建一个新的范围,并使用 setAttribute() 方法设置范围属性。接下来,将当前上下文设置为此范围的父级。使用 startSpan() 方法启动范围:

    final Span sendSpan = tracer
    .spanBuilder("mySolacePublisherApp" + " " + MessagingOperationValues.PROCESS)
    .setSpanKind(SpanKind.CLIENT)
    // 发布到非临时主题端点
    .setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND, MessagingDestinationKindValues.TOPIC)
    .setAttribute(SemanticAttributes.MESSAGING_TEMP_DESTINATION, false)
    // 根据需要设置更多属性
    //.setAttribute(...)
    //.setAttribute(...)
    .setParent(Context.current()) // 将当前上下文设置为父级范围
    .startSpan();
  2. 将步骤 1 中创建的范围(在此示例中为 sendSpan)设置为新的当前上下文。接下来,将当前上下文注入您的消息,然后发布消息。调用范围的 end() 方法以导出范围数据:

    try (Scope scope = sendSpan.makeCurrent()) {
    final SolaceJCSMPTextMapSetter setter = new SolaceJCSMPTextMapSetter();
    final TextMapPropagator propagator = openTelemetry.getPropagators().getTextMapPropagator();
    // 将包含发送范围的当前上下文注入消息
    propagator.inject(Context.current(), message, setter);
    // 将消息发布到指定主题
    messageProducer.send(message, messageDestination);
    } catch (Exception e) {
    sendSpan.recordException(e); // 范围可以记录异常
    sendSpan.setStatus(StatusCode.ERROR, e.getMessage()); // 将范围状态设置为 ERROR/FAILED
    } finally {
    sendSpan.end(); // 调用 end() 后导出范围数据
    }

在消息接收时生成接收范围

您的消费应用程序可以生成一个接收范围,然后将其导出到 OpenTelemetry 收集器。以下步骤展示了如何从接收到的消息中提取追踪上下文并生成接收范围:

  1. 使用 SolaceJCSMPTextMapGetter 从接收到的消息中提取任何现有的上下文:

    final SolaceJCSMPTextMapGetter getter = new SolaceJCSMPTextMapGetter();
    final Context extractedContext = openTelemetry.getPropagators().getTextMapPropagator()
    .extract(Context.current(), message, getter);
  2. 使用 makeCurrent() 方法将提取的上下文设置为当前上下文。接下来,创建一个子范围(在此示例中为 receiveSpan),并将提取的上下文设置为该子范围的父级。使用 startSpan() 方法启动范围:

    try (Scope scope = extractedContext.makeCurrent()) {
    // 创建一个子范围,并将提取的/当前上下文设置为该范围的父级
    final Span receiveSpan = tracer
    .spanBuilder("mySolaceReceiverApp" + " " + MessagingOperationValues.RECEIVE)
    .setSpanKind(SpanKind.CLIENT)
    // 如果消息是在非临时队列端点上接收的
    .setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND,
    MessagingDestinationKindValues.QUEUE)
    .setAttribute(SemanticAttributes.MESSAGING_TEMP_DESTINATION, false)
    // 根据需要设置更多属性
    //.setAttribute(...)
    //.setAttribute(...)
    // 如果存在,则创建与消息发布者应用程序范围的父子关系
    .setParent(extractedContext)
    // 启动范围
    .startSpan();
    // try-catch 在下一步继续...
  3. 接收并处理消息,然后调用接收范围的 end() 方法以导出范围数据:

    try {
    // 在回调函数中对消息进行处理
    messageProcessor.accept(receivedMessage);
    } catch (Exception e) {
    receiveSpan.recordException(e); // 范围可以记录异常
    receiveSpan.setStatus(StatusCode.ERROR, e.getMessage()); // 将范围状态设置为 ERROR
    } finally {
    receiveSpan.end(); // 调用 span.end() 时导出范围数据
    }

为.NET添加分布式追踪工具

手动工具化涉及对您的企业应用程序的源代码进行更改,并允许您将额外的上下文(如行李和追踪状态)注入消息中。上下文传播使调试和优化应用程序变得更加容易。关于 Solace 事件消息中上下文传播的更多信息,请参阅分布式追踪上下文传播。以下示例展示了如何使用 OpenTelemetry API 创建追踪范围。

上下文传播如何在 .NET PubSub+ API 中启用分布式追踪

在客户端应用程序中,您可以使用 OpenTelemetry API 创建一个追踪范围,其中包含分布式系统中操作的元数据。此范围与一个上下文相关联,上下文中包含一个唯一的 TraceID。接下来,当您使用 PubSub+ 消息生产者发布消息时,Solace OTel 集成包会将包含 TraceID 的上下文注入消息中。随着消息通过事件代理并被消费应用程序接收,每个步骤都会生成范围,并且这些范围中包含原始消息上下文中的相同 TraceID。当发布或消费应用程序中的每个范围关闭时,.NET OpenTelemetry API 会将其发送到 OpenTelemetry 收集器,后者收集、处理并将范围导出到后端应用程序。后端应用程序使用这些范围的唯一 TraceID 将它们关联起来,创建一个追踪,这是一个端到端的快照,详细记录了消息如何通过分布式系统流动。如果您不使用上下文传播,后端应用程序将无法使用唯一的 TraceID 将范围关联起来,从而难以追踪消息在分布式系统中的流动路径。

依赖项

要启用分布式追踪的上下文传播,您必须首先将 Solace .NET OpenTelemetry 集成 NuGet 包添加到您的应用程序中。关于 OpenTelemetry 版本兼容性,请参阅分布式追踪版本兼容性。Solace .NET OpenTelemetry 集成 NuGet 包包含 SolaceMessageCarrier 类,它为您的应用程序提供了以下内容:

  • SolaceMessageCarrier.Setter
    • 与 OpenTelemetry API 的 TraceContextPropagator 一起使用,将上下文注入 IMessage
    • 与 OpenTelemetry API 的 BaggagePropagator 一起使用,将行李注入 IMessage
  • SolaceMessageCarrier.Getter
    • 与 OpenTelemetry API 的 TraceContextPropagator 一起使用,从 IMessage 中提取上下文。
    • 与 OpenTelemetry API 的 BaggagePropagator 一起使用,从 IMessage 中提取行李。

本指南假设您熟悉 OpenTelemetry 类实例的配置。有关配置 OpenTelemetry 对象的说明,请参阅 OpenTelemetry 文档中的.NET 手动工具化

要在 .NET PubSub+ API 中使用上下文传播,请在您的应用程序中包含以下程序包:

using OpenTelemetry.Context.Propagation;   // 需要用于追踪传播
using OpenTelemetry; // 包含用于注入和提取行李的结构
using Solace.Messaging.Trace.Propagation; // 需要用于在 Solace API 中使用上下文传播
using SolaceSystems.Solclient.Messaging; // 需要用于使用 .NET PubSub+ API
using System.Diagnostics; // 需要用于创建活动实例

在 .NET PubSub+ API 中使用 System.Diagnostics.Activity

Microsoft 的 System.Diagnostics.Activity 类用于 PubSub+ .NET API 中的分布式追踪。此类的一个实例(activity)类似于一个范围,表示应用程序内的一个操作,例如发布或接收消息。这些活动允许后端应用程序(如 Jaeger 或 Datadog)追踪事件在分布式系统中的流动。要创建一个 activity,您必须在一个 ActivitySource 对象上调用 StartActivity(String, ActivityKind) 方法:

ActivitySource activitySource;
using var activity = activitySource.StartActivity("activityName", ActivityKind.Producer);

您还可以使用以下方法为 activity 设置可选属性,以提供有关 activity 执行的更多详细信息或自定义信息:

  • SetTag(String, Object) — 为 activity 设置自定义键值对属性,以提供更多上下文信息。

    activity?.SetTag("myKey1", "myValue1");
  • AddEvent(ActivityEvent) — 向活动的时间线添加一个事件。此方法接受一个 ActivityEvent 实例作为参数,该实例表示在 activity 执行期间发生的一个事件。以下示例展示了如何创建一个异常事件并将其添加到 activity 中:

    ActivityTagsCollection tagsCollection = new()
    {
    { "exception.message", exceptionMessage }
    };
    activity?.AddEvent(new ActivityEvent("exceptions", tags: tagsCollection));
  • SetStatus(ActivityStatusCode, String) — 设置 activity 的状态。此方法接受一个 ActivityStatusCode 实例作为参数,该实例表示活动的当前状态:

    activity?.SetStatus(ActivityStatusCode.Error, "statusErrorMessage");      // 表示操作过程中遇到错误的状态码
    activity?.SetStatus(ActivityStatusCode.OK, "statusOKMessage"); // 表示操作已验证并成功完成的状态码

有关更多信息,请参阅 Microsoft 的 .NET 文档 中的 System.Diagnostics.Activity

将上下文注入传出消息

以下步骤展示了如何将上下文注入消息并为发布的消息生成一个 activity

  1. 创建传播器实例。.NET PubSub+ API 支持追踪上下文和行李传播。如果您计划在应用程序中使用这两种传播方式,建议您使用 CompositeTextMapPropagator,它接受一个 TextMapPropagator 列表作为参数:

    List<TextMapPropagator> propagators = new List<TextMapPropagator>() { new TraceContextPropagator(), new BaggagePropagator() };
    CompositeTextMapPropagator compositeContextPropagator;
    // ...
    compositeContextPropagator = new CompositeTextMapPropagator(propagators);
  2. 创建一个 ActivitySource 实例,它允许您创建和启动 activity 对象。使用 activitySource.StartActivity() 启动 activity

    ActivitySource activitySource;
    // ...
    using var activity = activitySource.StartActivity("activityName", ActivityKind.Producer);
  3. (可选)使用 Baggage 类和 SetBaggage(key,value) 方法为消息附加行李:

    Baggage.SetBaggage("myBaggageKey", "myBaggageValue");
  4. 使用您的 compositeContextPropagator 将上下文(以及可选的行李)注入消息:

    compositeContextPropagator.Inject(new PropagationContext(activity.Context, Baggage.Current), message, SolaceMessageCarrier.Setter);
  5. 发送消息并调用 Stop() 方法以导出 activity 数据:

    session.Send(message);
    activity?.Stop();

以下步骤展示了如何从接收到的消息中提取追踪上下文并生成一个 activity

  1. 创建传播器和 ActivitySource 实例。.NET PubSub+ API 支持追踪上下文和行李传播。如果您计划在应用程序中使用这两种传播方式,建议您使用 CompositeTextMapPropagator,它接受一个 TextMapPropagator 列表作为参数:

    List<TextMapPropagator> propagators = new List<TextMapPropagator>() { new TraceContextPropagator(), new BaggagePropagator() };
    CompositeTextMapPropagator compositeContextPropagator;
    ActivitySource activitySource; // 允许您创建和启动 Activity 对象
    // ...
    compositeContextPropagator = new CompositeTextMapPropagator(propagators);
  2. 在您的接收应用程序的消息处理程序中,从接收到的消息中提取上下文和行李(如果适用)。使用提取的消息上下文创建一个新的 ActivityContext 对象(在以下示例中为 parentContext):

    private void HandleMessageEvent(object source, MessageEventArgs args)
    {
    using (IMessage message = args.Message)
    {
    if (message == null) return;
    var propagatorContext = compositeContextPropagator.Extract(default, message, SolaceMessageCarrier.Getter);
    ActivityContext parentContext = propagatorContext.ActivityContext;
    Baggage.Current = propagatorContext.Baggage;
  3. 使用 activitySource.StartActivity() 创建并启动一个消费者 activity 对象:

    using var activity = activitySource.StartActivity("activityName", ActivityKind.Consumer, parentContext);
  4. 使用 SetParentId() 方法设置父级 ID:

    activity?.SetParentId(parentContext.SpanId.ToString());
  5. 调用 Stop() 方法以导出 activity 数据:

    activity?.Stop();

为JavaScript和Node.js添加分布式追踪工具

手动工具化涉及对您的企业应用程序的源代码进行更改,并允许您将额外的上下文(如行李和追踪状态)注入消息中。上下文传播使调试和优化应用程序变得更加容易。关于 Solace 事件消息中上下文传播的更多信息,请参阅分布式追踪上下文传播。以下示例展示了如何使用 OpenTelemetry API 创建追踪范围。

理解上下文传播如何在 JavaScript 和 Node.js PubSub+ API 中启用分布式追踪

在客户端应用程序中,您可以使用 OpenTelemetry API 创建一个追踪范围,其中包含分布式系统中操作的元数据。此范围与一个上下文相关联,上下文中包含一个唯一的 TraceID。接下来,当您使用 PubSub+ 消息生产者发布消息时,Solace OTel 集成包会将包含 TraceID 的上下文注入消息中。随着消息通过事件代理并被消费应用程序接收,每个步骤都会生成范围,并且这些范围中包含原始消息上下文中的相同 TraceID。 当发布或消费应用程序中的每个范围关闭时,JavaScript OpenTelemetry API 会将其发送到 OpenTelemetry 收集器,后者收集、处理并将范围导出到后端应用程序。后端应用程序使用这些范围的唯一 TraceID 将它们关联起来,创建一个追踪,这是一个端到端的快照,详细记录了消息如何通过分布式系统流动。如果您不使用上下文传播,后端应用程序将无法使用唯一的 TraceID 将范围关联起来,从而难以追踪消息在分布式系统中的流动路径。

依赖项

要启用分布式追踪的上下文传播,您必须首先将 Solace OpenTelemetry JavaScript 集成包添加到您的应用程序中。关于 OpenTelemetry 版本兼容性,请参阅分布式追踪版本兼容性。添加库后,您将能够访问以下两个类:

  • SolaceW3CTextMapSetter
    • 与 OpenTelemetry API 的 W3CTraceContextPropagator 一起使用,将上下文注入 Message
    • 与 OpenTelemetry API 的 W3CBaggagePropagator 一起使用,将行李注入 Message
  • SolaceW3CTextMapGetter
    • 与 OpenTelemetry API 的 W3CTraceContextPropagator 一起使用,从 Message 中提取上下文。
    • 与 OpenTelemetry API 的 W3CBaggagePropagator 一起使用,从 Message 中提取行李。

本指南假设您熟悉 OpenTelemetry 类实例的配置。有关配置 OpenTelemetry 对象的说明,请参阅 OpenTelemetry 文档中的JavaScript 手动工具化

要在 PubSub+ JavaScript API 和 PubSub+ Node.js API 中使用上下文传播,请在您的应用程序中包含以下模块:

import {
SemanticAttributes,
MessagingOperationValues,
MessagingDestinationKindValues,
} from '@opentelemetry/semantic-conventions';

const opentelemetry = require('@opentelemetry/api');
const { CompositePropagator, W3CTraceContextPropagator, W3CBaggagePropagator, TraceState } = require("@opentelemetry/core");
const { BasicTracerProvider } = require("@opentelemetry/sdk-trace-base");
const { context, propagation, trace } = require("@opentelemetry/api");
const { SemanticAttributes, MessagingDestinationKindValues } = require("@opentelemetry/semantic-conventions");
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { SolaceW3CTextMapSetter, SolaceW3CTextMapGetter } = require("pubsubplus-opentelemetry-js-integration");

const api = require('@opentelemetry/api');
var solace = require('solclientjs').debug; // 支持日志记录

将上下文注入传出消息

您的发布应用程序可以生成一个发送范围,并将其导出到 OpenTelemetry 收集器。以下步骤展示了如何将上下文注入消息并为发布的消息生成一个发送范围:

  1. 创建传播器实例。JavaScript 和 Node.js API 支持追踪上下文和行李传播。如果您计划在应用程序中使用这两种传播方式,建议您使用 CompositePropagator,它接受一个传播器数组作为参数:

    const compositePropagator = new CompositePropagator({
    propagators: [
    new W3CBaggagePropagator(),
    new W3CTraceContextPropagator(),
    ],
    });
  2. 使用 W3CTraceContextPropagator() 和可选的 W3CBaggagePropagator 设置全局传播器,确保应用程序中的所有上下文传播遵循相同的规则和机制。在以下示例中,两者都包含在步骤 1 中创建的 compositePropagator 中。接下来,创建一个 BasicTracerProvider,它允许您的应用程序创建和激活追踪:

    propagation.setGlobalPropagator(compositePropagator);
    const tracerProvider = new BasicTracerProvider();
  3. 使用 register() 函数将步骤 1 中创建的 tracerProvider 注册到 OpenTelemetry API。这允许您使用追踪器实例创建范围并记录追踪数据。接下来,创建一个追踪器。传递给 getTracer() 函数的参数代表追踪器的名称,用于将追踪器与应用程序中的特定组件、服务或模块相关联:

    tracerProvider.register();
    const tracer = opentelemetry.trace.getTracer("solace-pubsub-publisher-test");
  4. 使用 OpenTelemetry API 获取当前活动的上下文,该上下文将被传播。然后,创建一个“发布范围”,并使用 startSpan() 函数启动它:

    let ctx = api.context.active();
    const span = tracer.startSpan(topicName + ' send', { kind: opentelemetry.SpanKind.CLIENT }, ctx);
  5. 使用 setAttribute() 函数设置范围属性。这允许您将额外的上下文和元数据附加到范围上。您还可以使用 setAttribute() 为范围设置一个描述性名称,以指示范围所代表的操作类型:

    span.setAttribute('attributeKey', 'attributeValue');
    span.setAttribute(SemanticAttributes.MESSAGING_OPERATION, 'send');
  6. (可选)使用 createBaggage() 函数创建行李,并通过在传播实例上调用 setBaggage() 函数将其设置到上下文中:

    const baggage = propagation.createBaggage({
    "baggageKey": {
    value: 'baggageValue',
    metadata: undefined,
    },
    });
    ctx = propagation.setBaggage(ctx, baggage);
  7. 创建一个 SolaceContextSetter 实例,并使用 W3C 上下文传播器将上下文注入 Solace 消息。发送消息并使用 setStatus() 函数设置 SpanStatusCode

    var setter = new SolaceContextSetter();
    opentelemetry.propagation.inject(ctx, solaceMessage, setter);
    try {
    solaceSession.send(solaceMessage);
    span.setStatus({
    code: opentelemetry.SpanStatusCode.OK,
    message: 'Message Sent'
    });
    } catch (error) {
    span.setStatus({
    code: opentelemetry.SpanStatusCode.ERROR,
    message: error.toString()
    });
    }
  8. 调用范围的 end() 函数以关闭它并导出数据:

    span.end();

您的消费应用程序可以生成一个处理范围,然后将其导出到 OpenTelemetry 收集器。以下步骤展示了如何从接收到的消息中提取追踪上下文并生成一个处理范围:

  1. 使用 OpenTelemetry API 创建一个追踪器:传递给 getTracer() 函数的参数代表追踪器的名称,用于将追踪器与应用程序中的特定组件、服务或模块相关联:

    const tracer = opentelemetry.trace.getTracer('solace-pubsub-receiver-test');
  2. 在您的消息事件监听器中,创建一个 SolaceContextGetter。使用 extract() 函数从接收到的消息中提取追踪和上下文信息。使用此提取的上下文为后续操作创建一个 parentContext

    messageConsumer.on(solace.MessageConsumerEventName.MESSAGE, function (message) {
    var getter = new SolaceContextGetter();
    const parentContext = propagation.extract(opentelemetry.ROOT_CONTEXT, message, getter);
  3. 创建一个新的处理范围,并将其链接到 parentContext(如果存在):

    if (!parentContext)
    var span = tracer.startSpan(MessagingOperationValues.PROCESS, { kind: opentelemetry.SpanKind.CLIENT });
    else
    var span = tracer.startSpan(MessagingOperationValues.PROCESS, { kind: opentelemetry.SpanKind.CLIENT }, parentContext);
  4. 使用 setAttribute() 函数设置范围属性。这允许您将额外的上下文和元数据附加到范围上。您还可以使用 setAttribute() 为范围设置一个描述性名称,以指示范围所代表的操作类型:

    span.setAttribute('attributeKey', 'attributeValue');
    span.setAttribute(SemanticAttributes.MESSAGING_OPERATION, 'process');
  5. (可选)使用 getBaggage() 函数从父上下文中提取行李:

    const baggage = propagation.getBaggage(parentContext);
  6. 处理消息并使用 setStatus() 函数设置 SpanStatusCode

    try {
    // 在这里处理消息
    span.setStatus({
    code: opentelemetry.SpanStatusCode.OK,
    message: 'Message processed'
    });
    message.acknowledge();
    } catch (error) {
    span.setStatus({
    code: opentelemetry.SpanStatusCode.ERROR,
    message: error.toString()
    });
    }
  7. 调用范围的 end() 函数以关闭它并导出数据:

    span.end();