OpenTelemetry搭配aspire-dashboard来监测系统性能

一、概念介绍

1、什么是OpenTelemetry

OpenTelemetry 是一个可观测性框架和工具包,旨在创建和管理遥测数据,如链路、、指标和日志。OpenTelemetry 对供应商和工具是中立的,这意味着它可以与各种可观测性后端一起使用,包括 Jaeger 和 Prometheus 这类开源工具以及商业化产品。

OpenTelemetry 不是像 Jaeger、Prometheus 或其他商业供应商那样的可观测性后端。OpenTelemetry 专注于遥测数据的生成、采集、管理和导出。OpenTelemetry 的一个主要目标是,无论应用程序或系统采用何种编程语言、基础设施或运行时环境,你都可以轻松地将其仪表化。重要的是,遥测数据的存储和可视化是有意留给其他工具处理的

2、什么是aspire-dashboard

Aspire-dashboard 是一个用于实时监控和调试应用程序的工具,特别适用于 ASP.NETCore 和其他支持 OpenTelemetry 的应用。它提供了一个用户界面来跟踪日志、指标数据,帮助开发者在本地环境中快速诊断问题。

二、安装OpenTelemetry环境

如下图所示安装所需的 nuget 包,如果想监测本项目的 MongoDB 性能,必须安装 MongoDB.Driver.Core.Extensions.DiagnosticSources 包。

三、开启监测项目本身Mongodb请求的性能

本项目使用 MongoDB 来存储数据,所以我们需要在 MongoClient 初始化之后再启用 OpenTelemetry。而且必须订阅 DiagnosticsActivityEventSubscriber 才能监测本项目的 MongoDB 性能。

namespace TelemetryConsole
{
    public static class StartupExtensions
    {
        public static void AddConfigurations(this IServiceCollection services, IConfiguration configuration)
        {
            
        }

        public static void AddServices(this IServiceCollection services)
        {
            services.AddSingleton<IAnalyzerService, AnalyzerService>();
            
        }

