

OpenTrace take a tracer instance (e.g. Jaeger) to post the metrics via UDP to the remote Jaeger instance for display
OpenTrace then can be acquired in DI manner and get the hierrchical span id everywhere you want to leverage.
In this example
First processor - Product Service will acquire OpenTrace and get the span ids which seperated in semi colon
"fa9af85bcbcc5f09:ceb22ff39dc75f9f:fa9af85bcbcc5f09:1"
then this will be pass to rabbit mq message as property value of message.
Second Processor - Order Sevice will acquire the property via JaegerMiddleWare and resolve it to get the span id hierarchy then use it to trace whatever the operation under the same parent span id


Configuration Phase : Freqence of sending these tracing data can be configured using sampler


using Jaeger; using Jaeger.Reporters; using Jaeger.Samplers; using Jaeger.Senders; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OpenTracing; using OpenTracing.Util; using RawRabbit.Instantiation;namespace Common.Jaeger {public static class Extensions{private static bool _initialized;public static IServiceCollection AddJaeger(this IServiceCollection services){if (_initialized){return services;}_initialized = true;var options = GetJaegerOptions(services);if (!options.Enabled){var defaultTracer = DefaultTracer.Create();services.AddSingleton(defaultTracer);return services;}services.AddSingleton<ITracer>(sp =>{var loggerFactory = sp.GetRequiredService<ILoggerFactory>();var reporter = new RemoteReporter.Builder().WithSender(new UdpSender(options.UdpHost, options.UdpPort, options.MaxPacketSize)).WithLoggerFactory(loggerFactory).Build();var sampler = GetSampler(options);var tracer = new Tracer.Builder(options.ServiceName).WithReporter(reporter).WithSampler(sampler).Build();GlobalTracer.Register(tracer);return tracer;});return services;}public static IClientBuilder UseJaeger(this IClientBuilder builder, ITracer tracer){builder.Register(pipe => pipe.Use<JaegerStagedMiddleware>(tracer));return builder;}private static JaegerOptions GetJaegerOptions(IServiceCollection services){using (var serviceProvider = services.BuildServiceProvider()){var configuration = serviceProvider.GetService<IConfiguration>();services.Configure<JaegerOptions>(configuration.GetSection("jaeger"));return configuration.GetOptions<JaegerOptions>("jaeger");}}private static ISampler GetSampler(JaegerOptions options){switch (options.Sampler){case "const": return new ConstSampler(true);case "rate": return new RateLimitingSampler(options.MaxTracesPerSecond);case "probabilistic": return new ProbabilisticSampler(options.SamplingRate);default: return new ConstSampler(true);}}} }
For first processor - Product Service at controller level embed the span id to the context and pass over the RabbitMQ message

For Second Processor, Order Sevice will acquire the property via JaegerMiddleWare and resolve it to get the span id hierarchy then use it to trace whatever the operation under the same parent span id


using Autofac; using Common.Messages; using Common.RabbitMQ; using Jaeger; using OpenTracing; using OpenTracing.Tag; using RawRabbit.Pipe; using RawRabbit.Pipe.Middleware; using System; using System.Threading; using System.Threading.Tasks;namespace Common.Jaeger {public class JaegerStagedMiddleware : StagedMiddleware{private readonly ITracer _tracer;public JaegerStagedMiddleware(ITracer tracer)=> _tracer = tracer;public override string StageMarker => RawRabbit.Pipe.StageMarker.MessageDeserialized;public override Task InvokeAsync(IPipeContext context, CancellationToken token = new CancellationToken()){var correlationContext = (ICorrelationContext)context.GetMessageContext();var messageType = context.GetMessageType();using (var scope = BuildScope(messageType, correlationContext.SpanContext)){var span = scope.Span;span.Log($"Processing {messageType.Name}");try{return Next.InvokeAsync(context, token);}catch (Exception ex){span.SetTag(Tags.Error, true);span.Log(ex.Message);}}return Next.Next.InvokeAsync(context, token);}private IScope BuildScope(Type messageType, string serializedSpanContext){var spanBuilder = _tracer.BuildSpan($"processing-{messageType.Name}").WithTag("message-type", $@"{(messageType.IsAssignableTo<ICommand>() ? "command" : "event")}");if (string.IsNullOrEmpty(serializedSpanContext)){return spanBuilder.StartActive(true);}var spanContext = SpanContext.ContextFromString(serializedSpanContext);return spanBuilder.AddReference(References.FollowsFrom, spanContext).StartActive(true);}} }