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();
}
...
}