1. 引入包
OpenTelemetry.Exporter.Console // 控制台输出
OpenTelemetry.Exporter.OpenTelemetryProtocol // 协议输出,可以对接jaeger、ziplin、seq
OpenTelemetry.Exporter.Prometheus.AspNetCore //普罗米修斯的输出
OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper // dotnetcore 自动注入库
OpenTelemetry.Instrumentation.AspNetCore // dotnetcore 的一些功能的遥测实现
OpenTelemetry.Instrumentation.Http // http请求相关的遥测实现
OpenTelemetry.Extensions.Hosting // 扩展
System.Diagnostics.DiagnosticSource // 一些model 要用的话得用这个库
2. 注入service
public static class OpentelemetryExtension
{
/// <summary>
/// exporter的endpoint
/// </summary>
const string OTLP_ENDPOINT_URL = nameof(OTLP_ENDPOINT_URL);
/// <summary>
/// 携带的header信息
/// </summary>
const string OTEL_EXPORTER_OTLP_HEADERS = nameof(OTEL_EXPORTER_OTLP_HEADERS);
/// <summary>
/// 集成opentelemetry 到seq收集trace日志
/// </summary>
/// <param name="services">IServiceCollection</param>
/// <param name="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. 示例代码如下,在api 中调用了一个服务,该服务手动转发了 Propagator 信息,在跨服务调用时可以考虑该信息放置在header中
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($"参数是{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>();
}
}
}