• DiagnosticSource DiagnosticListener 无侵入式分布式跟踪


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

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

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

    创建监听

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

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

    复制代码
    public class TestDiagnosticObserver : IObserver
    {
        public void OnNext(DiagnosticListener value)
        {
            value.Subscribe(new TestKeyValueObserver());
        }
        public void OnCompleted() { }
        public void OnError(Exception error) { }
    }
    复制代码
     
    其中重要的方法是OnNext。
    然后我们传入另一个自定义类型TestKeyValueObserver,这是实际接收实例发出的事件的类DiagnosticListener。
    该事件会接受KeyValuePair参数,我们后续可针对此参数做业务相关的筛选。
    复制代码
    public class TestKeyValueObserver : IObserverstring, object?>>
    {
        public void OnNext(KeyValuePair<string, object?> value)
        {
            var activity = Activity.Current;
    
            Console.WriteLine($"traceId {activity?.TraceId} Received event: {value.Key}");
        }
        public void OnCompleted() { }
        public void OnError(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 = new Item()
        {
            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 : IObserverstring, 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 void OnCompleted() { }
        public void OnError(Exception error) { }
    }
    复制代码

     

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

     

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


  • 相关阅读:
    STM32介绍和资料地址
    python程序,放入docker容器中, 生成镜像
    剑指 Offer 04. 二维数组中的查找
    Java判断时间间隔来决定任务执行
    spring gateway给请求添加params
    微服务和 Kubernetes 的三大服务网格技术
    8 个针对高级职位的高级 JavaScript 面试问题(译文)
    abortControllerMap: Map<string, AbortController>
    PDF压缩文件怎么压缩?两分钟让你学会三种方法
    【数据结构笔记06】数据结构之队列的顺序表示和实现(普通队列、循环队列)
  • 原文地址:https://www.cnblogs.com/chenyishi/p/18071178