        public static void AddMongoDbConnection(this IServiceCollection services, IConfiguration configuration)
        {
            services.Configure<DatabaseSettings>(configuration.GetSection(nameof(DatabaseSettings)));
            services.AddSingleton<IDatabaseSettings>(sp => sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
            //services.AddSingleton<IMongoClient, MongoClient>(x =>
            //    new MongoClient(((DatabaseSettings)x.GetService(typeof(DatabaseSettings)))
            //        .ConnectionString));
            services.AddSingleton<IMongoClient, MongoClient>(x =>
            {
                /* If we have initialized a MongoClient, then its settings will be frozen.
                 * We cannot enable open telemetry at the moment.
                 * So we only enable open telemetry before initializing the MongoClient.
                 */
                var settings = MongoClientSettings
                    .FromConnectionString(((IDatabaseSettings)x.GetService(typeof(IDatabaseSettings)))?.ConnectionString)
                    .EnableOpenTelemetry();

                return new MongoClient(settings);
            });
            services.AddSingleton<IDbConnectionPool, MongoDbConnectionPool>();
            services.AddSingleton<IBaseDbService, BaseDbService>();
            services.AddSingleton<IGuidWrapper, GuidWrapper>();
            services.AddSingleton<IDateTimeWrapper, DateTimeWrapper>();
        }
    }
}
namespace TelemetryConsole.Telemetry
{
    public static class MongoClientExtensions
    {
        /// <summary>
        /// 开启监测项目本身Mongodb请求的性能
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        public static MongoClientSettings EnableOpenTelemetry(this MongoClientSettings settings)
        {
            if (settings == null)
            {
                return settings;
            }

            var previous = settings.ClusterConfigurator;
            settings.ClusterConfigurator = cb =>
            {
                previous?.Invoke(cb);
                cb.Subscribe(new DiagnosticsActivityEventSubscriber(
                    new InstrumentationOptions
                    {
                        CaptureCommandText = true
                    }));
            };
            return settings;
        }
    }
}

四、配置启动项

使用OpenTelemetry 最重要的有两点:一是在 appsetting.json 中添加配置Otlp:Endpoint,二是 builder.Services.AddDefaultOpenTelemetry() 配置OpenTelemetry 组件。若跟下面的示例代码类似配置了 o.Tracing.Endpoint = "http://localhost:4317",那不在 appsetting.json 中添加配置也行,配置这个 URL 是为了把生成的遥测数据发过去,让其他组件为我们生成可观测的性能报告。

namespace TelemetryConsole
{
    [ExcludeFromCodeCoverage]
    class Program
    {
        public static async Task<int> Main(string[] args)
        {
            try
            {
                var builder = Host.CreateApplicationBuilder();
                var conventionPack = new ConventionPack { new CamelCaseElementNameConvention() };
                ConventionRegistry.Register("camelCase", conventionPack, t => true);

                builder.Services.AddHttpClient();
                builder.Services.AddHostedService<Entry>();
                builder.Services.AddConfigurations(builder.Configuration);
                builder.Services.AddServices();
                builder.Services.AddMongoDbConnection(builder.Configuration);
                
                builder.Services.AddDefaultOpenTelemetry(
                    builder.Configuration,
                    o =>
                    {
                        o.Resource.Namespace = "MyNamespace"; // From configuration 'ServiceNamespace'
                        o.Resource.Version = "0.0.1";         // From configuration 'ServiceVersion'
                        o.Resource.InstanceId = "MyInstance"; // From configuration 'HOSTNAME'
                        o.Tracing.Endpoint = "http://localhost:4317";
                    });

                var host = builder.Build();
                await host.RunAsync();
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error occured when running Maxwell Perf Analyzer : {ex}");
                return 1;
            }
        }
    }
}

五、改造待监测的类

AnalyzerService() 是我们要监测的主要类,构造函数中的 ITracerFactory 是改造时添加的,只要是已启动监测的类型(MongoDB、Redis、Http请求),都会生成一条同等级的记录。但我们的代码是嵌套结构,若我们想更清晰的了解某个方法及其内部子方法的性能情况,就要用到ITracerFactory。using var span = _traceFactory.CreateSpan("Main point") 会生成一条目录,using 范围内的监测类型都会在这个目录之下,若子方法内部还有 _traceFactory.CreateSpan("Sub point"),那本目录就会缩进变成子目录,这样有了层级结构报告会更清晰。

namespace TelemetryConsole.Services.Impls
{
    public class AnalyzerService : IAnalyzerService
    {
        private const string DefaultUser = "test@slb.com";
        private readonly IBaseDbService _baseDbService;
        private IHttpClientFactory _httpFactory;
        private ITracerFactory _traceFactory;

        public AnalyzerService(IBaseDbService baseDbService, IHttpClientFactory httpFactory, ITracerFactory traceFactory)
        {
            _baseDbService = baseDbService;
            _httpFactory = httpFactory;
            _traceFactory = traceFactory;
        }

        public async Task AnalyzeAsync()
        {
			try
			{
                using var span = _traceFactory.CreateSpan("Main point");

                await QuerySomeData();

                await InsertSomeData();

                await ExecuteSomeHttpRequests();
            }
			catch (Exception EX)
			{
				throw;
			}
        }

        async Task QuerySomeData()
        {
            using var span = _traceFactory.CreateSpan("Query some data");

            var item01 = await _baseDbService.GetAsync<ModificationItem>("6a89034a-6e31-4aeb-a4f0-b36494fe7b18", "wireline-bgc-drillplan-bgc-bil", "modification");
            var item02 = await _baseDbService.GetAsync<ModificationItem>("10208e50-e4aa-447f-893d-dd8a4b29c8e4", "wireline-bgc-drillplan-bgc-bil", "modification");
            string str = "";
            for (int i = 0; i < 10000; i++)
            {
                str += i;
            }
        }

        async Task InsertSomeData()
        {
            using var activity = _traceFactory.CreateSpan("Insert Some Data");

            for (var i = 0; i < 10; i++)
            {
                await _baseDbService.CreateAsync(new ModificationItem
                {
                    Source = ParsingDataSource.Job,
                    ItemId = Guid.NewGuid().ToString()
                }, DefaultUser, null, "modification_wireline-bgc-drillplan-bgc-bil");
            }
            string str = "";
            for (int i = 0; i < 10000; i++)
            {
                str += i;
            }

            activity?.SetTag("RecordInserted", 10);
        }

