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

文章插图
二、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异常被赋予了正确的参数名 。

文章插图
三、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 。

文章插图
四、”魔法”的背后其实这四个Attribute背后并没有什么魔法,“语法糖”而已 。对于Invoker的三个方法(InvokeAsync、FooAsync和BarAsync)针对StartNewActivity扩展方法的调用 。虽然我们并没有指定任何参数 , 但是编译器在编译后会帮助我们将参数补齐 , 完整的代码如下所示 。
using System.Diagnostics;using System.Threading.Tasks;public class Invoker{ private readonly ActivitySource _activitySource; public Invoker(ActivitySource activitySource) { _activitySource = Guard.ArgumentNotNull(activitySource, "activitySource"); } public async Task InvokeAsync() { using (_activitySource.StartNewActivity(ActivityKind.Internal, "InvokeAsync", "D:\\Projects\\App\\App\\Program.cs", 40)) { await Task.Delay(100); await FooAsync(); } } private async Task FooAsync() { using (_activitySource.StartNewActivity(ActivityKind.Internal, "FooAsync", "D:\\Projects\\App\\App\\Program.cs", 49)) { await Task.Delay(100); await BarAsync(); } } private Task BarAsync() { using (_activitySource.StartNewActivity(ActivityKind.Internal, "BarAsync", "D:\\Projects\\App\\App\\Program.cs", 58)) { return Task.Delay(100); } }}
推荐阅读
- 去越南必买的十件东西,值得买的越南特产排名,越买越心动
- 剑南春52度有几种版本_剑南春分几个档次
- 野三坡六大特色景区 野三坡景区攻略
- 在Tomcat中启用虚拟线程特性
- 【Java8新特性】- Lambda表达式
- 特工17v18.5版本怎么玩
- 小米11有几个版本_小米11有哪些版本
- 哈利波特:魔法觉醒万圣节卡南瓜拍照怎么做
- 【Java8新特性】- 接口中默认方法修饰为普通方法
- 痔疮用哪些药物?那些年我用过的痔疮膏 今天给大家推荐几个我用过的痔疮药。