几个Caller-特性的妙用( 二 )

我们利用注册的ActivityListener在Activity终止时将Activity相关跟踪信息(操作名称、SpanId、ParentId、执行时间和Tag)打印在控制台上,具体输出如下所示 。

几个Caller-特性的妙用

文章插图
二、CallerArgumentExpressionAttributeCallerArgumentExpressionAttribute特性里利用目标参数将当前方法调用的某个参数(构造函数的参数表示该参数的名称)的表达式保存下来 。如果指定的是一个变量(或者参数),捕获到的就是变量名 。比如我们定义了如下这个用来验证参数并确保它不能为Null的ArgumentNotNull<T> 。除了第一个表示参数值的argumentValue参数,它还具有一个表示参数名的argumentName参数,抛出的ArgumentNullException异常的参数名就来源于此 。
public static class Guard{    public static T ArgumentNotNull<T>(T argumentValue, [CallerArgumentExpression("argumentValue")] string argumentName = "") where T:class    {        if (argumentValue is null) throw new ArgumentNullException(argumentName);        return argumentValue;    }}我们修改了Invoker的构造函数,并按照如下的方式添加了针对输出参数(ActivitySource对象)的验证,以避免后续抛出NullReferenceException异常 。可以看出,我们调用ArgumentNotNull方法时并没有执行表示参数名称的第二个参数 。
var invoker = new Invoker(null);public class Invoker{    private readonly ActivitySource _activitySource;    public Invoker(ActivitySource activitySource) => _activitySource = Guard.ArgumentNotNull(activitySource);   ...}如果我们按照如上的方式调用Invoker的构造函数,并将Null作为参数,此时会抛出如下的异常,可以看到抛出的ArgumentNullException异常被赋予了正确的参数名 。
几个Caller-特性的妙用

文章插图
三、CallerFilePathAttribute &CallerLineNumberAttributeCallerFilePathAttribute 和CallerLineNumberAttribute特性会将源代码的两个属性赋值给目标参数 。具体来说,前者会将当前源文件的路径绑定到目标参数,后者绑定的则是当前执行代码在源文件中的行数 。下面的代码为StartNewActivity扩展方法额外添加了两个参数,并标注了如上两个特性,我们将对应的参数值作为Tag添加到创建的Activity中 。
public static class Extensions{    public static Activity? StartNewActivity(        this ActivitySource activitySource,        ActivityKind kind = ActivityKind.Internal,        [CallerMemberName] string name = "",        [CallerFilePath] string? filePath = default,        [CallerLineNumber] int lineNumber = default)    => activitySource        .StartActivity(name: name, kind: kind)        ?.AddTag("CallerFilePath", filePath)        ?.AddTag("CallerLineNumber", lineNumber);}再次执行我们的程序,控制台上就会输出添加的两个Tag 。
【几个Caller-特性的妙用】

经验总结扩展阅读