        async Task ExecuteSomeHttpRequests()
        {
            using (var activity01 = _traceFactory.CreateSpan("Call HTTP API", ActivityKind.Client))
            {
                var client = _httpFactory.CreateClient();
                var response = await client.GetAsync("https://httpbin.org/get");
                var ret = await response.Content.ReadAsStringAsync();
            }

            using (var activity02 = _traceFactory.CreateSpan("Call TelemetryApi", ActivityKind.Client))
            {
                var client = _httpFactory.CreateClient();
                var response01 = await client.GetAsync("https://localhost:7094/ParsingStep?id=d9fff557-1e08-4850-97d7-5cdb73e809e4");
                var ret01 = await response01.Content.ReadAsStringAsync();

                var response02 = await client.GetAsync("https://localhost:7094/ParsingStep?id=e18031b9-1ff4-4320-bfd2-e0f618356ae0");
                var ret02 = await response01.Content.ReadAsStringAsync();
            }
        }
    }
}

六、利用podman启动aspire-dashboard

我在 Windows 系统中安装了WSL2(Ubuntu22.04.5 LTS)、Podman,不熟悉可参考:安装WSL2。启动 aspire-dashboard 之前要先检查 podman 环境是否正常,然后再启动。

--检查podman环境是否开启
wsl -l -v

--开启podman环境
podman machine start

--关闭podman环境
podman machine stop

--运行aspire-dashboard
podman run --rm -it -p 18888:18888 -p 4317:18889 -p 4318:18890 -d --name aspire-dashboard -e DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true mcr.microsoft.com/dotnet/aspire-dashboard:9.0

七、启动aspire-dashboard

当我们启动配置了 OpenTelemetry 的项目之后,就会生成遥测数据,访问 http://localhost:18888/traces 即可浏览性能报告。

1、层级结构日志

2、数据库处理请求

3、Http请求

4、调用的API中又请求了数据库

若配置了 OpenTelemetry 的项目,调用的 API 所属的项目也配置了 OpenTelemetry,那性能监测可以穿透到 API 项目。如下图所示,api 内部调用了数据库请求,在这里也能显示出来。

于2024年4月-2025年9月期间,研究团队在贵州习水国家级自然保护区制定39条样线,涵盖灌木林、常绿阔叶林、针叶林、常绿落叶阔叶混交林、针阔混交林等不同植被类型,每条样线分春夏秋冬4个季节采集样品,用真菌采集软件记录经纬度、海拔、采集地点、时间、生境等信息,使用佳能相机(R6 mark Ⅱ)对大型真菌进行拍照,并采集标本,标本存放于贵州省生物研究所大型真菌标本馆(HGAMF)。 通过形态学初步鉴定,结合分子生物学最终鉴定,参考已]报道的中国毒蘑菇名录开展毒蘑菇的认定。 调查到保护区内有毒真菌7目25科64种,导致中毒的主要类型有急性肾衰竭型、神经精神型和胃肠炎型。最终形成贵州习水国家级自然保护区大型有毒真菌图片数据集,它由以下2个部分组成。 (1)附件1包含78张原始照片(.JPG),照片名字包括了大型有毒真菌的拉丁名和中文名,若无中文名的直接用拉丁名。 (2)附件2是一个压缩文件,包含了2张工作表,其中一张表是大型有毒真菌39条样线的信息,另一张表是大型有毒真菌的中毒类型。 照片采用佳能相机R6 mark Ⅱ拍摄,物种鉴定通过多种文献核实,并经两位以上专家鉴定确认。该数据集可为研究地及周边的普通人识别有毒大型真菌提供参考,通过及时的图片对比,能有效避免误采误食大型有毒真菌,同时为因误食大型真菌可能引发的身体损伤进行了总结,能为患者及时治疗提供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

changuncle

若恰好帮到您,请随心打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值