手机微网站平台,外贸推广网站哪家,qq小程序权限设置,哪里有做效果图的前言大家可能对诊断工具并不陌生#xff0c;从大名鼎鼎的 dotTrace#xff0c;到 .NET CLI 推出的一系列的高效诊断组件#xff08;dotnet trace,dotnet sos,dotnet dump#xff09;等, 这些工具提升了对程序Debug的能力和效率#xff0c;可以让开发人员从更高层次的维度来… 前言大家可能对诊断工具并不陌生从大名鼎鼎的 dotTrace到 .NET CLI 推出的一系列的高效诊断组件dotnet trace,dotnet sos,dotnet dump等, 这些工具提升了对程序Debug的能力和效率可以让开发人员从更高层次的维度来发现程序中的问题。今天我们针对于.NET Core, 尝试动手实现一个简单的诊断工具在保证对程序无侵入不修改代码和配置的前提下我们尝试获取程序的运行信息包括内存线程垃圾回收异常等。这里可能会有小伙伴说我可以用C编写然后利用Profiling API实现类似于OneAPMDatadog 自动探针的形式来收集数据当然也可以不过今天我们主要用到了 Microsoft.Diagnostics.NETCore.Client运行时团队给开发人员提供了更简单和友好的组件。初始化项目首先我们需要创建两个.NET Core 的项目一个是C#的控制台项目名字叫ConsoleApp,这是我们的诊断程序,另一个是普通的WebAPI我们需要对这个API项目进行诊断分析。然后在控制台项目上通过Nuget引入诊断组件分别是 Microsoft.Diagnostics.NETCore.Client,Microsoft.Diagnostics.Tracing.TraceEvent1.获取正在运行的程序列表在无侵入的情况下我们首先需要获取到运行的dotnet程序包括进程的名字和PID在多个dotnet项目中我们后边都会通过PID来对特定的程序进行诊断。修改ConsoleApp的Program.cs如下这里主要用到了 GetPublishedProcesses 方法。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到控制台。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 ListEventPipeProvider(){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的代码在控制器中的方法中创建了一个集合并且添加了很多数据。[HttpGet]
public IEnumerableWeatherForecast Get()
{Liststring 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项目手动抛出一个异常。[HttpGet]
public IEnumerableWeatherForecast 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是可以获取到相关的异常信息那我用trycatch 把异常吃掉呢它还能捕获到异常吗[HttpGet]
public IEnumerableWeatherForecast 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如下: 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如下: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 ListEventPipeProvider(){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觉得不错的就给我点个赞吧最后欢迎扫码关注我们的公众号 【全球技术精选】专注国外优秀博客的翻译和开源项目分享。