ASP.NET Core 中的框架中发出大量诊断事件,包括
当前请求
进入请求完成事件,
HttpClient
发出收到与响应,
EFCore
查询等等。

我们可以利用DiagnosticListener来选择性地监听这些事件,然后通过自己的方式组织这些日志,实现
无侵入
的分布式跟踪。

下面我们通过DiagnosticSource监听EFCore,与HTTPClient,实现链路追踪。

创建监听

现在我们将配置一个DiagnosticListener来监听全部事件。

首先,我们需要一个IObserver<DiagnosticListener>,我们将使用它来订阅所有事件。

public class TestDiagnosticObserver : IObserver<DiagnosticListener>{public voidOnNext(DiagnosticListener value)
{
value.Subscribe(
newTestKeyValueObserver());
}
public voidOnCompleted() { }public voidOnError(Exception error) { }
}
其中重要的方法是OnNext。
然后我们传入另一个自定义类型TestKeyValueObserver,这是实际接收实例发出的事件的类DiagnosticListener。
该事件会接受KeyValuePair<string, object>参数,我们后续可针对此参数做业务相关的筛选。
public class TestKeyValueObserver : IObserver<KeyValuePair<string, object?>>{public void OnNext(KeyValuePair<string, object?>value)
{
var activity =Activity.Current;

Console.WriteLine($
"traceId {activity?.TraceId} Received event: {value.Key}");
}
public voidOnCompleted() { }public voidOnError(Exception error) { }
}

最后一步是在应用程序中注册我们的程序TestDiagnosticObserver。

DiagnosticListener.AllListeners.Subscribe(new TestDiagnosticObserver());

创建HTTP请求与EFCore查询

我们新建一个接口,用来集成EF与HttpClient。并调用这个接口查看DiagnosticListener 监听到的内容

[HttpGet]public async Task<string>GetAsync()
{
//HTTP await _httpClient.GetAsync("https://www.baidu.com");//EF Item item = newItem()
{
Barcode
=Guid.NewGuid().ToString(),
Brand
= "Milky Way",
Name
= "Milk",
PruchasePrice
= 20.5,
SellingPrice
= 25.5};
_productsContext.Items.Add(item);
_productsContext.SaveChanges();
return "OK";
}

调用此接口来看看我们的DiagnosticListener的效果。

可以看到收到了很多Event,包括
当前请求
的各个阶段,
HttpClient
的各个阶段,与
EFCore查询
的各个阶段。

解析Event

然后修改TestKeyValueObserver,我们从中挑选我们需要的HTTPClient与EFCore相关的事件。

public class TestKeyValueObserver : IObserver<KeyValuePair<string, object?>>{public void OnNext(KeyValuePair<string, object?>value)
{
var activity =Activity.Current;//Console.WriteLine($"traceId {activity?.TraceId} Received event: {value.Key}"); if (value.Key.StartsWith("System.Net.Http.Request"))
{
var cEventStr =JsonConvert.SerializeObject(value.Value);var cEvent = JsonConvert.DeserializeAnonymousType(cEventStr, new { Request = new { RequestUri = ""} , Timestamp = 2879029490722});
Console.WriteLine($
"traceId {activity?.TraceId} Request.Start: {cEvent.Timestamp}");
Console.WriteLine($
"traceId {activity?.TraceId} Request.Uri: {cEvent.Request.RequestUri}");
}
if (value.Key.StartsWith("System.Net.Http.Response"))
{
var cEventStr =JsonConvert.SerializeObject(value.Value);var cEvent = JsonConvert.DeserializeAnonymousType(cEventStr, new { Request = new { RequestUri = "" }, Timestamp = 2879029490722});
Console.WriteLine($
"traceId {activity?.TraceId} Http.Response: {cEvent.Timestamp}");
}
if (value.Key.StartsWith("Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpening"))
{
var cEvent =(Microsoft.EntityFrameworkCore.Diagnostics.ConnectionEventData)value.Value;
Console.WriteLine($
"traceId {activity?.TraceId} Connection.ConnectionOpening: {cEvent?.StartTime.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
}
if (value.Key.StartsWith("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting"))
{
var cEvent =(Microsoft.EntityFrameworkCore.Diagnostics.CommandEventData)value.Value;
Console.WriteLine($
"traceId {activity?.TraceId} {cEvent?.Command.CommandText}");
}
if (value.Key.StartsWith("Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosed"))
{
var cEvent =(Microsoft.EntityFrameworkCore.Diagnostics.ConnectionEventData)value.Value;
Console.WriteLine($
"traceId {activity?.TraceId} Connection.ConnectionClosed: {cEvent?.StartTime.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
}
}
public voidOnCompleted() { }public voidOnError(Exception error) { }
}

再次启动,查看效果,可以看到已经获取到了http请求的开始结束事件,EF的查询语句,开始事件等。

最后我们可以结构化这些数据,并将其持久化到自己的监控体系中,实现链路跟踪。

标签: none

添加新评论