dotnet-rabbitmq-client-instrumentation-app-insights

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.

Instrumented RabbitMQ in Jaeger

How to use

  1. Start RabbitMQ in Docker:
docker run -d --hostname rabbitmq --name rabbitmq -p 15672:15672 -p 5672:5672 rabbitmq:3-management
  1. 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
  1. Run the InstrumentedRabbitMqDotNetClient.TestApplication

  2. Execut the GET method in the api.http file.

  3. Open Jaeger: http://localhost:16686

  4. In Service, select RabbitMQTestApplication

  5. Click on Find traces.

  6. Click on the trace RabbitMQTestApplication: /publish

Register it in Program

  1. In the Program class, add AddRabbitMqInstrumentation() 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
});
  1. Afterwards, add the following line:
builder.Services.AddRabbitMQ(serviceName);

Create an event

  1. Declare a record that inherits from IEvent.
public record MyEvent : IEvent
{
    public string EventName => "my.event";
}

Publish an event

  1. Inject IEventPublisher in:

    1. 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);
    2. a class:
      public class MyClass
      {
          private readonly IEventPublisher _eventPublisher;
      
          public MyClass(IEventPublisher eventPublisher)
          {
              _eventPublisher = eventPublisher;
          }
      
          public void DoSomething()
          {
              this._eventPublisher.Publish(new MyEvent())
          }
      }

Subscribe to an event

  1. Create a class to inherit from IEventSubscription<MyEvent>:
public class MyEventSubscription : IEventSubscription<MyEvent>
{
    public Task HandleEventAsync(MyEvent receivedEvent, string operationId)
    {
        // Your logic here.
    }
}

Instrumentation

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:

How it works

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.

Further work

Looks like MassTransit is using a different approach, so its worth exploring:

Features

  • 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.