https://mp.weixin.qq.com/s/stRWiUvq__bGEht_PZuNag
针对活动的跟踪可反映完整的调用链信息,通过《.NET Core的诊断日志[9]:针对EventSource的跟踪日志[补充]》的介绍我们知道针对TraceSource和EventSource的日志框架都提供了针对活动的跟踪的支持,基于DiagnosticSource的日志框架也不例外。在介绍 DiagnosticSource这个抽象类定义的时候,我们已经提到如下两个与活动跟踪相关的方法,其中StartActivity和StopActivity分别用来发送活动开始和结束事件。
public abstract class DiagnosticSource
{
public Activity StartActivity(Activity activity, object args);
public void StopActivity(Activity activity, object args);
...
}
从上面的代码片段可以看出,DiagnosticSource涉及的活动通过一个Activity对象来表示。一个Activity对象与一个逻辑操作进行关联,它不仅仅可以反映调用链的流转,还保存了操作开始的时间戳和耗时。
如下面的代码片段所示,Activity的Id和OperationName属性分别代表活动的唯一标识和操作名称。Activity的StartTimeUtc和Duration属性表示活动的开始时间(UTC时间)和耗时。调用链的流转信息看通过表示“父活动”的Parent属性来体现,除此之外,我们还可以利用其ParentId和RootId获取“父活动”和“根活动”的标识。
public class Activity
{
public string Id { get; }
public string OperationName { get; }
public DateTime StartTimeUtc { get; }
public TimeSpan Duration { get; }
public string RootId { get; }
public Activity Parent { get; }
public string ParentId { get; }
public Activity(string operationName);
...
}
我们可以利用Activity的静态属性Current得到以AsyncLocal<Activity>对象形式保存的当前活动。活动的开始和结束可以通过调用Start和Stop方法来完成。当我们调用Start方法的时候,Activity会被分配一个唯一标识,并将当前事件作为开始时间戳。与此同时,当前的活动会作为“父活动”赋予Parent属性,而自身将被作为当前活动赋予静态属性Current。
当我们调用Stop方法结束活动的时候,整个耗时会被计算出来并赋予Duration属性,然后将Parent属性表示的“父活动”作为当前活动。除了目前介绍的这些成员,Activity还定义为了其他若干方法和属性,篇幅所限,我们就不再这里赘言介绍了。
public class Activity
{
public static Activity Current { get; }
public Activity Start();
public void Stop();
}
我们最后来看看DiagnosticSource是如何实现活动跟踪的。从如下所示的代码片段可以看出DiagnosticSource的StartActivity和StopActivity方法并没有什么特别之处,它除了Start和Stop方法开始和结束指定的Activity对象之外,只是调用了Write方法发送了一个日志事件而已。发送日志事件的名称为指定Activity的操作名称分别加上对应后缀“.Start”和“.Stop”,所以我们需要根据此命名规则来订阅活动开始和结束事件。
public abstract class DiagnosticSource
{
public Activity StartActivity(Activity activity, object args)
{
activity.Start();
this.Write(activity.OperationName + ".Start", args);
return activity;
}
public void StopActivity(Activity activity, object args)
{
if (activity.Duration == TimeSpan.Zero)
{
activity.SetEndTime(Activity.GetUtcNow());
}
this.Write(activity.OperationName + ".Stop", args);
activity.Stop();
}
...
}