Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >动手实现一个适用于.NET Core 的诊断工具

动手实现一个适用于.NET Core 的诊断工具

作者头像
全球技术精选
发布于 2021-05-18 03:16:19
发布于 2021-05-18 03:16:19
63600
代码可运行
举报
文章被收录于专栏:全球技术精选全球技术精选
运行总次数:0
代码可运行

前言

大家可能对诊断工具并不陌生,从大名鼎鼎的 dotTrace,到 .NET CLI 推出的一系列的高效诊断组件(dotnet trace,dotnet sos,dotnet dump)等, 这些工具提升了对程序Debug的能力和效率,可以让开发人员从更高层次的维度来发现程序中的问题。

今天我们针对于.NET Core, 尝试动手实现一个简单的诊断工具,在保证对程序无侵入(不修改代码和配置)的前提下,我们尝试获取程序的运行信息,包括内存,线程,垃圾回收,异常等。

这里可能会有小伙伴说,我可以用C++编写然后利用Profiling API实现,类似于OneAPM,Datadog 自动探针的形式来收集数据,当然也可以,不过今天我们主要用到了 Microsoft.Diagnostics.NETCore.Client,运行时团队给开发人员提供了更简单和友好的组件。

初始化项目

首先,我们需要创建两个.NET Core 的项目,一个是C#的控制台项目,名字叫ConsoleApp,这是我们的诊断程序,另一个是普通的WebAPI,我们需要对这个API项目进行诊断分析。

然后在控制台项目上通过Nuget引入诊断组件,分别是 Microsoft.Diagnostics.NETCore.Client,Microsoft.Diagnostics.Tracing.TraceEvent

1.获取正在运行的程序列表

在无侵入的情况下,我们首先需要获取到运行的dotnet程序,包括进程的名字和PID,在多个dotnet项目中,我们后边都会通过PID来对特定的程序进行诊断。修改ConsoleApp的Program.cs如下,这里主要用到了 GetPublishedProcesses 方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Program
{
    static void Main(string[] args)
    {
        if (args.Any())
        {
            switch (args[0])
            {
                case "ps": PrintProcessStatus(); break; 
            }
        }
    }

    public static void PrintProcessStatus()
    {
        var processes = DiagnosticsClient.GetPublishedProcesses()
            .Select(Process.GetProcessById)
            .Where(process => process != null);

        foreach (var process in processes)
        {
            Console.WriteLine($"ProcessId: {process.Id}");
            Console.WriteLine($"ProcessName: {process.ProcessName}");
            Console.WriteLine($"StartTime: {process.StartTime}");
            Console.WriteLine($"Threads: {process.Threads.Count}");

            Console.WriteLine();
            Console.WriteLine();
        }

    }
}

修改完成后,我们用命令行启动项目,WebAPI 项目运行dotnet run命令 , 启动之后,ConsoleApp 再运行 dotnet run ps命令,ps 是我们传入的参数,我们可以在控制台上看到正在运行的进程信息,我们主要会用到pid。

2.获取 GC 信息

我们创建了一个 DiagnosticsClient的实例,在构造函数中传入了processId进程ID,然后开启了一个有关GC信息的会话,最后订阅了CLR相关的事件回调,输出了事件名称EventName到控制台。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    if (args.Any())
    {
        switch (args[0])
        {
            case "ps": PrintProcessStatus(); break;
            case "runtime": PrintRuntime(int.Parse(args[1])); break;
        }
    }
} 

public static void PrintRuntime(int processId)
{ 
    var providers = new List<EventPipeProvider>()
    {
        new ("Microsoft-Windows-DotNETRuntime",EventLevel.Informational, (long)ClrTraceEventParser.Keywords.GC)

    };

    var client = new DiagnosticsClient(processId);
    using (var session = client.StartEventPipeSession(providers, false))
    {
        var source = new EventPipeEventSource(session.EventStream);

        source.Clr.All += (TraceEvent obj) =>
        {
            Console.WriteLine(obj.EventName);
        };

        try
        {
            source.Process();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}

接下来,我们修改一下WebAPI的代码,在控制器中的方法中创建了一个集合,并且添加了很多数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
    List<string> list = new ();

    for (int i = 0; i < 1000000; i++)
    {
        list.Add(i.ToString());
    } 


    var rng = new Random();
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
    }).ToArray();
}

同样,我们首先通过 dotnet run 命令启动WebAPI项目,然后 dotnet run ps 启动ConsoleApp项目,控制台会输出 webapi 项目的进程信息,我这里的pid是3832

