Using opentelemetry in .net core6

1. Import package

OpenTelemetry.Exporter.Console //Console output

OpenTelemetry.Exporter.OpenTelemetryProtocol //Protocol output, can be connected to jaeger, ziplin, seq

OpenTelemetry.Exporter.Prometheus.AspNetCore //Prometheus output

OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper // dotnetcore automatic injection library

OpenTelemetry.Instrumentation.AspNetCore // Telemetry implementation of some functions of dotnetcore

OpenTelemetry.Instrumentation.Http // Telemetry implementation related to http requests

OpenTelemetry.Extensions.Hosting // Extensions

System.Diagnostics.DiagnosticSource // If you want to use some models, you must use this library

2. Inject service

 public static class OpentelemetryExtension
    {
        /// <summary>
        /// exporter’s endpoint
        /// </summary>
        const string OTLP_ENDPOINT_URL = nameof(OTLP_ENDPOINT_URL);
        /// <summary>
        /// Header information carried
        /// </summary>
        const string OTEL_EXPORTER_OTLP_HEADERS = nameof(OTEL_EXPORTER_OTLP_HEADERS);
        /// <summary>
        /// Integrate opentelemetry into seq to collect trace logs
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <param name="environment">Environment</param>
        /// <param name="configuration">configuration</param>
        /// <returns></returns>
        public static IServiceCollection AddOpentelemetryWithTrace(this IServiceCollection services, IWebHostEnvironment environment, IConfiguration configuration)
        {
            var tracingOtlpEndpoint = configuration[OTLP_ENDPOINT_URL];
            var tracingOtlpHeader = configuration[OTEL_EXPORTER_OTLP_HEADERS];
            services.AddOpenTelemetry().WithTracing(tracing =>
            {
                tracing.AddAspNetCoreInstrumentation();
                tracing.AddHttpClientInstrumentation();
                tracing.AddEntityFrameworkCoreInstrumentation();
                tracing.AddSqlClientInstrumentation();
                tracing.AddQuartzInstrumentation();
                tracing.AddSource(environment.ApplicationName);
            });
            services.AddLogging(logging => logging.AddOpenTelemetry(openTelemetryLoggerOptions =>
            {
                openTelemetryLoggerOptions.SetResourceBuilder(
                    ResourceBuilder.CreateEmpty()
                        .AddService($"{environment.ApplicationName}-OpenTelemetry")
                        .AddAttributes(new Dictionary<string, object>
                        {
                            [nameof(environment.EnvironmentName).ToLower()] = environment.EnvironmentName
                        }));
                openTelemetryLoggerOptions.IncludeScopes = true;
                openTelemetryLoggerOptions.IncludeFormattedMessage = true;
                openTelemetryLoggerOptions.AddOtlpExporter(exporter =>
                {
                    exporter.Endpoint = new Uri(tracingOtlpEndpoint);
                    exporter.Protocol = OtlpExportProtocol.HttpProtobuf;
                    exporter.Headers = tracingOtlpHeader;
                });
            }));
            return services;
        }
    }

3. The sample code is as follows. A service is called in the api, and the service manually forwards the Propagator information. This information can be considered to be placed in the header when calling across services.

using Microsoft.AspNetCore.Mvc;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
using System.Diagnostics;

namespace OpenTelemetryDemo.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", " Sweltering", "Scorching"
    };

        private readonly ILogger<WeatherForecastController> _logger;
        private readonly ServiceA serviceA;
        private readonly IWebHostEnvironment webHostEnvironment;

        public WeatherForecastController(ILogger<WeatherForecastController> logger, ServiceA serviceA, IWebHostEnvironment webHostEnvironment)
        {
            _logger = logger;
            this.serviceA = serviceA;
            this.webHostEnvironment = webHostEnvironment;
        }
        private static readonly ActivitySource RegisteredActivity = new ActivitySource("Examples.ManualInstrumentations.Registered");

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            var name = Guid.NewGuid();
            _logger.LogInformation($"{name}");
            // A span
            using var activity = ServiceA.ActivitySource.StartActivity("Call to Service B");
            activity?.AddTag("Path","GetApi");

            serviceA.GetName(name.ToString());
            activity?.Stop();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }

    public class ServiceA
    {
        private readonly ILogger logger;

        public ServiceA(ILogger<ServiceA> logger)
        {
            this.logger = logger;
        }
        public static readonly ActivitySource ActivitySource = new ActivitySource("AAAA");

        private static readonly TextMapPropagator Propagator = new OpenTelemetry.Extensions.Propagators.B3Propagator();
        public string GetName(string name)
        {

            // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name
            var activityName = $"send";

            using var activity = Activity.Current  ActivitySource.StartActivity(activityName, ActivityKind.Producer);

            ActivityContext contextToInject = default;
            if (activity != null)
            {
                contextToInject = activity.Context;
            }
            else if (Activity.Current != null)
            {
                contextToInject = Activity.Current.Context;
            }
            Propagator.Inject(new PropagationContext(contextToInject, Baggage.Current), new Dictionary<string, string>() { { "X-B3-Flags", "1" }, { "X-B3-TraceId ", Guid.NewGuid().ToString() }, { "X-B3-SpanId", Guid.NewGuid().ToString() } }, InjectTraceContextIntoBasicProperties);

            var parentContext = Propagator.Extract(new PropagationContext(contextToInject, Baggage.Current), new Dictionary<string, string>() { { "X-B3-Flags", "1" }, { "X- B3-TraceId", Guid.NewGuid().ToString() }, { "X-B3-SpanId", Guid.NewGuid().ToString() } }, this.ExtractTraceContextFromBasicProperties);

            Baggage.Current = parentContext.Baggage;

            var activityName1 = $"receive";
            using var activity1 = ActivitySource.StartActivity(activityName1, ActivityKind.Consumer, parentContext.ActivityContext);
            activity1?.AddTag("Path", "GetName function");
            logger.LogWarning($"The parameter is {name}");
            activity1?.Stop();
            return name;
        }

        private void InjectTraceContextIntoBasicProperties(Dictionary<string, string> headers, string key, string value)
        {
            try
            {
                if (headers == null)
                {
                    headers = new Dictionary<string, string>();
                }

                headers[key] = value;
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Failed to inject trace context.");
            }
        }

        private IEnumerable<string> ExtractTraceContextFromBasicProperties(Dictionary<string, string> headers, string key)
        {
            try
            {
                if (headers.TryGetValue(key, out var value))
                {
                    return new[] { value };
                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Failed to extract trace context.");
            }

            return Enumerable.Empty<string>();
        }
    }
}

Source code