通过服务总线消息传递进行分布式跟踪和关联Distributed tracing and correlation through Service Bus messaging

微服务开发中的常见问题之一是通过处理流程所涉及到的所有服务跟踪来自客户端的操作的能力。One of the common problems in microservices development is the ability to trace operation from a client through all the services that are involved in processing. 此能力对于进行调试、性能分析、A/B 测试和其他典型诊断方案相当有用。It's useful for debugging, performance analysis, A/B testing, and other typical diagnostics scenarios. 此问题的一个组成部分是跟踪工作的逻辑片段。One part of this problem is tracking logical pieces of work. 这包括消息处理结果和延迟,以及外部依赖项调用。It includes message processing result and latency and external dependency calls. 另一个组成部分是这些诊断事件在进程边界外部的关联。Another part is correlation of these diagnostics events beyond process boundaries.

当生成者通过队列发送消息时,此活动通常发生在其他某个逻辑操作的范围内,并由其他客户端或服务启动。When a producer sends a message through a queue, it typically happens in the scope of some other logical operation, initiated by some other client or service. 当使用者收到消息时,会继续相同的操作。The same operation is continued by consumer once it receives a message. 生成者与使用者(以及其他处理该操作的服务)也许会发出遥测事件,以跟踪操作流和结果。Both producer and consumer (and other services that process the operation), presumably emit telemetry events to trace the operation flow and result. 若要将此类事件相关联并以端到端的方式跟踪操作,报告遥测数据的每个服务必须为每个事件提供跟踪上下文戳记。In order to correlate such events and trace operation end-to-end, each service that reports telemetry has to stamp every event with a trace context.

世纪互联 Azure 服务总线消息传递已定义生成者与使用者应该用来传递此类跟踪上下文的有效负载属性。21Vianet Azure Service Bus messaging has defined payload properties that producers and consumers should use to pass such trace context. 该协议基于 HTTP 关联协议The protocol is based on the HTTP Correlation protocol.

属性名称Property Name 说明Description
Diagnostic-IdDiagnostic-Id 生成者针对队列发出的外部调用的唯一标识符。Unique identifier of an external call from producer to the queue. 请参阅 HTTP 协议中的 Request-Id 了解事实依据、注意事项和格式Refer to Request-Id in HTTP protocol for the rationale, considerations, and format
Correlation-ContextCorrelation-Context 操作上下文,将传播到操作处理流程涉及到的所有服务。Operation context, which is propagated across all services involved in operation processing. 有关详细信息,请参阅 HTTP 协议中的 Correlation-ContextFor more information, see Correlation-Context in HTTP protocol

服务总线 .NET 客户端自动跟踪Service Bus .NET Client autotracing

从版本 3.0.0 开始,适用于 .NET 的 Microsoft Azure 服务总线客户端提供可由跟踪系统或客户端代码片段挂接的跟踪检测点。Starting with version 3.0.0 Microsoft Azure ServiceBus Client for .NET provides tracing instrumentation points that can be hooked by tracing systems, or piece of client code. 使用检测可以从客户端跟踪对服务总线消息传递服务发出的所有调用。The instrumentation allows tracking all calls to the Service Bus messaging service from client side. 如果消息处理是通过消息处理程序模式完成的,则还会检测消息处理If message processing is done with the message handler pattern, message processing is also instrumented

使用 Azure Application Insights 进行跟踪Tracking with Azure Application Insights

Microsoft Application Insights 提供丰富的性能监视功能,包括自动请求和依赖项跟踪。Microsoft Application Insights provides rich performance monitoring capabilities including automagical request and dependency tracking.

请根据项目类型安装 Application Insights SDK:Depending on your project type, install Application Insights SDK:

如果使用消息处理程序模式来处理消息,则无需执行其他操作,系统会自动跟踪由服务所完成的所有服务总线调用,并将其与其他遥测项关联。If you use message handler pattern to process messages, you are done: all Service Bus calls done by your service are automatically tracked and correlated with other telemetry items. 否则,请参考以下示例手动进行消息处理跟踪。Otherwise refer to the following example for manual message processing tracking.

跟踪消息处理Trace message processing

private readonly TelemetryClient telemetryClient;