然后在控制台项目中运行 dotnet run runtime 3832, runtime 和 3832 都是我们传入的参数, 然后开启一个新的命令行窗口,通过curl访问几次webapi的接口,当然你也可以在浏览器中访问,我们发现,在右边的控制台项目输出了GC的相关信息, 这里我们只输出了事件名,实际上我们可以拿到更多的数据信息。

3.获取异常信息

同样的,我们先修改WebApi项目,手动抛出一个异常。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
       throw new Exception("error");

       var rng = new Random();
       return Enumerable.Range(1, 5).Select(index => new WeatherForecast
       {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
        }).ToArray();
}

在控制台项目中,我们只需要改动一个Keywords 枚举,就是把 ClrTraceEventParser.Keywords.GC 改成 ClrTraceEventParser.Keywords.Exception,当然这里支持了其他更多的类型。

修改完成后,我们先启动 WebApi 项目,然后在ConsoleApp中先运行 dotnet run ps,查看webapi的进程id,然后再运行 dotnet run runtime 13600, 最后我们通过 curl 命令或者浏览器访问webapi的接口,同样,在右边的ConsoleApp中,输出了异常的相关事件信息。

在上面的代码中,我手动抛出一个异常,我们的诊断工具ConsoleApp是可以获取到相关的异常信息,那我用try,catch 把异常吃掉呢?它还能捕获到异常吗?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
      try
      {
           Convert.ToInt32("sss");
      }
      catch (Exception ex)
      {
           Console.WriteLine(ex.ToString()); 
      }  

      var rng = new Random();
      return Enumerable.Range(1, 5).Select(index => new WeatherForecast
      {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
      }).ToArray();
 }

修改代码后,我们重新运行webapi和诊断工具ConsoleApp,访问api接口时,你会发现,就算我们用try,catch 吃掉了异常,它仍然会输出异常信息。

4. 生成Dump文件

通过 Microsoft.Diagnostics.NETCore.Client 组件,我们可以很方便的为程序生生成Dump文件,然后可以用 windbg 工具来进行分析。

修改控制台项目ConsoleApp的Program.cs如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 static void Main(string[] args)
 {
            if (args.Any())
            {
                switch (args[0])
                {
                    case "ps": PrintProcessStatus(); break;
                    case "runtime": PrintRuntime(int.Parse(args[1])); break;
                    case "dump": Dump(int.Parse(args[1])); break;
                }
            }
}

public static void Dump(int processId)
{
     var client = new DiagnosticsClient(processId);
     client.WriteDump(DumpType.Normal, @"mydump.dmp", false);
}

修改完成后,启动webapi项目和控制台项目,在控制台项目中运行 dotnet run dump 13288 命令,它会在webapi的目录下,生成程序的dump文件

5.生成 Trace 文件

同样,我们可以很方便的生成 Trace 文件,它可以分析到CPU的函数执行耗时情况,它的格式是.nettrace, 你可以直接用VS 2017及以上或者 PerfView 工具打开。

修改控制台项目ConsoleApp的Program.cs如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    if (args.Any())
    {
        switch (args[0])
        {
            case "ps": PrintProcessStatus(); break;
            case "runtime": PrintRuntime(int.Parse(args[1])); break;
            case "dump": Dump(int.Parse(args[1])); break;
            case "trace": Trace(int.Parse(args[1])); break;
        }
    }
}

public static void Trace(int processId)
{
    var cpuProviders = new List<EventPipeProvider>()
    {
        new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default),
        new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.None)
    };
    var client = new DiagnosticsClient(processId);
    using (var traceSession = client.StartEventPipeSession(cpuProviders))
    {
        Task.Run(async () =>
        {
            using (FileStream fs = new FileStream(@"mytrace.nettrace", FileMode.Create, FileAccess.Write))
            {
                await traceSession.EventStream.CopyToAsync(fs);
            }

        }).Wait(10 * 1000);

        traceSession.Stop();
    }
}

修改完成后,启动webapi项目和控制台项目,在控制台项目中运行 dotnet run trace 13288命令,trace和13288都是参数,它会在控制台项目的目录下,生成 mytrace.nettrace文件

我们可以使用VS或者 PerfView 打开它

总结

其实在.NET Core CLI 中,已经提供了高度可用的一系列诊断工具,dotnet-trace,dotnet-dump 等等,Microsoft.Diagnostics.NETCore.Client 提供了非常友好和高层次的API,不仅仅是文中这些, 我们可以用C#代码,来完成对CLR层面的一些操作,来帮助我们发掘对程序诊断的更多可能性。

