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