async Task ProcessAsync(Message message)
{
    var activity = message.ExtractActivity();

    // If you are using Microsoft.ApplicationInsights package version 2.6-beta or higher, you should call StartOperation<RequestTelemetry>(activity) instead
    using (var operation = telemetryClient.StartOperation<RequestTelemetry>("Process", activity.RootId, activity.ParentId))
    {
        telemetryClient.TrackTrace("Received message");
        try 
        {
           // process message
        }
        catch (Exception ex)
        {
             telemetryClient.TrackException(ex);
             operation.Telemetry.Success = false;
             throw;
        }

        telemetryClient.TrackTrace("Done");
   }
}

在本示例中,系统针对每个已处理的消息报告 RequestTelemetry,并提供时间戳、持续时间和结果(成功)。In this example, RequestTelemetry is reported for each processed message, having a timestamp, duration, and result (success). 遥测功能也会提供一组关联属性。The telemetry also has a set of correlation properties. 在消息处理期间报告的嵌套跟踪和异常也带有关联属性的戳记,代表它们是 RequestTelemetry 的“子级”。Nested traces and exceptions reported during message processing are also stamped with correlation properties representing them as 'children' of the RequestTelemetry.

如果在消息处理期间对支持的外部组件发出调用,则会自动跟踪和关联这些调用。In case you make calls to supported external components during message processing, they are also automatically tracked and correlated. 请参阅使用 Application Insights .NET SDK 跟踪自定义操作来了解手动跟踪和关联。Refer to Track custom operations with Application Insights .NET SDK for manual tracking and correlation.

如果除了 Application Insights SDK 之外,还运行了任何外部代码,则在查看 Application Insights 日志时,会看到更长的持续时间。If you are running any external code in addition to the Application Insights SDK, expect to see longer duration when viewing Application Insights logs.

Application Insights 日志中更长的持续时间

这并不意味着接收消息时存在延迟。It doesn't mean that there was a delay in receiving the message. 在这种情况下,消息已经被接收,因为消息以参数的形式传递给 SDK 代码。In this scenario, the message has already been received since the message is passed in as a parameter to the SDK code. 而且,App Insights 日志(进程)中的“名称”标记指示消息正在由外部事件处理代码处理 。And, the name tag in the App Insights logs (Process) indicates that the message is now being processed by your external event processing code. 此问题与 Azure 无关。This issue is not Azure-related. 相反,这些指标指示的是外部代码的效率,前提是已从服务总线接收到消息。Instead, these metrics refer to the efficiency of your external code given that the message has already been received from Service Bus. 请参阅 GitHub 上的此文件,了解从服务总线接收到消息后,生成和分配“进程”标记的位置。See this file on GitHub to see where the Process tag is generated and assigned once the message has been received from Service Bus.

在没有跟踪系统的情况下进行跟踪Tracking without tracing system

如果跟踪系统不支持自动服务总线调用跟踪,可以考虑将此类支持添加到跟踪系统或应用程序中。In case your tracing system does not support automatic Service Bus calls tracking you may be looking into adding such support into a tracing system or into your application. 本部分介绍服务总线 .NET 客户端发送的诊断事件。This section describes diagnostics events sent by Service Bus .NET client.

使用 .NET 跟踪基元 System.Diagnostics.ActivitySystem.Diagnostics.DiagnosticSource 检测服务总线 .NET 客户端。Service Bus .NET Client is instrumented using .NET tracing primitives System.Diagnostics.Activity and System.Diagnostics.DiagnosticSource.

Activity 充当跟踪上下文,而 DiagnosticSource 是通知机制。Activity serves as a trace context while DiagnosticSource is a notification mechanism.

如果没有针对 DiagnosticSource 事件的侦听器,检测将会关闭,以避免产生检测成本。If there is no listener for the DiagnosticSource events, instrumentation is off, keeping zero instrumentation costs. DiagnosticSource 将所有控制权授予侦听器:DiagnosticSource gives all control to the listener:

  • 侦听器控制要侦听的源和事件listener controls which sources and events to listen to
  • 侦听器控制事件速率和采样listener controls event rate and sampling
  • 事件连同某个有效负载一起发送,该有效负载可提供完整上下文,使你能够在事件发生期间访问和修改 Message 对象events are sent with a payload that provides full context so you can access and modify Message object during the event

在继续实现之前,请先通过 DiagnosticSource 用户指南熟悉相关流程。Familiarize yourself with DiagnosticSource User Guide before proceeding with implementation.