示例代码都已经上传到 https://github.com/SpringLeee/DiagnosticDemo,觉得不错的就给我点个赞吧!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半栈程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C# 11:接口中的静态抽象成员
接口我们都很了解了,在编写代码的时,如果有一定的抽象思维,就会将不同对象的相同行为抽象出来,放到接口中,我们最熟悉的就是在接口中写一堆方法的定义。
oec2003
2022/12/01
8240
C# 11:接口中的静态抽象成员
.NET Core开发实战(第19课:日志作用域:解决不同请求之间的日志干扰)--学习笔记
日志框架是用统一的记录方式,让我们可以把日志记录到不同的地方去,输出到不同的地方去
郑子铭
2021/01/13
5610
基于.net8在 ASP.NET Core 中掌握 API 密钥身份验证
如果我告诉您,保护 API 比您想象的要简单,会怎么样?如果您认为向 .NET 8 应用程序添加强大的安全性需要大量复杂的设置,那么想象一下只需几行代码即可实现 API 密钥身份验证的轻松程度。突然之间,您的服务变得安全并受到保护,不会受到未经授权的访问!您知道在 ASP.NET Core 中实施 API 密钥身份验证是多么容易吗?如果您有兴趣让您的 API 免受窥探,那么您绝对应该继续阅读。
郑子铭
2024/12/05
5910
基于.net8在 ASP.NET Core 中掌握 API 密钥身份验证
Unity3D网络通讯(一)--Asp.Net Core WebApi创建发布注意事项
最近一直在练习Unity3D,如果在项目中肯定少不了与后台交互,所以就准备把Unity中和后台交互这块专门做点Demo学习和掌握一下,计划是包括Http Restful,Socket,Webservice等方式都练习一下,本章就开始先从后台的Asp.Net Core的WebApi创建和发布开始。
Vaccae
2020/09/10
1.6K0
使用 Tye 辅助开发 k8s 应用竟如此简单(三)
续上篇,这篇我们来进一步探索 Tye 更多的使用方法。本篇我们来了解一下如何在 Tye 中如何对数据库进行链接。
newbe36524
2021/02/18
6430
使用 Tye 辅助开发 k8s 应用竟如此简单(三)
详解Net Core Web Api项目与在NginX下发布
本文将介绍Net Core的一些基础知识和如何NginX下发布Net Core的WebApi项目。
Kiba518
2020/02/26
1.5K0
ASP.NET Core 6框架揭秘实例演示[12]:诊断跟踪的进阶用法
一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根据当前的运行状态预知未来可能发生的问题,并将问题扼杀在摇篮中。诊断跟踪能够帮助我们有效地纠错和排错《几种基本诊断跟踪编程方式》提供了7个实例演示了针对TraceSource、EventSource和DiagnosticSource的基本用法,其实它们还具有一个更“高级”的使用方式。(本篇提供的实例已经汇总到《ASP.NET Core 6框架揭秘-实例演示版》)
蒋金楠
2022/05/09
4440
ASP.NET Core 6框架揭秘实例演示[12]:诊断跟踪的进阶用法
给.Net 5 Api增加JwtBearer认证
JWT是Json Web Token的缩写。JWT, 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
Mr. Wei
2020/12/27
1.7K0
给.Net 5 Api增加JwtBearer认证
【.NET】通过代码实现导出进程的dump文件和内存分析
因为需要获取进程的processID,所以接着上次写的识别.NET进程的控制台程序【参考检测.NET CORE+和.NET FX进程有关那个文章】,直接在这上面新增功能。
Wesky
2024/08/13
3040
【.NET】通过代码实现导出进程的dump文件和内存分析
.Net Aspire初体验
今天参加了Post Microsoft Build & AI Day深圳的集会,众多大佬分享了非常优质前沿的技术和实践,实在受益良多,为了消化吸收关于张队分享的.Net Aspire的内容,特实操一遍小示例并记录如下:
DotNet Whisperer
2024/08/14
2220
.Net Aspire初体验
.NET Core 中的日志与分布式链路追踪
程序记录的日志一般有两种作用,故障排查、显式程序运行状态,当程序发生故障时,我们可以通过日志定位问题,日志可以给我们留下排查故障的依据。很多时候,往往会认为日志记录非常简单,例如很多程序只是 try-catch{},直接输出到 .txt,但是这些日志往往无法起到帮助定位问题的作用,甚至日志充斥了大量垃圾内容;日志内容全靠人眼一行行扫描,或者 Ctrl+F 搜索,无法高效率审查日志;日志单纯输出到文本文件中,没有很好地管理日志。
痴者工良
2021/04/26
1.6K0
asp.net core 3.1/swagger
安装nuget包:Swashbuckle.AspNetCore.SwaggerUI和Swashbuckle.AspNetCore.Annotations,配置swagger:
雪飞鸿
2020/05/25
6160
不安装运行时运行 .NET 程序 - NativeAOT
大家好,先祝大家国庆快乐。不过大家看到这篇文章的时候估计已经过完国庆了 😃。 上一篇我们写了如何通过 SelfContained 模式发布程序(不安装运行时运行.NET程序)达到不需要在目标机器上安装 runtime 就可以运行 .NET 程序的目标。其实除了标准的 self-contained 微软还给我们带来了 Native AOT 发布模式。是的你没看错,通过该技术我们的 .NET 程序会直接编译为 Native 代码而不再是 IL ,程序运行的时候直接就是机器码,不再需要 JIT 编译。通过 AO
MJ.Zhou
2022/10/27
1.4K0
不安装运行时运行 .NET 程序 - NativeAOT
使用.Net Core编写命令行工具(CLI)
  命令行工具(CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。
leon公众号精选
2022/04/27
1K0
使用.Net Core编写命令行工具(CLI)
当 .NET 5 遇上OpenTelemetry,会碰撞出怎样的火花?
OpenTelemetry是谷歌和微软共同推进的云原生监控的新规范, 兼容OpenTracing和OpenCensus
全球技术精选
2021/02/19
5980
当 .NET 5 遇上OpenTelemetry,会碰撞出怎样的火花?
ASP.NET Core分布式项目实战(第三方ClientCredential模式调用)--学习笔记
先启动 IdentityServerCenter,ClientCredentialApi
郑子铭
2021/01/13
3320
诊断日志知多少 | DiagnosticSource 在.NET上的应用
最近为了解决ABP集成CAP时无法通过拦截器启用工作单元的问题,从小伙伴那里学了一招。借助DiagnossticSource,可以最小改动完成需求。关于DiagnosticSource晓东大佬18年在文章 在 .NET Core 中使用 Diagnostics (Diagnostic Source) 记录跟踪信息就有介绍,文章开头就说明了Diagnostics 一直是一个被大多数开发者忽视的东西。是的,我也忽略了,这个好东西,有必要学习一下,下面就和大家简单聊一聊System.Diagnostics.DiagnosticSource在.NET上的应用。
圣杰
2020/10/16
1.2K0
诊断日志知多少 | DiagnosticSource 在.NET上的应用
ASP.NET Core分布式项目实战(oauth密码模式identity server4实现)--学习笔记
在 IdentityServerCenter 的 Config 中引入测试命名空间
郑子铭
2021/01/13
5110
ASP.NET Core分布式项目实战(oauth密码模式identity server4实现)--学习笔记
.NET 5.0正式发布,新功能特性(翻译)
  我们很高兴今天.NET5.0正式发布。这是一个重要的版本—其中也包括了C# 9和F# 5大量新特性和优秀的改进。微软和其他公司的团队已经在生产和性能测试环境中开始使用了。这些团队向我们反馈的结果比较令人满意,它证明了对性能提升及降低Web应用托管成本的机会有积极的表现。从预览版1开始,我们一直在5.0上运行我们自己的网站。从我们目前的所见所闻来看,.NET5.0无需在升级上花费太多的精力就能带来巨大的价值。对于你的下一个应用来说,这是一个很好的选择,而且可以直接从早期的.NET Core版本升级。我们希望您在台式机、笔记本电脑和云实例上正式开始使用它。
张传宁IT讲堂
2020/11/13
2.5K0
.NET 5.0正式发布,新功能特性(翻译)
使用Azure DevOps Pipeline实现.Net Core程序的CI
上次介绍了Azure Application Insights,实现了.net core程序的监控功能。这次让我们来看看Azure DevOps Pipeline功能。Azure DevOps Pipeline 是Azure DevOps里面的一个组件,对于12个月试用账号同样永久免费。
MJ.Zhou
2020/07/21
7820
使用Azure DevOps Pipeline实现.Net Core程序的CI
推荐阅读
相关推荐
C# 11:接口中的静态抽象成员
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档