本文是《ASP.NET Core 3 框架揭秘》8.5.4 的案例。
由于很简单,作者没有写案例。但是自己在尝试写的时候还是遇到点困难的。所有分享一下,希望能帮到别人。
我的案例是基于本书案例S812(事件日志EventSource的活动跟踪)和S815(诊断日志DiagnosticSource 强类型事件订阅)改造的。
活动跟踪我是通过Activity对象的传输实现的,不知道对不对,也希望大家指出问题。
首先是发布者的定义
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ConsoleApp6
{
public sealed class FoobarSource : DiagnosticListener
{
public FoobarSource(string name) : base(name) { }
public static FoobarSource Instance = new FoobarSource("Web");
public Activity FooStart(long timestamp)
{
var activity = new Activity("Foo");
Instance.StartActivity(activity, new { Activity = activity, Index =1, TimeStamp =timestamp });
return activity;
}
public void FooStop(Activity activity, double elapsed)
{
Instance.StopActivity(activity, new { Activity= activity,Index = 2, Elapsed = elapsed });
}
public Activity BarStart(long timestamp)
{
var activity = new Activity("Bar");
Instance.StartActivity(activity, new { Activity = activity, Index = 3, TimeStamp = timestamp });
return activity;
}
public void BarStop(Activity activity, double elapsed)
{
Instance.StopActivity(activity, new { Activity = activity, Index = 4, Elapsed = elapsed });
}
public Activity BazStart(long timestamp)
{
var activity = new Activity("Baz");
Instance.StartActivity(activity, new { Activity = activity, Index = 5, TimeStamp = timestamp });
return activity;
}
public void BazStop(Activity activity, double elapsed)
{
Instance.StopActivity(activity, new { Activity = activity, Index = 6, Elapsed = elapsed });
}
public Activity GuxStart(long timestamp)
{
var activity = new Activity("Gux");
Instance.StartActivity(activity, new { Activity = activity, Index = 7, TimeStamp = timestamp });
return activity;
}
public void GuxStop(Activity activity, double elapsed)
{
Instance.StopActivity(activity, new { Activity = activity, Index = 8, Elapsed = elapsed });
}
}
}
还是Foo,Bar,Bza,Gux的start,stop几个函数。
start时,自己创建了一个Activity,并指定operationName。调用StartActivity并返回这个Activity。
stop时,需要以参数方式传入Activity.其实就是对应的start时创建的Activity。调用StopActivity。
根据书中8.5.4对DiagnosticSource的StartActivity方法和StopActivity方法的说明。两个方法内部除了调用Start方法和Stop方法开始与结束指定活动对象之外,只是调用Write方法发送了一个日志事件。发送日志事件的名称为指定Activity对象的操作名称分别加上对应后缀“.Start”和“.Stop”,所以需要根据此命名规则来订阅活动的开始事件和结束事件。
public abstract class DiagnosticSource
{
public Activity StartActivity(Activity activity,object args)
{
activity.Start();
Write(activity.OperationName + ".Start", args);
return activity;
}
public void StopActivity(Activity activity,object args)
{
if (activity.Duration == TimeSpan.Zero)
{
activity.SetEndTime(Activity.GetUtcNow());
}
Write(activity.OperationName + ".Stop", args);
activity.Stop();
}
...
}
所以我的订阅者的定义如下
using Microsoft.Extensions.DiagnosticAdapter;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
namespace ConsoleApp6
{
public sealed class DiagnosticCollector
{
[DiagnosticName("Foo.Start")]
public void OnFooStart(Activity activity,int Index, long TimeStamp)
=> Console.WriteLine($"FooStart,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index},timestamp:{TimeStamp}");
[DiagnosticName("Foo.Stop")]
public void OnFooStop(Activity activity, int Index, double Elapsed)
=> Console.WriteLine($"FooStop ,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index};Elapsed:{Elapsed}");
[DiagnosticName("Bar.Start")]
public void OnBarStart(Activity activity, int Index, long TimeStamp)
=> Console.WriteLine($"BarStart,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index},timestamp:{TimeStamp}");
[DiagnosticName("Bar.Stop")]
public void OnBarStop(Activity activity, int Index, double Elapsed)
=> Console.WriteLine($"BarStop ,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index};Elapsed:{Elapsed}");
[DiagnosticName("Baz.Start")]
public void OnBazStart(Activity activity, int Index, long TimeStamp)
=> Console.WriteLine($"BazStart,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index},timestamp:{TimeStamp}");
[DiagnosticName("Baz.Stop")]
public void OnBazStop(Activity activity, int Index, double Elapsed)
=> Console.WriteLine($"BazStop ,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index};Elapsed:{Elapsed}");
[DiagnosticName("Gux.Start")]
public void OnGuxStart(Activity activity, int Index, long TimeStamp)
=> Console.WriteLine($"GuxStart,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index},timestamp:{TimeStamp}");
[DiagnosticName("Gux.Stop")]
public void OnGuxStop(Activity activity, int Index, double Elapsed)
=> Console.WriteLine($"GuxStop ,ActivityId:{activity.Id,-32}-{ activity.ParentId,-32},index:{Index};Elapsed:{Elapsed}");
}
}
最后是main方法调用,和S812中也很类似
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApp6
{
public class Program
{
static readonly Random _random = new Random();
public static async Task Main(string[] args)
{
DiagnosticListener.AllListeners.Subscribe(listener =>
{
if (listener.Name == "Web")
{
listener.SubscribeWithAdapter(new DiagnosticCollector());
}
});
await FooAsync();
}
static Task FooAsync() => InvokeAsync(FoobarSource.Instance.FooStart, FoobarSource.Instance.FooStop,
async () =>
{
await BarAsync();
await GuxAsync();
});
static Task BarAsync() => InvokeAsync(FoobarSource.Instance.BarStart, FoobarSource.Instance.BarStop,
BazAsync);
static Task BazAsync() => InvokeAsync(FoobarSource.Instance.BazStart, FoobarSource.Instance.BazStop,
() => Task.CompletedTask);
static Task GuxAsync() => InvokeAsync(FoobarSource.Instance.GuxStart, FoobarSource.Instance.GuxStop,
() => Task.CompletedTask);
static async Task InvokeAsync(Func<long,Activity> start, Action<Activity,double> stop, Func<Task> body)
{
//这里返回Activity,后面传入Stop方法
var activity = start(Stopwatch.GetTimestamp());
var sw = Stopwatch.StartNew();
await Task.Delay(_random.Next(10, 100));
await body();
stop(activity,sw.ElapsedMilliseconds);
}
}
}
当我写完了我发现确实很简单,作者没必要专门写一个案例。只是自己理解不到位。