让我们在 ASP.NET Core 应用中针对服务总线事件创建一个侦听器,以通过 Microsoft.Extension.Logger 写入日志。Let's create a listener for Service Bus events in ASP.NET Core app that writes logs with Microsoft.Extension.Logger. 该侦听器使用 System.Reactive.Core 库来订阅 DiagnosticSource(不使用它也能轻松订阅 DiagnosticSource)It uses System.Reactive.Core library to subscribe to DiagnosticSource (it's also easy to subscribe to DiagnosticSource without it)

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory, IApplicationLifetime applicationLifetime)
{
    // configuration...

    var serviceBusLogger = factory.CreateLogger("Microsoft.Azure.ServiceBus");

    IDisposable innerSubscription = null;
    IDisposable outerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener)
    {
        // subscribe to the Service Bus DiagnosticSource
        if (listener.Name == "Microsoft.Azure.ServiceBus")
        {
            // receive event from Service Bus DiagnosticSource
            innerSubscription = listener.Subscribe(delegate (KeyValuePair<string, object> evnt)
            {
                // Log operation details once it's done
                if (evnt.Key.EndsWith("Stop"))
                {
                    Activity currentActivity = Activity.Current;
                    TaskStatus status = (TaskStatus)evnt.Value.GetProperty("Status");
                    serviceBusLogger.LogInformation($"Operation {currentActivity.OperationName} is finished, Duration={currentActivity.Duration}, Status={status}, Id={currentActivity.Id}, StartTime={currentActivity.StartTimeUtc}");
                }
            });
        }
    });

    applicationLifetime.ApplicationStopping.Register(() =>
    {
        outerSubscription?.Dispose();
        innerSubscription?.Dispose();
    });
}

在此示例中,侦听器记录每个服务总线操作的持续时间、结果、唯一标识符和开始时间。In this example, listener logs duration, result, unique identifier, and start time for each Service Bus operation.

事件Events

对于每个操作,将发送两个事件:“Start”和“Stop”。For every operation, two events are sent: 'Start' and 'Stop'. 你很有可能只对“Stop”事件感兴趣。Most probably, you are only interested in 'Stop' events. 这些事件提供操作的结果,并以 Activity 属性的形式提供开始时间和持续时间。They provide the result of operation, as well as start time and duration as Activity properties.

每个事件有效负载为侦听器提供操作上下文,并复制 API 传入参数和返回值。Event payload provides a listener with the context of the operation, it replicates API incoming parameters and return value. “Stop”事件有效负载具有“Start”事件有效负载的所有属性,因此可以完全忽略“Start”事件。'Stop' event payload has all the properties of 'Start' event payload, so you can ignore 'Start' event completely.

此外,所有事件具有“Entity”和“Endpoint”属性,但下表中省略了这些属性All events also have 'Entity' and 'Endpoint' properties, they are omitted in below table

  • string Entity - 实体(队列、主题等)的名称string Entity - Name of the entity (queue, topic, etc.)
  • Uri Endpoint - 服务总线终结点 URLUri Endpoint - Service Bus endpoint URL

每个“Stop”事件具有表示 TaskStatus 异步操作已完成的 Status 属性,为便于阅读,下表中也省略了该属性。Each 'Stop' event has Status property with TaskStatus async operation was completed with, that is also omitted in the following table for simplicity.

下面是检测操作的完整列表:Here is the full list of instrumented operations:

