.NET 核心中的 OpenTracing 不依赖于特定的解决方案库

OpenTracing in .NET core without taking dependency on specific solution library

为了登录我们的微服务应用程序,我们只需登录到 stdout/console,然后 docker 日志驱动程序处理这些日志并将其重定向到某个地方,例如gelf/logstash、fluentd 等。基本上,我们遵循 12 factor 日志记录指南。这意味着处理应用程序代码的开发人员不需要了解底层日志记录解决方案(例如 Elasticsearch、Graylog、Splunk 等)的任何信息——这完全是一个 ops/configuration 问题。从理论上讲,我们应该能够在不更改任何代码的情况下更改底层日志记录解决方案。

我想要类似的跟踪工具,我的研究让我找到了 OpenTracing。开发人员不需要了解底层跟踪解决方案(例如 Jaeger、Zipkin、Elastic APM 等)和日志记录;从理论上讲,我们应该能够在不更改任何代码的情况下更改底层跟踪解决方案。

我已经成功地获得了一个 .NET 核心 POC,它使用 opentracing/opentracing-csharp and jaegertracing/jaeger-client-csharp 库向 Jaeger 发送跟踪。

我仍在尝试完全了解 OpenTracing,但我想知道是否有一种方法可以将跟踪发送到 OpenTracing 兼容 API 而不必硬性依赖特定解决方案例如 Jaeger(即 jaeger-client-csharp 库)。根据我的理解,OpenTracing 只是一个标准。难道我不需要 jaeger-client-csharp 库就可以配置带有一些采样选项的 OpenTracing 端点吗?或者是 jaeger-client-csharp 实际上不是 Jaeger 特定的,实际上可以将跟踪发送到任何 OpenTracing API?

示例配置如下所示,它使用 jaeger 客户端库:

services.AddOpenTracing();

if (appSettings.TracerEnabled)
{
   services.AddSingleton(serviceProvider =>
   {
      var loggerFactory = new LoggerFactory();
      var config = Jaeger.Configuration.FromEnv(loggerFactory);

      var tracer = config.GetTracer();

      GlobalTracer.Register(tracer);

      return tracer;
   });
}

OpenTracing is a set of standard APIs that consistently model and describe the behavior of distributed systems)

OpenTracing 没有描述如何收集、报告、存储或表示相互关联的跟踪和跨度的数据。它是实现细节(例如jaeger or wavefront)。

jaeger-client-csharp 非常特定于 jaeger。但有一个例外,称为 zipkin,它反过来不完全符合 OpenTracing,即使它有类似的条款。

如果你对 opentracing-contrib/csharp-netcore 没问题(希望你正在使用这个库)那么如果你想实现 "no code change"(在目标微服务中)以配置跟踪子系统,你应该使用一些插件模型。

好消息是 aspnetcore 具有 hosted startup assemblies 的概念,它允许您配置跟踪系统。因此,您可以拥有一些名为 JaegerStartup 的库,您将在其中实现 IHostedStartup,如下所示:

public class JaegerStartup : IHostingStartup
{
    public void Configure(IWebHostBuilder builder)
    {
        builder.ConfigureServices((ctx, services) =>
        {
            services.AddOpenTracing();

            if (ctx.Configuration.IsTracerEnabled()) // implement it by observing your section in configuration.
            {
                services.AddSingleton(serviceProvider =>
                {
                    var loggerFactory = new LoggerFactory();
                    var config = Jaeger.Configuration.FromEnv(loggerFactory);

                    var tracer = config.GetTracer();

                    GlobalTracer.Register(tracer);

                    return tracer;
                });
            }
        });
    }
}

当你决定切换跟踪系统时——你需要创建另一个库,它可以自动加载,目标微服务代码不会被触及。

我认为这应该更新,现在我们有 ELastic APM 服务器支持的 Opentelemetry 直接支持以及 NetCore 中的本地支持 Activity class..

public static partial class ServiceExtension {

        public static IServiceCollection AddTelemerty(
            this IServiceCollection serviceCollection,
            IConfiguration Configuration, IWebHostEnvironment Environment) {

            serviceCollection.AddOpenTelemetryTracing((builder) => {
                // Sources
                builder.AddSource(Sources.DemoSource.Name);

                builder.SetResourceBuilder(ResourceBuilder
                  .CreateDefault()
                    //.AddAttributes( new List<KeyValuePair<String, object>> { 
                    //   new KeyValuePair<String, object>("SomeKey", "This is String Value")
                    //  })
                  .AddService(Environment.ApplicationName));

                builder.AddAspNetCoreInstrumentation(opts => {
                    opts.RecordException = true;
                });

                builder.AddElasticsearchClientInstrumentation();

                builder.AddSqlClientInstrumentation();

                builder.AddHttpClientInstrumentation(opts => opts.RecordException = true);

                if (Uri.TryCreate(Configuration.GetConnectionString("Jaeger"), UriKind.Absolute, out var uri)) {
                    builder.AddJaegerExporter(opts => {
                        opts.AgentHost = uri.Host;
                        opts.AgentPort = uri.Port;
                        opts.BatchExportProcessorOptions = new OpenTelemetry.BatchExportProcessorOptions<Activity>() {
                        };
                    });
                
                    // builder.AddZipkinExporter(opts => {
                    //     opts.Endpoint = new Uri("http://localhost:9412/api/v2/spans");
                    // });
                }
            });

            return serviceCollection;
        }
    }