A Serilog sink that publishes log events to RabbitMQ via the official RabbitMQ.Client library.
The aim of this sink is to expose RabbitMQ.Client functionality in an idiomatic Serilog
way without burying it behind extra abstractions. Expect plain RabbitMQ behaviour with a
slightly simpler surface.
- Features
- Installation
- Quick start
- Configuration
- Configuration reference
- Audit sink
- Customising message properties and routing keys
- Multi-host configuration
- Samples
- Compatibility matrix
- Migrating to 9.0.0
- References
- License
- Publishes log events through
RabbitMQ.Clientv7. - Supports both
WriteTo(batched) andAuditTo(synchronous, throws on failure) sinks. - Eagerly opens a fixed pool of channels at startup; broken channels are replaced in the background.
- Optional automatic exchange declaration.
- TLS / SSL support, multi-host clusters, dynamic routing keys, custom message properties.
- Pluggable failure sinks for fan-out on emit errors.
- Targets
netstandard2.0,net8.0, andnet10.0.
dotnet add package Serilog.Sinks.RabbitMQOr via the Package Manager Console:
Install-Package Serilog.Sinks.RabbitMQusing Serilog;
using Serilog.Sinks.RabbitMQ;
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.RabbitMQ(
hostnames: ["localhost"],
username: "guest",
password: "guest",
exchange: "logs",
exchangeType: "topic",
autoCreateExchange: true)
.CreateLogger();
Log.Information("Hello RabbitMQ");
Log.CloseAndFlush();The sink eagerly opens 64 channels in the background at startup. See Channel pool to tune.
The sink can be configured from code, from appsettings.json (via
Serilog.Settings.Configuration),
or from App.config (via
Serilog.Settings.AppSettings).
All approaches accept the same set of options — see Configuration reference.
The recommended way is the action-based overload, which gives strongly typed access to both the client and the sink configuration:
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.RabbitMQ((client, sink) =>
{
client.Hostnames = ["localhost"];
client.Username = "guest";
client.Password = "guest";
client.Exchange = "logs";
client.ExchangeType = "topic";
client.DeliveryMode = RabbitMQDeliveryMode.Durable;
client.RoutingKey = "log";
client.ChannelCount = 32;
sink.TextFormatter = new Serilog.Formatting.Json.JsonFormatter();
sink.BatchPostingLimit = 100;
sink.BufferingTimeLimit = TimeSpan.FromSeconds(2);
})
.CreateLogger();A flat-parameter overload is also available for simple setups (see Quick start).
{
"Serilog": {
"Using": [ "Serilog.Sinks.RabbitMQ" ],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "RabbitMQ",
"Args": {
"clientConfiguration": {
"hostnames": [ "localhost" ],
"username": "guest",
"password": "guest",
"exchange": "logs",
"exchangeType": "topic",
"deliveryMode": "Durable",
"routingKey": "log",
"autoCreateExchange": true,
"channelCount": 32
},
"sinkConfiguration": {
"batchPostingLimit": 100,
"bufferingTimeLimit": "00:00:02",
"textFormatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
}
}
}
]
}
}Keys are case-insensitive.
<add key="serilog:using:RabbitMQ" value="Serilog.Sinks.RabbitMQ" />
<add key="serilog:write-to:RabbitMQ.hostnames" value="server1,server2" />
<add key="serilog:write-to:RabbitMQ.username" value="guest" />
<add key="serilog:write-to:RabbitMQ.password" value="guest" />
<add key="serilog:write-to:RabbitMQ.exchange" value="logs" />
<add key="serilog:write-to:RabbitMQ.batchPostingLimit" value="100" />
<add key="serilog:write-to:RabbitMQ.bufferingTimeLimit" value="00:00:02" />| Option | Type | Default | Description |
|---|---|---|---|
hostnames |
string[] |
required | One or more broker hostnames. See Multi-host configuration. |
username |
string |
required | Authentication user. |
password |
string |
required | Authentication password. |
port |
int |
0 |
Broker port. 0 defaults to the RabbitMQ.Client default (5672 / 5671). |
vHost |
string |
"" |
Virtual host. |
heartbeat |
ushort |
0 (broker default) |
Heartbeat interval in milliseconds. |
clientProvidedName |
string? |
null |
Connection name shown in the RabbitMQ Management UI. |
| Option | Type | Default | Description |
|---|---|---|---|
exchange |
string |
"" |
Target exchange name. |
exchangeType |
string |
"fanout" |
Exchange type (direct, fanout, topic, headers). |
deliveryMode |
RabbitMQDeliveryMode |
NonDurable |
Persistence of published messages. |
routingKey |
string |
"" |
Default routing key. Can be overridden per event via ISendMessageEvents. |
autoCreateExchange |
bool |
false |
Declare the exchange on startup if it does not exist. |
| Option | Type | Default | Description |
|---|---|---|---|
sslEnabled |
bool |
false |
Enable TLS for broker connections. |
sslServerName |
string? |
first hostname | Server name used for certificate validation. |
sslVersion |
SslProtocols |
None |
TLS protocol version. |
sslAcceptablePolicyErrors |
SslPolicyErrors |
None |
Tolerated certificate validation errors. |
sslCheckCertificateRevocation |
bool |
false |
Check certificate revocation status. |
| Option | Type | Default | Description |
|---|---|---|---|
channelCount |
int |
64 |
Number of channels held in the pool. Channels are opened eagerly in the background at startup; broken channels are replaced automatically. When all channels are in use, additional publish calls await until one is returned. |
warmUpMaxRetries |
int? |
10 |
Maximum consecutive warm-up failures before the pool enters a broken state. While broken, GetAsync throws InvalidOperationException immediately so publish failures surface to the BatchingSink failure listener (or WriteTo.Fallback(...) chain) instead of blocking waiters. A 60 s cooldown elapses between exhaustion and the next probe attempt; probe success transitions the pool back to warming. Set to null for unlimited retries (pre-9.0 behaviour). The exponential backoff schedule between attempts (500 ms → 30 s cap) is not configurable. |
Deprecated:
maxChannels(parameter) andMaxChannels(property) are kept as[Obsolete]shims that forward tochannelCount/ChannelCount. They will be removed in a future major version. See Migrating to 9.0.0.
These options apply to WriteTo.RabbitMQ only. Audit sinks write each event synchronously
and ignore them.
| Option | Type | Default | Description |
|---|---|---|---|
batchPostingLimit |
int |
50 |
Maximum events written per batch. |
bufferingTimeLimit |
TimeSpan |
2s |
Flush interval for partial batches. |
queueLimit |
int? |
null |
Maximum buffered events. null = unbounded. |
| Option | Type | Default | Description |
|---|---|---|---|
emitEventFailure |
EmitEventFailureHandling |
WriteToSelfLog |
Combination of Ignore, WriteToSelfLog, WriteToFailureSink, ThrowException. See behaviour table below. |
failureSinkConfiguration |
Action<LoggerSinkConfiguration>? |
null |
Legacy sink(s) that receive events when the primary sink fails. Used together with WriteToFailureSink. Prefer WriteTo.Fallback(...) for new code. |
formatter |
ITextFormatter? |
CompactJsonFormatter |
Formatter used to render the event into the message body. |
levelSwitch |
LogEventLevel |
Verbose |
Minimum level for events emitted by the sink. |
sendMessageEvents |
ISendMessageEvents? |
null |
Hooks for customising message properties and routing keys (see below). |
WriteTo.RabbitMQ runs inside Serilog's BatchingSink. When a batch fails, the sink's behaviour depends on emitEventFailure:
| Flags | Behaviour |
|---|---|
Ignore |
Rethrow. BatchingSink's failure listener observes the exception (defaults to SelfLog). |
WriteToSelfLog (default) |
Log to SelfLog, then rethrow. |
ThrowException |
Rethrow (same as Ignore; kept for clarity). |
WriteToFailureSink |
Route events to the legacy failure sink; do not rethrow. |
WriteToFailureSink | WriteToSelfLog |
Log and route to failure sink; do not rethrow. |
WriteToFailureSink | ThrowException |
Route to failure sink and rethrow. |
For new code, use Serilog's native fallback chain instead of failureSinkConfiguration:
Log.Logger = new LoggerConfiguration()
.WriteTo.Fallback(
primary => primary.RabbitMQ((client, sink) =>
{
client.Hostnames = ["localhost"];
client.Username = "guest";
client.Password = "guest";
client.Exchange = "logs";
}),
fallback => fallback.Console())
.CreateLogger();Leave emitEventFailure at its default — the fallback chain relies on the sink rethrowing.
Log.Logger = new LoggerConfiguration()
.WriteTo.RabbitMQ(
configure: (client, sink) =>
{
client.Hostnames = ["localhost"];
client.Username = "guest";
client.Password = "guest";
client.Exchange = "logs";
sink.EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog
| EmitEventFailureHandling.WriteToFailureSink;
},
failureSinkConfiguration: failure => failure.Console())
.CreateLogger();A Serilog audit sink writes events that must succeed and surfaces exceptions to the caller.
Use AuditTo.RabbitMQ instead of WriteTo.RabbitMQ. Wrap audit logging calls in
try/catch to handle failures.
Log.Logger = new LoggerConfiguration()
.AuditTo.RabbitMQ((client, sink) =>
{
client.Hostnames = ["localhost"];
client.Username = "guest";
client.Password = "guest";
client.Exchange = "audit";
client.DeliveryMode = RabbitMQDeliveryMode.Durable;
})
.CreateLogger();The audit sink supports the same options as the regular sink except the batching options
(batchPostingLimit, bufferingTimeLimit, queueLimit).
Implement ISendMessageEvents to set per-event message properties or compute a routing key
from the log event.
public class CustomMessageEvents : ISendMessageEvents
{
public void OnSetMessageProperties(LogEvent logEvent, IBasicProperties properties)
{
properties.Headers = new Dictionary<string, object?>
{
["log-level"] = logEvent.Level.ToString(),
};
if (logEvent.Properties.TryGetValue("CorrelationId", out var correlationId))
{
properties.CorrelationId = correlationId.ToString();
}
}
public string OnGetRoutingKey(LogEvent logEvent, string defaultRoutingKey) =>
logEvent.Level switch
{
LogEventLevel.Error => "error",
_ => defaultRoutingKey,
};
}Wire it up via RabbitMQClientConfiguration.SendMessageEvents:
client.SendMessageEvents = new CustomMessageEvents();Pass multiple hostnames to connect to a RabbitMQ cluster. The client will attempt each in turn until one succeeds.
<add key="serilog:using:RabbitMQ" value="Serilog.Sinks.RabbitMQ" />
<add key="serilog:write-to:RabbitMQ.hostnames" value="host1,host2,host3" />
<add key="serilog:write-to:RabbitMQ.username" value="guest" />
<add key="serilog:write-to:RabbitMQ.password" value="guest" />Three runnable sample projects live under samples/:
| Sample | Configuration style | Target |
|---|---|---|
NetFromCodeSample |
Pure code, includes audit sink and failure sink | net10.0 |
NetAppsettingsJsonSample |
appsettings.json via Serilog.Settings.Configuration, includes a custom ISendMessageEvents |
net10.0 |
NetFrameworkAppSettingsConfigSample |
App.config via Serilog.Settings.AppSettings |
net48 |
A docker-compose.yml at the repo root brings up a RabbitMQ broker for running the samples
and integration tests.
| Serilog.Sinks.RabbitMQ | .NETStandard | .NETFramework | Serilog | RabbitMQ.Client |
|---|---|---|---|---|
| 2.0.0 | 1.6.0 | 4.5.1 | 2.3.0 | 4.* |
| 3.0.0 | 1.6.1 | 4.5.1 | 2.8.0 | 5.* |
| 6.0.0 | 2.0.0 | 4.7.2 | 2.8.0 | 6.* |
| 7.0.0 | 2.0.0 | — | 3.1.1 | 6.8.* |
| 8.0.0 | 2.0.0 | — | 4.2.0 | 7.0 |
| 9.0.0 | 2.0.0 | — | 4.3.x | 7.2.x |
MaxChannelsis nowChannelCount. The property and themaxChannelsparameter onWriteTo.RabbitMQ/AuditTo.RabbitMQhave been renamed toChannelCountandchannelCount. The property keeps an[Obsolete]shim so existing code compiles with a warning; the parameter is a hard rename — updateappsettings.json/App.configkeys frommaxChannelstochannelCount.- Channel pool semantics changed. The pool is now fixed-size and pre-opens all channels in the background at startup. When all channels are in use, additional publish calls await until one is returned (previous behaviour grew the pool on demand). Broken channels are replaced automatically in the background.
- Failure handling aligns with Serilog's
BatchingSink.EmitBatchAsyncnow rethrows by default so the BatchingSink's failure listener observes the error (it writes toSelfLogunless a listener is configured). Previously, failures were swallowed whenWriteToFailureSinkwas not set. If you relied on the old silent behaviour, setemitEventFailuretoWriteToFailureSinkwith afailureSinkConfiguration, or wire aWriteTo.Fallback(...)chain.AuditTo.RabbitMQalso notifies any configuredILoggingFailureListenerbefore rethrowing. WriteTo.Fallback(...)is now the recommended pattern. Serilog 4.1's native fallback chain is preferred overfailureSinkConfiguration. See the Failure handling section.Validate()on configuration objects.RabbitMQClientConfigurationandRabbitMQSinkConfigurationnow expose publicValidate()methods and are invoked during sink construction. Misconfiguration (missing hostnames, invalid port, zero batch limit, etc.) throws at startup instead of failing at first publish.QueueLimitvalidation.QueueLimit, when set, must be greater than zero —QueueLimit = 0now throwsArgumentOutOfRangeExceptionrather than silently creating a zero-capacity queue. Leave the property unset (null) for an unbounded queue.ChannelCountvalidation.ChannelCountmust be greater than zero.ChannelCount = 0or a negative value now throwsArgumentOutOfRangeExceptionat configuration time; previously the channel pool silently substituted the default of 64. Omit the property (or leave the parameter at its default) to keep the default pool size.- Bounded warm-up retry with circuit-breaker recovery.
RabbitMQChannelPoolnow backs off exponentially (500 ms → 30 s cap) and gives up afterWarmUpMaxRetriesconsecutive failures (default10). A broken pool failsGetAsyncfast so events surface to Serilog'sBatchingSinkfailure listener (orWriteTo.Fallback(...)) instead of blocking waiters. Self-heals via a half-open probe after a 60 s cooldown. SetRabbitMQClientConfiguration.WarmUpMaxRetries = nullfor unlimited retries — matches pre-9.0 behaviour, but preferWriteTo.Fallback(...)for resilience instead. Microsoft.Extensions.ObjectPooldependency removed. No action needed unless your application referenced it transitively through this package.- Target frameworks:
net6.0andnet9.0were removed. Supported targets arenetstandard2.0,net8.0, andnet10.0.
See CHANGELOG.md for the full release notes.
- Serilog
- RabbitMQ .NET client
- Serilog.Settings.Configuration
- Serilog.Settings.AppSettings
- Logging in ASP.NET Core
Apache-2.0 — see LICENSE.