Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/Components/Aspire.RabbitMQ.Client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dotnet add package Aspire.RabbitMQ.Client

## Usage example

In the _AppHost.cs_ file of your project, call the `AddRabbitMQClient` extension method to register an `IConnection` for use via the dependency injection container. The method takes a connection name parameter.
In the _Program.cs_ file of your project, call the `AddRabbitMQClient` extension method to register an `IConnection` for use via the dependency injection container. The method takes a connection name parameter.

```csharp
builder.AddRabbitMQClient("messaging");
Expand Down Expand Up @@ -89,6 +89,22 @@ You can also setup the [ConnectionFactory](https://rabbitmq.github.io/rabbitmq-d
builder.AddRabbitMQClient("messaging", configureConnectionFactory: factory => factory.ClientProvidedName = "MyApp");
```

## Observability
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have this section in other READMEs? I don't see it.

Copy link
Copy Markdown
Member

@eerhardt eerhardt Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We track these in https://github.com/microsoft/aspire/blob/main/src/Components/Telemetry.md - which doesn't have the new ones - RabbitMQ.Client.Publisher and Subscriber


### Tracing

The Aspire RabbitMQ Client integration includes the following activity sources for OpenTelemetry tracing:

- `Aspire.RabbitMQ.Client` - Tracks connection establishment and retry attempts.
- `RabbitMQ.Client.Publisher` - Tracks message publishing operations.
- `RabbitMQ.Client.Subscriber` - Tracks message consumption operations.

Tracing is enabled by default. To disable it, set `DisableTracing` to `true` in the settings:

```csharp
builder.AddRabbitMQClient("messaging", settings => settings.DisableTracing = true);
```

## AppHost extensions

In your AppHost project, install the `Aspire.Hosting.RabbitMQ` library with [NuGet](https://www.nuget.org):
Expand Down
71 changes: 60 additions & 11 deletions tests/Aspire.RabbitMQ.Client.Tests/ConformanceTests.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Aspire.TestUtilities;
using Aspire.Components.ConformanceTests;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Trace;
using RabbitMQ.Client;
using Xunit;

namespace Aspire.RabbitMQ.Client.Tests;

public class ConformanceTests : ConformanceTests<IConnection, RabbitMQClientSettings>, IClassFixture<RabbitMQContainerFixture>
{
private readonly RabbitMQContainerFixture _containerFixture;
private readonly RabbitMQContainerFixture? _containerFixture;
private string ConnectionString { get; set; }

public ConformanceTests(RabbitMQContainerFixture containerFixture, ITestOutputHelper? output = null) : base(output)
public ConformanceTests(RabbitMQContainerFixture? containerFixture, ITestOutputHelper? output = null) : base(output)
{
_containerFixture = containerFixture;
ConnectionString = (_containerFixture is not null && RequiresFeatureAttribute.IsFeatureSupported(TestFeature.Docker))
? _containerFixture.GetConnectionString()
: "amqp://localhost:5672";
}

protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Singleton;
Expand All @@ -31,7 +38,11 @@ public ConformanceTests(RabbitMQContainerFixture containerFixture, ITestOutputHe

protected override string[] RequiredLogCategories => Array.Empty<string>();

protected override string ActivitySourceName => "";
#if RABBITMQ_V6
protected override string ActivitySourceName => "Aspire.RabbitMQ.Client";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this get used for v6?

#else
protected override string ActivitySourceName => "RabbitMQ.Client.Publisher";
#endif

protected override string? ConfigurationSectionName => "Aspire:RabbitMQ:Client";

Expand Down Expand Up @@ -69,15 +80,9 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne
};

protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null)
{
var connectionString = RequiresFeatureAttribute.IsFeatureSupported(TestFeature.Docker) ?
_containerFixture.GetConnectionString() :
"amqp://localhost:5672";

configuration.AddInMemoryCollection([
new(CreateConfigKey("Aspire:RabbitMQ:Client", key, "ConnectionString"), connectionString)
=> configuration.AddInMemoryCollection([
new(CreateConfigKey("Aspire:RabbitMQ:Client", key, "ConnectionString"), ConnectionString)
]);
}

protected override void RegisterComponent(HostApplicationBuilder builder, Action<RabbitMQClientSettings>? configure = null, string? key = null)
{
Expand Down Expand Up @@ -132,4 +137,48 @@ protected override void SetupConnectionInformationIsDelayValidated()
{
Assert.Skip("RabbitMQ connects to localhost by default if the connection information isn't available.");
}

#if !RABBITMQ_V6
[Fact]
[RequiresFeature(TestFeature.Docker)]
public void TracingEnablesTheRightActivitySource()
=> RemoteInvokeWithLogging(static connectionStringToUse =>
RunWithConnectionString(connectionStringToUse, static obj => obj.RunActivitySourceTest(key: null)),
ConnectionString, Output);

[Fact]
[RequiresFeature(TestFeature.Docker)]
public void TracingEnablesTheRightActivitySource_Keyed()
=> RemoteInvokeWithLogging(static connectionStringToUse =>
RunWithConnectionString(connectionStringToUse, static obj => obj.RunActivitySourceTest(key: "key")),
ConnectionString, Output);

private void RunActivitySourceTest(string? key)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't this call the base test method?

// This method can have side effects (setting AppContext switch, enabling activity source by name).
// That is why it needs to be executed in a standalone process.
// We use RemoteExecutor for that, but it does not support abstract classes
// (it can not determine the type to instantiate), so that is why this "test"
// is here and derived types call it
protected void ActivitySourceTest(string? key)

{
HostApplicationBuilder builder = CreateHostBuilder(key: key);
builder.Logging.AddConsole();
RegisterComponent(builder, options => SetTracing(options, true), key);

List<Activity> exportedActivities = [];
builder.Services.AddOpenTelemetry().WithTracing(traceBuilder => traceBuilder.AddInMemoryExporter(exportedActivities));

using IHost host = builder.Build();
host.Start();

IConnection service = key is null
? host.Services.GetRequiredService<IConnection>()
: host.Services.GetRequiredKeyedService<IConnection>(key);

// Clear activities generated during connection establishment (from "Aspire.RabbitMQ.Client" source)
exportedActivities.Clear();

TriggerActivity(service);

Assert.NotEmpty(exportedActivities);
Assert.Contains(exportedActivities, activity => activity.Source.Name == ActivitySourceName);
}

private static void RunWithConnectionString(string connectionString, Action<ConformanceTests> test)
=> test(new ConformanceTests(null) { ConnectionString = connectionString });
#endif
}