操作名称Operation Name 跟踪的 APITracked API 特定的有效负载属性Specific Payload Properties
Microsoft.Azure.ServiceBus.SendMicrosoft.Azure.ServiceBus.Send MessageSender.SendAsyncMessageSender.SendAsync IList<Message> Messages - 正在发送的消息的列表IList<Message> Messages - List of messages being sent
Microsoft.Azure.ServiceBus.ScheduleMessageMicrosoft.Azure.ServiceBus.ScheduleMessage MessageSender.ScheduleMessageAsyncMessageSender.ScheduleMessageAsync Message Message - 正在处理的消息Message Message - Message being processed
DateTimeOffset ScheduleEnqueueTimeUtc - 计划消息偏移DateTimeOffset ScheduleEnqueueTimeUtc - Scheduled message offset
long SequenceNumber - 计划消息的序号(“Stop”事件负载)long SequenceNumber - Sequence number of scheduled message ('Stop' event payload)
Microsoft.Azure.ServiceBus.CancelMicrosoft.Azure.ServiceBus.Cancel MessageSender.CancelScheduledMessageAsyncMessageSender.CancelScheduledMessageAsync long SequenceNumber - 要取消的消息序号long SequenceNumber - Sequence number of te message to be canceled
Microsoft.Azure.ServiceBus.ReceiveMicrosoft.Azure.ServiceBus.Receive MessageReceiver.ReceiveAsyncMessageReceiver.ReceiveAsync int RequestedMessageCount - 可接收的消息数上限。int RequestedMessageCount - The maximum number of messages that could be received.
IList<Message> Messages - 已接收的消息列表(“Stop”事件负载)IList<Message> Messages - List of received messages ('Stop' event payload)
Microsoft.Azure.ServiceBus.PeekMicrosoft.Azure.ServiceBus.Peek MessageReceiver.PeekAsyncMessageReceiver.PeekAsync int FromSequenceNumber - 浏览消息批的起点。int FromSequenceNumber - The starting point from which to browse a batch of messages.
int RequestedMessageCount - 要检索的消息数目。int RequestedMessageCount - The number of messages to retrieve.
IList<Message> Messages - 已接收的消息列表(“Stop”事件负载)IList<Message> Messages - List of received messages ('Stop' event payload)
Microsoft.Azure.ServiceBus.ReceiveDeferredMicrosoft.Azure.ServiceBus.ReceiveDeferred MessageReceiver.ReceiveDeferredMessageAsyncMessageReceiver.ReceiveDeferredMessageAsync IEnumerable<long> SequenceNumbers - 包含要接收的序号的列表。IEnumerable<long> SequenceNumbers - The list containing the sequence numbers to receive.
IList<Message> Messages - 已接收的消息列表(“Stop”事件负载)IList<Message> Messages - List of received messages ('Stop' event payload)
Microsoft.Azure.ServiceBus.CompleteMicrosoft.Azure.ServiceBus.Complete MessageReceiver.CompleteAsyncMessageReceiver.CompleteAsync IList<string> LockTokens - 包含要完成的相应消息的锁定标记的列表。IList<string> LockTokens - The list containing the lock tokens of the corresponding messages to complete.
Microsoft.Azure.ServiceBus.AbandonMicrosoft.Azure.ServiceBus.Abandon MessageReceiver.AbandonAsyncMessageReceiver.AbandonAsync string LockToken - 要丢弃的相应消息的锁定标记。string LockToken - The lock token of the corresponding message to abandon.
Microsoft.Azure.ServiceBus.DeferMicrosoft.Azure.ServiceBus.Defer MessageReceiver.DeferAsyncMessageReceiver.DeferAsync string LockToken - 要延迟的相应消息的锁定标记。string LockToken - The lock token of the corresponding message to defer.
Microsoft.Azure.ServiceBus.DeadLetterMicrosoft.Azure.ServiceBus.DeadLetter MessageReceiver.DeadLetterAsyncMessageReceiver.DeadLetterAsync string LockToken - 要加入死信队列的相应消息的锁定标记。string LockToken - The lock token of the corresponding message to dead letter.
Microsoft.Azure.ServiceBus.RenewLockMicrosoft.Azure.ServiceBus.RenewLock MessageReceiver.RenewLockAsyncMessageReceiver.RenewLockAsync string LockToken - 要续订锁定的相应消息的锁定标记。string LockToken - The lock token of the corresponding message to renew lock on.
DateTime LockedUntilUtc - 以 UTC 格式显示的新锁定标记过期日期和时间。DateTime LockedUntilUtc - New lock token expiry date and time in UTC format. (“Stop”事件有效负载)('Stop' event payload)
Microsoft.Azure.ServiceBus.ProcessMicrosoft.Azure.ServiceBus.Process IReceiverClient.RegisterMessageHandler 中提供的消息处理程序 Lambda 函数Message Handler lambda function provided in IReceiverClient.RegisterMessageHandler Message Message - 正在处理的消息。Message Message - Message being processed.
Microsoft.Azure.ServiceBus.ProcessSessionMicrosoft.Azure.ServiceBus.ProcessSession IQueueClient.RegisterSessionHandler 中提供的消息会话处理程序 lambda 函数Message Session Handler lambda function provided in IQueueClient.RegisterSessionHandler Message Message - 正在处理的消息。Message Message - Message being processed.
IMessageSession Session - 正在处理的会话IMessageSession Session - Session being processed
Microsoft.Azure.ServiceBus.AddRuleMicrosoft.Azure.ServiceBus.AddRule SubscriptionClient.AddRuleAsyncSubscriptionClient.AddRuleAsync RuleDescription Rule - 提供要添加的规则的规则说明。RuleDescription Rule - The rule description that provides the rule to add.
Microsoft.Azure.ServiceBus.RemoveRuleMicrosoft.Azure.ServiceBus.RemoveRule SubscriptionClient.RemoveRuleAsyncSubscriptionClient.RemoveRuleAsync string RuleName - 要删除的规则的名称。string RuleName - Name of the rule to remove.
Microsoft.Azure.ServiceBus.GetRulesMicrosoft.Azure.ServiceBus.GetRules SubscriptionClient.GetRulesAsyncSubscriptionClient.GetRulesAsync IEnumerable<RuleDescription> Rules - 与订阅关联的所有规则。IEnumerable<RuleDescription> Rules - All rules associated with the subscription. (仅限“Stop”有效负载)('Stop' payload only)
Microsoft.Azure.ServiceBus.AcceptMessageSessionMicrosoft.Azure.ServiceBus.AcceptMessageSession ISessionClient.AcceptMessageSessionAsyncISessionClient.AcceptMessageSessionAsync string SessionId - 消息中显示的 sessionId。string SessionId - The sessionId present in the messages.
Microsoft.Azure.ServiceBus.GetSessionStateMicrosoft.Azure.ServiceBus.GetSessionState IMessageSession.GetStateAsyncIMessageSession.GetStateAsync string SessionId - 消息中显示的 sessionId。string SessionId - The sessionId present in the messages.
byte [] State - 会话状态(“Stop”事件负载)byte [] State - Session state ('Stop' event payload)
Microsoft.Azure.ServiceBus.SetSessionStateMicrosoft.Azure.ServiceBus.SetSessionState IMessageSession.SetStateAsyncIMessageSession.SetStateAsync string SessionId - 消息中显示的 sessionId。string SessionId - The sessionId present in the messages.
byte [] State - 会话状态byte [] State - Session state
Microsoft.Azure.ServiceBus.RenewSessionLockMicrosoft.Azure.ServiceBus.RenewSessionLock IMessageSession.RenewSessionLockAsyncIMessageSession.RenewSessionLockAsync string SessionId - 消息中显示的 sessionId。string SessionId - The sessionId present in the messages.
Microsoft.Azure.ServiceBus.ExceptionMicrosoft.Azure.ServiceBus.Exception 任何已检测的 APIany instrumented API Exception Exception - 异常实例Exception Exception - Exception instance

