端对端跟踪关联的活动跟踪和传播
本主题说明如何使用活动跟踪和传播来帮助调试。
活动跟踪概述
Windows Communication Foundation (WCF) 提供的检测功能可用于对应用程序进行故障排除而无需使用调试器。WCF 诊断功能具有四个组件:事件处理、跟踪、活动跟踪和消息日志记录。
诊断功能 | 目标用户 | 用途 |
---|---|---|
事件处理 |
IT 专业人员 |
查看系统中发生的严重错误 |
跟踪 |
IT 专业人员和开发人员 |
以不同的详细级别查看系统中的正面和负面事件,以便诊断问题或验证适当的行为。 |
消息日志记录 |
IT 专业人员和开发人员 |
查看通过网络和/或调度到用户代码的实际消息的日志,以便诊断问题或验证适当的行为。 |
活动跟踪(跟踪功能的子集) |
IT 专业人员和开发人员 |
利用跟踪关联以找出错误的根本原因。 |
本主题重点说明活动跟踪,它可为用户提供跟踪关联以帮助用户诊断错误的根本原因。WCF 提供三种关联机制:活动、传播和传输。
活动
活动是帮助用户缩小故障范围的处理单位。在同一活动中发生的错误直接相关。例如,消息解密失败可导致操作失败。对操作和消息解密失败的跟踪都出现在同一活动中,显示解密错误和请求错误之间的直接关联。WCF 为处理应用程序提供了预定义活动。您还可以用编程方式定义活动,以便对用户跟踪进行分组。
若要在运行时发出活动跟踪,请对 System.ServiceModel 或其他自定义跟踪源使用 ActivityTracing 设置,如下面的配置代码所示。
<source name="System.ServiceModel" switchValue="Verbose,ActivityTracing">
若要了解有关所使用的配置元素和属性的更多信息,请参见配置跟踪主题。
您可以对任何跟踪源(包括 WCF 或用户定义的跟踪源)启用活动跟踪。
在任何 WCF 处理开始前,可以在用户代码中定义活动(使用 System.Diagnostics)。这样的活动称为“环境活动”。
可以在Service Trace Viewer Tool中查看活动及其效用。此工具根据活动来处理跟踪并将其分类。如果启用了 ActivityTracing,则还可以看到跟踪传输。跟踪传输指示不同的活动如何互相关联。您可以看到某个特定活动导致另外一个活动启动。例如,消息请求可启动安全握手以获取安全对话令牌。有关更多信息以及服务跟踪查看器工具的图形视图,请参见Service Trace Viewer Tool和使用服务跟踪查看器查看相关跟踪和进行故障诊断。
传播
传播可向用户提供应用程序终结点之间相同处理单元的错误跟踪的直接关联,如请求。在同一处理单元的不同终结点(例如请求)发出的错误即使跨应用程序域,也将分组到同一活动中。这是通过活动 ID 在消息头中的传播实现的。因此,如果客户端由于服务器内部错误而超时,则这两个错误都会显示在同一活动中以便直接关联。
为此,可以按前面示例所示使用 ActivityTracing 设置。此外,还要设置 System.ServiceModel 跟踪源的 propagateActivity 属性。
<source name="System.ServiceModel" switchValue="Verbose,ActivityTracing" propagateActivity=”true” >
活动传播是一项可配置的功能,此功能允许 WCF 向出站消息添加标头,其中包括 TLS 上的活动 ID。通过在服务器端的后续跟踪上包括此 ID,可以关联客户端和服务器活动。
传输
活动之间的传输表示终结点内相关活动中的事件之间的因果关系。当两个活动之间存在控制流(例如方法调用跨越活动边界)时,这两个活动将与传输相关。在 WCF 中,当通过服务传入字节时,“侦听”活动将转换为“接收字节”活动,消息对象将在“接收字节”活动中创建。
若要发出传输跟踪,可以按前面示例所示对跟踪源使用 ActivityTracing 设置。
有关端到端跟踪方案及其各自的活动和跟踪设计的列表,请参见端到端跟踪方案。
E2E 跟踪模型
本节提供活动及其边界、终结点之间通过传播和终结点内通过传输实现的活动关联以及活动分析的定义。本节还提供所有活动跟踪类型的摘要,并显示如何把这些类型放在一起以形成有效的跟踪序列。
活动
定义和范围
活动具有本地和全局标识符。它在设计时定义,并表示一个逻辑工作单元。所发出的具有相同活动标识符的跟踪直接相关,它们都属于同一个活动。因为一个活动可能跨越终结点边界(例如请求),因此为活动定义两个范围。
- 全局范围,限于每个应用程序。在此范围中,活动由其 128 位全局唯一活动标识符 gAId 进行标识。gAid 是跨越终结点传播的内容。
- 本地范围,限于每个跟踪源和进程。在此范围中,活动由它的 gAId 以及发出活动跟踪的跟踪源名称和进程 ID 标识。这三部分内容构成了本地活动 ID,即 lAId。lAId 用于定义活动的(本地)边界。
活动在本地由三个一组的 lAId={ProcessId, TraceSourceName, gAId} 标识给跟踪源,在应用程序中,活动由其 gAId 进行全局标识。
架构
跟踪可以使用任何架构发出,并可跨越 Microsoft 平台,一种常用的架构称为“E2E”(表示“端对端”)架构。此架构包括一个在跟踪源和进程内唯一的 128 位标识符(活动标识符)、跟踪源名称和进程 ID。活动标识符 (AID) 允许对发出的跟踪进行关联。这是因为发出的具有相同 AID 的跟踪是直接相关的,它们都是同一活动的组成部分。在托管代码中,XmlWriterTraceListener 可在 E2E 架构中发出跟踪。
开发人员可以通过使用线程本地存储 (TLS) 上的 Guid 设置 ActivityId 属性来设置随跟踪发出的 AID。下面的示例对此进行说明。
// set the current Activity ID to a new GUID.
CorrelationManager.ActivityId = Guid.NewGuid();
使用跟踪源发出跟踪时,设置 TLS 中的 gAId 非常简单,如下面的示例所示。
TraceSource traceSource = new TraceSource("myTraceSource");
traceSource.TraceEvent(TraceEventType.Warning, eventId, "Information");
发出的跟踪将包含当前 TLS 中的 gAId、作为参数传递给跟踪源构造函数的跟踪源名称和当前进程的 ID。
在服务跟踪查看器中关联活动
服务跟踪查看器工具提供以下两种活动视图。
- **“列表”**视图,其中使用 gAId 在跟踪源和进程之间直接关联跟踪。来自不同跟踪源(例如客户端和服务端),但具有相同 gAId 的跟踪将分组在同一活动中。因此,如果在服务上发生一个错误,然后该错误又导致客户端上发生错误,那么这两个错误都将出现在该工具的同一活动视图中。
- **“图形”**视图,其中,活动按进程分组。在此视图中,gAId 相同但 lAId 不同(因为处在不同的进程中)的客户端和服务的跟踪将显示在不同的活动中。为了关联不同进程中具有相同 gAId 的活动,该工具会显示相关活动的消息流。
活动生存期
- 开始:指示活动开始。“开始”跟踪提供开始一个新的处理里程碑的记录。对于给定进程中的给定跟踪源,它始终包含一个新的 AID,除非活动 ID 在使用同一跟踪源名称(例如 System.ServiceModel)的各个终结点之间进行传播并在同一个进程中运行。开始新活动的示例包括创建新的处理线程或进入新的公共方法。
- 停止:指示活动结束。“停止”跟踪提供结束现有处理里程碑的记录。对于给定进程中的给定跟踪源,它始终包含一个新的 AID,除非活动 ID 在使用同一跟踪源名称(例如 System.ServiceModel)的各个终结点之间进行传播并在同一个进程中运行。停止活动的示例包括终止处理线程或退出以“开始”跟踪表示开始的方法。
- 挂起:指示活动的处理已挂起。“挂起”跟踪包含其处理预期在以后恢复的现有 AID。在挂起和恢复事件之间不会从当前跟踪源发出具有此 AID 的跟踪。示例包括调用外部库函数或等待资源(如 I/O 完成端口)时暂停一个活动。
- 恢复:指示继续处理活动。“恢复”跟踪包含从当前跟踪源发出的最后一个跟踪为“挂起”跟踪的现有 AID。示例包括从对外部库函数的调用返回或发出让资源(如 I/O 完成端口)恢复处理的信号。
- 转换:因为一些活动是由其他活动导致的或与其他活动相关,所以可以通过“转换”跟踪将活动相互关联。转换跟踪记录了活动之间的定向关系。
活动跟踪使用指南
以下是使用 ActivityTracing 跟踪(开始、停止、挂起、恢复和转换)的指南。
- 跟踪是一个定向循环图,而不是树。如前面的示例所指示的那样,可以对生成活动的活动恢复控制。
- 活动表示一个处理边界,该边界对于系统管理员可能很有意义,或者可以用来实现可支持性。
- 无论是在客户端上还是在服务器上,每个 WCF 方法都由开始一个新活动、然后(工作完成后)结束该新活动并返回到环境活动来限定。
- 长时间运行(正在运行)的活动(例如,侦听连接或等待消息)由相应的开始/停止标记表示。
- 由接收或处理消息触发的活动由跟踪边界来表示。
- 活动表示的是活动,而不是对象。应该将活动理解为“这是在 .. . (发出有意义的跟踪)时发生的”。
使用开始和停止来标记活动边界
从最严格的角度来看,在发出的跟踪中首次使用 lAId 即表明活动开始,在发出的跟踪中最后一次使用 lAId 即表明活动结束。
活动的生存期从在发出的跟踪中首次使用 lAId 时开始,到在发出的跟踪中最后一次使用 lAId 时结束。System.Diagnostics 提供一组预定义的跟踪类型,它使用两种跟踪类型,即开始和停止,以显式标记活动边界。
开始 – 活动的开始
“开始”跟踪提供开始一个新的处理里程碑的记录。对于给定进程中的给定跟踪源,它始终包含一个新的 lAId,除非活动 ID 在使用同一跟踪源名称(例如 System.ServiceModel)的各个终结点之间进行传播并在同一个进程中运行,或者同一个 gAId 在相同的终结点之间传播多次(例如,WCF 设置安全性活动)。建议仅当请求为同步顺序请求时采取后一种做法。在这种情况下,可以看见每个终结点的 gAId 都相同的开始(和停止)跟踪。有关传播的更多详细信息,请参见“传播”一节。WCF 通过开始跟踪来设置活动名称。
开始新活动的示例包括创建新的处理线程或进入新的公共方法。
停止 – 活动的结束
“停止”跟踪提供结束现有处理里程碑的记录。对于给定进程中的给定跟踪源,它始终包含一个不会再次出现的 lAId,除非活动 ID 在使用同一跟踪源名称(例如 System.ServiceModel)的各个终结点之间进行传播并在同一个进程中运行,或者同一个 gAId 在相同的终结点之间传播多次(WCF 设置安全性活动)。建议仅当请求为同步顺序请求时采取后一种做法。在这种情况下,可以看见每个终结点的 gAId 都相同的开始(和停止)跟踪。有关传播的更多详细信息,请参见“传播”一节。
- 停止活动的示例包括终止处理线程或退出以“开始”跟踪表示开始的方法。
如果为了获得相关性而在各个终结点之间传播该 gAId,则每个跟踪源都会有一个具有该 gAId 的开始和停止跟踪。
使用开始和停止跟踪
“开始”和“停止”跟踪并不是关联所必需的。然而,它们可以帮助提高性能、分析和验证活动范围。
使用这些类型时,工具可以优化跟踪日志的导航以查找同一活动的直接相关事件,或者如果工具追踪传输跟踪,可查找相关活动中的事件。例如,当工具遇到开始/停止跟踪时,将停止分析给定活动的日志。
工具使用 lAId 作为活动标识符,但可以使用 gAId 来标识已加载文件之间的相关活动。
这些跟踪类型也可以用于分析。在开始和停止标记之间消耗的资源表示活动的全部时间,包括所包含的逻辑活动时间。减去“挂起”和“恢复”跟踪之间的时间间隔就得到实际活动时间。
停止跟踪对于验证已实现活动的范围特别有用。如果某些处理跟踪出现在停止跟踪之后,而不是出现在给定活动之内,这可能暗示代码有缺陷。
如果为跟踪源设置了 ActivityTracing 属性,则会发出开始和停止跟踪。
使用传播关联终结点之间的活动
活动为在本地与同一处理单元相关的跟踪提供到跟踪源的直接关联。在 WCF 中,为了在终结点之间关联属于同一逻辑处理单元(例如请求)的跟踪,将传播 gAId。关联后,不同终结点中具有相同 gAId 的活动中的跟踪可以合并在同一个活动中。
如果下列所有条件都适用,则活动 M 的 gAId 会传播到活动 N。
- N 是为 M 创建的
- N 已知 M 的 gAId
- N 的 gAId 与 M 的 gAId 相同。
gAId 通过 ActivityId 消息头传播,如下面的 XML 架构所示。
<xsd:element name=”ActivityId” type=”integer” minOccurs=”0”>
<xsd:attribute name=”CorrelationId” type=”integer” minOccurs=”0”/>
</xsd:element>
下面是一个消息头示例。
<MessageLogTraceRecord>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://Microsoft.ServiceModel.Samples/ICalculator/Subtract
</a:Action>
<a:MessageID>urn:uuid:f0091eae-d339-4c7e-9408-ece34602f1ce
</a:MessageID>
<ActivityId CorrelationId="f94c6af1-7d5d-4295-b693-4670a8a0ce34"
xmlns="https://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">
17f59a29-b435-4a15-bf7b-642ffc40eac8
</ActivityId>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous
</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">net.tcp://localhost/servicemodelsamples/service</a:To>
</s:Header>
<s:Body>
<Subtract xmlns="http://Microsoft.ServiceModel.Samples">
<n1>145</n1>
<n2>76.54</n2>
</Subtract>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
传播和活动边界
在 System.ServiceModel 中,如果用户在两端的配置中都设置了 propagateActivity=true,则活动 ID 将会在终结点之间传播。
当活动 ID 在终结点之间传播时,消息接收方使用该(传播的)活动 ID 发出开始跟踪和停止跟踪。因此,每个跟踪源的 gAId 都具有开始和停止跟踪。如果终结点位于同一个进程中,且使用相同的跟踪源名称,则会创建多个具有相同 lAId(相同 gAId、相同跟踪源和相同进程)的开始和停止跟踪。
同步
通过在消息头中设置 ActivityId 的 Correlation 属性,可以在计算机之间启用终结点跟踪的同步。
为了同步不同计算机上运行的各终结点之间的事件,向在消息中传播的 ActivityId 标头中添加了一个 CorrelationId。工具可使用此 ID 来同步计算机之间具有时钟差的事件。具体地说,服务跟踪查看器工具可使用此 ID 显示终结点之间的消息流。
使用传输关联终结点内的活动
活动 M 和活动 N 之间存在控制流时,会从 M 向 N 发出传输跟踪。例如,由于存在跨越活动边界的方法调用,因而 N 会为 M 执行某些工作。
N 可能已存在或已创建。N 由 M 生成,此时 N 是新活动,为 M 执行某些工作。
从 M 到 N 的传输之后可能不会紧跟一个从 N 到 M 的反向传输。这是因为 M 可能会在 N 中生成一些工作,并且不会跟踪何时 N 将完成这些工作。实际上,M 可以在 N 完成其任务之前终止。此情况在“打开 ServiceHost”活动 (M) 中发生,该活动生成“侦听器”活动 (N),然后终止。
从 N 传回 M 意味着 N 已完成与 M 相关的工作。
N 可以继续执行与 M 无关的其他处理,例如继续从不同登录活动接收登录请求 (M) 的现有身份验证器活动 (N)。
活动之间的传输表示终结点内相关活动中的事件之间的因果关系。用户可以通过活动和传输找出错误的根本原因。例如,如果分别在组件 M 和 N 的活动 M 和 N 之间来回传输,并且在传回 M 之后 N 中发生崩溃,则可以得出结论,发生错误很可能是由于 N 将数据传回了 M。
活动 M 与 N 之间并不一定存在嵌套关系。发生这种情况可能有两个原因。首先,虽然活动 M 启动了 N,但 M 并不监视 N 中执行的实际处理。其次,N 可能已经存在。
下面列出了两个传输示例。
- 在创建服务主机时,构造函数将从调用代码获得控制权,或者调用代码将控制权转移给构造函数。构造函数执行完时,它将控制权返回给调用代码,或者构造函数将控制权转移回调用代码。这是嵌套关系时的情况。
- 侦听器开始处理传输数据时,它将创建一个新线程,并将相应的上下文传递到“接收字节”活动进行处理,即传递控制权和数据。该线程处理完请求后,接收字节活动不向侦听器传回任何内容。在这种情况下,有传入的新线程活动而没有传出的新线程活动。这两个活动相关,但不是嵌套关系。
活动传输序列
格式正确的活动传输序列包含下列步骤。
- 开始一个新活动(包括选择新的 gAId)。
- 发出从当前活动 ID 到该新 gAId 的传输跟踪
- 在 TLS 中设置新 ID
- 发出开始跟踪以指示新活动的开始。
- 返回到原始活动包括下列步骤:
- 发出到原始 gAId 的传输跟踪
- 发出停止跟踪以指示新活动的结束
- 将 TLS 设置为旧的 gAId。
下面的代码示例演示如何执行此操作。此示例假定,在传输到新活动时将进行阻止调用,该调用包括挂起/继续跟踪。
// 0. Create a trace source
TraceSource ts = new TraceSource(“myTS”);
// 1. remember existing (“ambient”) activity for clean up
Guid oldGuid = Trace.CorrelationManager.ActivityId;
// this will be our new activity
Guid newGuid = Guid.NewGuid();
// 2. call transfer, indicating that we are switching to the new AID
ts.TraceTransfer(667, "Transferring.", newGuid);
// 3. Suspend the current activity.
ts.TraceEvent(TraceEventType.Suspend, 667, "Suspend: Activity " + i-1);
// 4. set the new AID in TLS
Trace.CorrelationManager.ActivityId = newGuid;
// 5. Emit the start trace
ts.TraceEvent(TraceEventType.Start, 667, "Boundary: Activity " + i);
// trace something
ts.TraceEvent(TraceEventType.Information, 667, "Hello from activity " + i);
// Perform Work
// some work.
// Return
ts.TraceEvent(TraceEventType.Information, 667, "Work complete on activity " + i);
// 6. Emit the transfer returning to the original activity
ts.TraceTransfer(667, "Transferring Back.", oldGuid);
// 7. Emit the End trace
ts.TraceEvent(TraceEventType.Stop, 667, "Boundary: Activity " + i);
// 8. Change the tls variable to the original AID
Trace.CorrelationManager.ActivityId = oldGuid;
// 9. Resume the old activity
ts.TraceEvent(TraceEventType.Resume, 667, "Resume: Activity " + i-1);
跟踪类型摘要
有关不同跟踪级别(严重、错误、警告、信息和详细)的详细信息以及 ActivityTracing 标志(切换跟踪边界和活动传输事件的输出)的说明,请参见源级别(可能为英文网页)。请参见
TraceEventType(可能为英文网页)了解可以从 System.Diagnostics 发出的跟踪的类型。下表列出了最重要的跟踪类型。
跟踪类型 | 说明 |
---|---|
严重 |
致命错误或应用程序崩溃。 |
错误 |
可恢复的错误。 |
警告 |
信息性消息。 |
信息 |
非严重问题。 |
详细 |
调试跟踪。 |
开始 |
开始处理逻辑单元。 |
挂起 |
挂起逻辑单元处理。 |
恢复 |
继续逻辑单元处理。 |
停止 |
停止处理逻辑单元。 |
传输 |
更改相关标识。 |
活动定义为上述跟踪类型的组合。
下面的正则表达式用于定义本地(跟踪源)范围内的理想活动,
R = Start ( (Critical | Error | Warning | Information | Verbose | Transfer)* (Transfer Suspend Transfer Resume)* )* Stop
这意味着活动必须满足以下条件。
- 它必须分别由开始跟踪和停止跟踪来启动和停止
- 它必须刚好在挂起跟踪或恢复跟踪之前具有传输跟踪
- 如果有挂起和恢复跟踪,则在挂起跟踪和恢复跟踪之间不能有任何跟踪
- 只要符合上述条件,就可以有任意多个严重/错误/警告/信息/详细/传输跟踪
下面的正则表达式用于定义全局范围内的理想活动,
R+
R 是本地范围内活动的正则表达式。这将转换为,
[R+ = Start ( (Critical | Error | Warning | Information | Verbose | Transfer)* (Transfer Suspend Transfer Resume)* )* Stop]+
另请参见
概念
配置跟踪
使用服务跟踪查看器查看相关跟踪和进行故障诊断
端到端跟踪方案