A RabbitMQ client for ASP .NET Core 7 with Open Telemetry instrumentation so that events are properly traced and compatible with any distributed tracing system.
- Start RabbitMQ in Docker:
docker run -d --hostname rabbitmq --name rabbitmq -p 15672:15672 -p 5672:5672 rabbitmq:3-management
- Start Jaeger in Docker:
docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=9411 -e COLLECTOR_OTLP_ENABLED=true -p 4317:4317 -p 4318:4318 -p 5775:5775/udp -p 5778:5778 -p 6831:6831/udp -p 6832:6832/udp -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 16686:16686 -p 9411:9411 jaegertracing/all-in-one:latest
-
Run the
InstrumentedRabbitMqDotNetClient.TestApplication
-
Execut the GET method in the
api.http
file. -
Open Jaeger: http://localhost:16686
-
In Service, select
RabbitMQTestApplication
-
Click on
Find traces
. -
Click on the trace
RabbitMQTestApplication: /publish
- In the
Program
class, addAddRabbitMqInstrumentation()
to your existing OpenTelemetry code:
builder.Services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.AddOtlpExporter(opt => ...)
.AddSource(serviceName)
.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService(serviceName, serviceVersion))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
...
.AddRabbitMqInstrumentation(); // <- This line
});
- Afterwards, add the following line:
builder.Services.AddRabbitMQ(serviceName);
- Declare a
record
that inherits fromIEvent
.
public record MyEvent : IEvent
{
public string EventName => "my.event";
}
-
Inject
IEventPublisher
in:- A minimal api method:
string Publish([FromServices] IEventPublisher eventPublisher) { eventPublisher.Publish(new TestEvent()); return $"Event Published at {DateTime.Now.ToLongDateString()} - {DateTime.Now.ToLongTimeString()}"; } app.MapGet("/publish", Publish);
- a class:
public class MyClass { private readonly IEventPublisher _eventPublisher; public MyClass(IEventPublisher eventPublisher) { _eventPublisher = eventPublisher; } public void DoSomething() { this._eventPublisher.Publish(new MyEvent()) } }
- A minimal api method:
- Create a class to inherit from
IEventSubscription<MyEvent>
:
public class MyEventSubscription : IEventSubscription<MyEvent>
{
public Task HandleEventAsync(MyEvent receivedEvent, string operationId)
{
// Your logic here.
}
}
The aporoach is based on my repo dotnet-rabbitmq-client-instrumentation-app-insights, where App Insights has been removed and Open Telemtry has been added.
Ultimately, it uses the same structure with all parts of the library calling the RabbitMQDiagnosticSource
class. Is this class that has been updated to use ActivitySource and Activity in the correct way.
The work in this class is based on:
- https://www.mytechramblings.com/posts/getting-started-with-opentelemetry-and-dotnet-core/
- https://github.com/karlospn/opentelemetry-tracing-demo
A DiagnosticSource is available in the Instrumentation
folder and contains all the necessary code to instrument the calls to and from RabbitMQ.
The EventPublisher
uses it to start the activity.
The RabbitMQSubscriberHostedService
uses it to start processing the event and to to signal that the event processing has finished.
Looks like MassTransit is using a different approach, so its worth exploring:
- https://masstransit-project.com/advanced/monitoring/diagnostic-source.html
- https://github.com/alexeyzimarev/ndc-2020-talk-tracetrace/
- Upgrade to .NET8.
- Reuse same connection in both publishing and subscribing.
- Rename to
JoanComasFdz.RabbitMQ
. - Allow publishing to different exchanges.
- IEvent should follow cloud standard events.