在每个事件中,可以访问保存当前操作上下文的 Activity.CurrentIn every event, you can access Activity.Current that holds current operation context.

记录其他属性Logging additional properties

Activity.Current 提供当前操作及其父级的详细上下文。Activity.Current provides detailed context of current operation and its parents. 有关更多详细信息,请参阅 Activity 文档For more information, see Activity documentation for more details. 服务总线检测在 Activity.Current.Tags 中提供其他信息 - 这些信息包含 MessageIdSessionId(如果已提供)。Service Bus instrumentation provides additional information in the Activity.Current.Tags - they hold MessageId and SessionId whenever they are available.

跟踪“Receive”、“Peek”和“ReceiveDeferred”事件的活动还可能带有 RelatedTo 标记。Activities that track 'Receive', 'Peek' and 'ReceiveDeferred' event also may have RelatedTo tag. 该标记包含作为结果收到的消息的 Diagnostic-Id 相异列表。It holds distinct list of Diagnostic-Id(s) of messages that were received as a result. 此类操作可能导致收到多个不相关的消息。Such operation may result in several unrelated messages to be received. 此外,当操作启动时,Diagnostic-Id 未知,因此,只能使用此标记将“Receive”操作关联到“Process”操作。Also, the Diagnostic-Id is not known when operation starts, so 'Receive' operations could be correlated to 'Process' operations using this Tag only. 此标记在分析性能问题时相当有用,可以检查接收消息所花费的时间。It's useful when analyzing performance issues to check how long it took to receive the message.

记录标记的有效方法是循环访问它们,将标记添加到上述示例后,结果如下所示:Efficient way to log Tags is to iterate over them, so adding Tags to the preceding example looks like

