博客
关于我
动手实现一个适用于.NET Core 的诊断工具
阅读量:453 次
发布时间:2019-03-06

本文共 5243 字,大约阅读时间需要 17 分钟。

.NET Core 诊断工具实践指南

前言

在软件开发的日常工作中,诊断工具是开发人员不可或缺的助手。从 dotTrace 到 .NET CLI 推出的高效诊断组件(dotnet trace、dotnet sos、dotnet dump),这些工具大大提升了程序调试的效率,让开发人员从更高层次发现问题。

今天,我们尝试动手实现一个简单的诊断工具。目标是在不修改代码和配置的前提下,获取程序运行信息,包括内存、线程、垃圾回收和异常等。我们将使用 Microsoft.Diagnostics.NETCore.Client 这个友好的组件来实现。


初始化项目

首先,我们创建两个 .NET Core 项目:

  • ConsoleApp:作为我们的诊断程序。
  • WebAPI:需要对这个 API 项目进行诊断分析。
  • 在 ConsoleApp 中通过 NuGet 引入必要的诊断组件:

    Install-Package Microsoft.Diagnostics.NETCore.ClientInstall-Package Microsoft.Diagnostics.Tracing.TraceEvent

    获取正在运行的程序列表

    在无侵入情况下,我们首先获取运行的 dotnet 程序,包括进程名称和 PID。

    修改 ConsoleApp 的 Program.cs

    class Program{    static void Main(string[] args)    {        if (args.Any())        {            switch (args[0])            {                case "ps": PrintProcessStatus(); break;            }        }    }    private 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();        }    }}

    运行 dotnet run ps 查看正在运行的进程信息。


    获取 GC 信息

    创建一个 DiagnosticsClient 实例,获取 GC 信息并输出事件名称。

    修改 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;        }    }}private static void PrintRuntime(int processId){    var providers = new List
    { new EventPipeProvider { Name = "Microsoft-Windows-DotNETRuntime", Level = EventLevel.Informational, Keywords = (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()); } }}

    运行 dotnet run runtime 3832 并通过浏览器或 curl 访问 WebAPI 接口。


    获取异常信息

    在 WebAPI 中抛出异常,测试诊断工具的捕获能力。

    修改 WebAPI 的 Get 方法:

    [HttpGet]public IEnumerable
    Get(){ try { 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(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); }}

    在 ConsoleApp 中,修改 PrintRuntime 方法的 Keywords 为 ClrTraceEventParser.Keywords.Exception

    运行 dotnet run runtime 13600 并访问 WebAPI 接口。


    生成 dump 文件

    使用 DiagnosticsClient 生成 dump 文件,便于后续分析。

    修改 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;        }    }}private static void Dump(int processId){    var client = new DiagnosticsClient(processId);    client.WriteDump(DumpType.Normal, @"mydump.dmp", false);}

    运行 dotnet run dump 13288 生成 dump 文件。


    生成 trace 文件

    同样使用 DiagnosticsClient 生成 trace 文件,分析 CPU 函数执行耗时。

    修改 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;        }    }}private static void Trace(int processId){    var cpuProviders = new List
    { new EventPipeProvider { Name = "Microsoft-Windows-DotNETRuntime", Level = EventLevel.Informational, Keywords = (long)ClrTraceEventParser.Keywords.Default }, new EventPipeProvider { Name = "Microsoft-DotNETCore-SampleProfiler", Level = EventLevel.Informational, Keywords = (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(); }}

    运行 dotnet run trace 13288 生成 trace 文件。


    总结

    .NET Core CLI 提供了强大的诊断工具,Microsoft.Diagnostics.NETCore.Client 让我们可以从高层次操作 CLR。通过生成 dump 和 trace 文件,我们可以深入分析程序性能和异常。

    这只是一个简单的实现示例,实际应用中可以根据需要扩展功能。如有疑问欢迎留言讨论!

    转载地址:http://ngeyz.baihongyu.com/

    你可能感兴趣的文章
    netcat的端口转发功能的实现
    查看>>
    netfilter应用场景
    查看>>
    netlink2.6.32内核实现源码
    查看>>
    Netpas:不一样的SD-WAN+ 保障网络通讯品质
    查看>>
    NetScaler的常用配置
    查看>>
    netsh advfirewall
    查看>>
    NETSH WINSOCK RESET这条命令的含义和作用?
    查看>>
    Netty WebSocket客户端
    查看>>
    netty 主要组件+黏包半包+rpc框架+源码透析
    查看>>
    Netty 异步任务调度与异步线程池
    查看>>
    Netty中集成Protobuf实现Java对象数据传递
    查看>>
    Netty事件注册机制深入解析
    查看>>
    Netty原理分析及实战(四)-客户端与服务端双向通信
    查看>>
    Netty客户端断线重连实现及问题思考
    查看>>
    Netty工作笔记0006---NIO的Buffer说明
    查看>>
    Netty工作笔记0007---NIO的三大核心组件关系
    查看>>
    Netty工作笔记0011---Channel应用案例2
    查看>>
    Netty工作笔记0013---Channel应用案例4Copy图片
    查看>>
    Netty工作笔记0014---Buffer类型化和只读
    查看>>
    Netty工作笔记0020---Selectionkey在NIO体系
    查看>>