Activity currentActivity = Activity.Current;
TaskStatus status = (TaskStatus)evnt.Value.GetProperty("Status");

var tagsList = new StringBuilder();
foreach (var tags in currentActivity.Tags)
{
    tagsList.Append($", "{tags.Key}={tags.Value}");
}

serviceBusLogger.LogInformation($"{currentActivity.OperationName} is finished, Duration={currentActivity.Duration}, Status={status}, Id={currentActivity.Id}, StartTime={currentActivity.StartTimeUtc}{tagsList}");

筛选和采样Filtering and sampling

在某些情况下,我们可能只需记录一部分事件,以减少性能开销或存储耗用量。In some cases, it's desirable to log only part of the events to reduce performance overhead or storage consumption. 可以只记录“Stop”事件(如上述示例所示),或者对特定百分比的事件采样。You could log 'Stop' events only (as in preceding example) or sample percentage of the events. 使用 DiagnosticSource 可以通过 IsEnabled 谓词实现此目的。DiagnosticSource provide way to achieve it with IsEnabled predicate. 有关详细信息,请参阅 DiagnosticSource 中基于上下文的筛选For more information, see Context-Based Filtering in DiagnosticSource.

可以针对单个操作调用 IsEnabled 多次,以将性能影响降到最低。IsEnabled may be called multiple times for a single operation to minimize performance impact.

按以下顺序调用 IsEnabledIsEnabled is called in following sequence:

  1. IsEnabled(<OperationName>, string entity, null),例如 IsEnabled("Microsoft.Azure.ServiceBus.Send", "MyQueue1")IsEnabled(<OperationName>, string entity, null) for example, IsEnabled("Microsoft.Azure.ServiceBus.Send", "MyQueue1"). 请注意,末尾没有“Start”或“Stop”。Note there is no 'Start' or 'Stop' at the end. 使用此语句可以筛选出特定的操作或队列。Use it to filter out particular operations or queues. 如果回调返回 false,则表示未发送操作的事件If callback returns false, events for the operation are not sent

    • 对于“Process”和“ProcessSession”操作,还会收到 IsEnabled(<OperationName>, string entity, Activity activity) 回调。For the 'Process' and 'ProcessSession' operations, you also receive IsEnabled(<OperationName>, string entity, Activity activity) callback. 使用此回调可根据 activity.Id 或 Tags 属性筛选事件。Use it to filter events based on activity.Id or Tags properties.
  2. IsEnabled(<OperationName>.Start),例如 IsEnabled("Microsoft.Azure.ServiceBus.Send.Start")IsEnabled(<OperationName>.Start) for example, IsEnabled("Microsoft.Azure.ServiceBus.Send.Start"). 检查是否应激发“Start”事件。Checks whether 'Start' event should be fired. 结果只影响“Start”事件,但其他检测不依赖于它。The result only affects 'Start' event, but further instrumentation does not depend on it.

“Stop”事件没有 IsEnabledThere is no IsEnabled for 'Stop' event.

如果某个操作结果为异常,则会调用 IsEnabled("Microsoft.Azure.ServiceBus.Exception")If some operation result is exception, IsEnabled("Microsoft.Azure.ServiceBus.Exception") is called. 只能订阅“Exception”事件并阻止剩余的检测。You could only subscribe to 'Exception' events and prevent the rest of the instrumentation. 在此情况下,仍需处理此类异常。In this case, you still have to handle such exceptions. 由于其他检测已禁用,因此跟踪上下文不应会连同消息一起从使用者流向生成者。Since other instrumentation is disabled, you should not expect trace context to flow with the messages from consumer to producer.

也可以使用 IsEnabled 来实现采样策略。You can use IsEnabled also implement sampling strategies. 基于 Activity.IdActivity.RootId 的采样可确保在所有尝试都获取一致的采样结果(前提是采样内容由跟踪系统或你自己的代码传播)。Sampling based on the Activity.Id or Activity.RootId ensures consistent sampling across all tires (as long as it is propagated by tracing system or by your own code).

如果同一个源存在多个 DiagnosticSource 侦听器,只需其中一个侦听器接受事件便已足够,因此无法保证调用 IsEnabledIn presence of multiple DiagnosticSource listeners for the same source, it's enough for just one listener to accept the event, so IsEnabled is not guaranteed to be called,

后续步骤Next steps