Skip to content

Commit

Permalink
Release/211 add basic properties to message (#222)
Browse files Browse the repository at this point in the history
* Add SendMessageEvents class for setting message properties prior Publish

Rename
- RouteKey -> RoutingKey
- Period -> BufferingTimeLimit
  • Loading branch information
ArieGato authored Nov 24, 2024
1 parent e26f1fe commit 8cec7bb
Show file tree
Hide file tree
Showing 29 changed files with 428 additions and 161 deletions.
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,57 @@ The SSL support has been improved. Now all properties are used when creating the
- Requires `Serilog.Sinks.PeriodicBatching` `4.0`. This could break other sinks that inherit from `PeriodicBatchingSink` from this package. This was obsolete code and it has been removed. See [release notes](https://github.com/serilog/serilog-sinks-periodicbatching/releases/tag/v4.0.0) for more information.

## 8.0.0

### Added support for RabbitMQ.Client 7.0.0

Add support for `RabbitMQ.Client` version `7.0.0`. Note that this is a breaking change. The `RabbitMQ.Client` `7.0.0` is not compatible with the `RabbitMQ.Client` `6.x`.

### Added support for Serilog 4.1.0

Removed reference to `Serilog.Sinks.PeriodicBatching` and use `Serilog.Sinks.Batch` instead.

### Set message properties

Add support for setting `BasicProperties` before publishing a message. Properties can be set by creating a class implemnenting `ISendMessageEvents`.

```csharp
public void OnSetMessageProperties(LogEvent logEvent, IBasicProperties properties)
{
// example of setting message headers based on log event properties
logEvent.Properties.TryGetValue("messageType", out var messageType);
properties.Headers = new Dictionary<string, object?>
{
{ "messageType", messageType?.ToString() },
};

// example of setting correlation id based on log event properties
if (logEvent.Properties.TryGetValue(LogProperties.CORRELATION_ID, out var correlationId))
{
properties.CorrelationId = correlationId.ToString();
}
}
```

### Dynamic Routing Key

Moved the logic for determining the routing key logic to the `ISendMessageEvents`. This allows for more flexibility when setting the routing key.

```csharp
public string OnGetRoutingKey(LogEvent logEvent, string defaultRoutingKey)
{
// example of routing based on log level
return logEvent.Level switch
{
LogEventLevel.Error => "error",
_ => _defaultRoutingKey
};
}
```

### Breaking changes

- Upgrade RabbitMQ.Client to `7.0.0`.
- Upgrade Serilog to `4.1.0`.
- Renamed `Period` to `BufferingTimeLimit`.
- Renamed `RouteKey` to `RoutingKey`
- Removed `RoutingFunc`
57 changes: 48 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Use of named arguments is strongly recommended.
* `exchange`
* `exchangeType`
* `deliveryMode`
* `routeKey`
* `routingKey`
* `port`
* `vHost`
* `heartbeat`
Expand All @@ -85,7 +85,7 @@ Use of named arguments is strongly recommended.
* `sslAcceptablePolicyErrors`
* `sslCheckCertificateRevocation`
* `batchPostingLimit`
* `period`
* `bufferingTimeLimit`
* `queueLimit`
* `formatter`
* `autoCreateExchange`
Expand All @@ -94,7 +94,7 @@ Use of named arguments is strongly recommended.

### Arguments

Parameters `exchange`, `exchangeType`, `deliveryMode`, `routeKey` provide additional configuration when connecting to RabbitMQ.
Parameters `exchange`, `exchangeType`, `deliveryMode`, `routingKey` provide additional configuration when connecting to RabbitMQ.
If `autoCreateExchange` is `true`, the sink will create the exchange if an exchange by that name doesn't exist.
Exchange is not created by default.

Expand All @@ -106,7 +106,7 @@ and certificate revocation checking is disabled. You can change server name thro
This is a "periodic batching sink." The sink will queue a certain number of log events before they're actually written to RabbitMQ.
There is also a timeout period so that the batch is always written even if it has not been filled.
By default, the batch size is 50 rows and the timeout is 2 seconds.
You can change these through by setting the `batchPostingLimit` and `period` arguments.
You can change these through by setting the `batchPostingLimit` and `bufferingTimeLimit` arguments.

Refer to the [Formatter](https://github.com/serilog/serilog/wiki/Formatting-Output#formatting-json) for details about the _formatter_ arguments.

Expand Down Expand Up @@ -139,7 +139,7 @@ for complete details about sink configuration. This is an example of setting som
<add key="serilog:write-to:RabbitMQ.hostnames" value="server1,server2"/>
<add key="serilog:write-to:RabbitMQ.exchange" value="LogExchange"/>
<add key="serilog:write-to:RabbitMQ.batchPostingLimit" value="1000"/>
<add key="serilog:write-to:RabbitMQ.period" value="0.00:00:02.00"/>
<add key="serilog:write-to:RabbitMQ.bufferingTimeLimit" value="0.00:00:02.00"/>
```

## External configuration using Serilog.Settings.Configuration
Expand All @@ -165,7 +165,7 @@ for complete details about sink configuration. Keys and values are not case-sens
"exchange": "LogExchange",
"autoCreateExchange": true,
"batchPostingLimit": 1000,
"period": "0.00.00.02.00"
"bufferingTimeLimit": "0.00.00.02.00"
}
}
]
Expand All @@ -188,7 +188,7 @@ The constructor accepts most of the same arguments, and like other Serilog audit
* `exchange`
* `exchangeType`
* `deliveryMode`
* `routeKey`
* `routingKey`
* `port`
* `vHost`
* `heartbeat`
Expand All @@ -202,7 +202,7 @@ The constructor accepts most of the same arguments, and like other Serilog audit
* `maxChannels`
* `levelSwitch`

The _batchPostingLimit_ and _period_ parameters are not available because the audit sink writes log events immediately.
The _batchPostingLimit_ and _bufferingTimeLimit_ parameters are not available because the audit sink writes log events immediately.

```json
{
Expand Down Expand Up @@ -259,7 +259,7 @@ Log.Logger = new LoggerConfiguration()
clientConfiguration.Exchange = _config["RABBITMQ_EXCHANGE"];
clientConfiguration.ExchangeType = _config["RABBITMQ_EXCHANGE_TYPE"];
clientConfiguration.DeliveryMode = RabbitMQDeliveryMode.Durable;
clientConfiguration.RouteKey = "Logs";
clientConfiguration.RoutingKey = "Logs";
clientConfiguration.Port = 5672;

foreach (string hostname in _config.GetSection("RABBITMQ_HOSTNAMES").Get<string[]>())
Expand Down Expand Up @@ -324,8 +324,47 @@ loggerFactory
services.AddSingleton<ILoggerFactory>(loggerFactory);
```

## Customize Message Properties and Routing Key

In order to set message properties, you can create a class which implements `ISendMessageEvents`.
This interface describes two methods that you must implement. The first is `OnSetMessageProperties` which is called before
the message is sent to RabbitMQ. The second is `OnGetRoutingKey` which is called to determine the routing key for the message.

```csharp
public class CustomMessageEvents : ISendMessageEvents
{
/// <inheritdoc />
public void OnSetMessageProperties(LogEvent logEvent, IBasicProperties properties)
{
// example of setting message header based on log event level
properties.Headers = new Dictionary<string, object?>
{
{ "log-level", logEvent.Level.ToString() },
};

// example of setting correlation id based on log event properties
if (logEvent.Properties.TryGetValue(LogProperties.CORRELATION_ID, out var correlationId))
{
properties.CorrelationId = correlationId.ToString();
}
}

/// <inheritdoc />
public string OnGetRoutingKey(LogEvent logEvent, string defaultRoutingKey)
{
// example of routing based on log level
return logEvent.Level switch
{
LogEventLevel.Error => "error",
_ => _defaultRoutingKey
};
}
}
```

## References

- [Serilog](https://serilog.net/)
- [RabbitMQ Client](https://github.com/rabbitmq/rabbitmq-dotnet-client)
- [Logging in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging)
- [Dependency Injection in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection)
65 changes: 65 additions & 0 deletions samples/Net8AppsettingsJsonSample/CustomSendMessageEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2015-2024 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using RabbitMQ.Client;
using Serilog.Events;
using Serilog.Sinks.RabbitMQ;

namespace Net8AppsettingsJsonSample;

/// <summary>
/// A custom SendMessageEvents class to handle events before sending a message.
/// </summary>
public sealed class CustomSendMessageEvents : ISendMessageEvents
{
private readonly string _defaultRoutingKey;

/// <summary>
/// The constructor for the CustomSendMessageEvents class.
/// </summary>
/// <param name="defaultRoutingKey">The default routing key.</param>
public CustomSendMessageEvents(string defaultRoutingKey)
{
_defaultRoutingKey = defaultRoutingKey;
}

/// <inheritdoc />
public void OnSetMessageProperties(LogEvent logEvent, IBasicProperties properties)
{
// example of setting message headers based on log event properties
logEvent.Properties.TryGetValue("messageType", out var messageType);
properties.Headers = new Dictionary<string, object?>
{
{ "messageType", messageType?.ToString() },
{ "log-level", logEvent.Level.ToString() },
};

// example of setting correlation id based on log event properties
if (logEvent.Properties.TryGetValue(LogProperties.CORRELATION_ID, out var correlationId))
{
properties.CorrelationId = correlationId.ToString();
}
}

/// <inheritdoc />
public string OnGetRoutingKey(LogEvent logEvent, string defaultRoutingKey)
{
// example of routing based on log level
return logEvent.Level switch
{
LogEventLevel.Error => "error",
_ => _defaultRoutingKey
};
}
}
12 changes: 12 additions & 0 deletions samples/Net8AppsettingsJsonSample/LogProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Net8AppsettingsJsonSample;

/// <summary>
/// A class to hold the log context properties.
/// </summary>
public static class LogProperties
{
/// <summary>
/// The correlation id property name.
/// </summary>
public const string CORRELATION_ID = "CorrelationId";
}
10 changes: 8 additions & 2 deletions samples/Net8AppsettingsJsonSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
// limitations under the License.

using Microsoft.Extensions.Configuration;
using Net8AppsettingsJsonSample;
using Serilog;
using Serilog.Context;
using Serilog.Debugging;

// Enable the SelfLog output
Expand All @@ -26,8 +28,12 @@

Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.CreateLogger();

Log.Information("Hello, world!");
using (LogContext.PushProperty(LogProperties.CORRELATION_ID, Guid.NewGuid()))
{
Log.Information("Hello, world!");

Log.CloseAndFlush();
Log.CloseAndFlush();
}
42 changes: 27 additions & 15 deletions samples/Net8AppsettingsJsonSample/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,33 @@
{
"Name": "RabbitMQ",
"Args": {
"username": "serilog",
"password": "serilog",
"hostnames": [
"localhost"
],
"port": 5672,
"virtualHost": "/",
"exchange": "LogExchange",
"exchangeType": "fanout",
"deliveryMode": "Durable",
"autoCreateExchange": true,
"batchPostingLimit": 50,
"period": "0.00:00:02.00",
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog",
"emitEventFailure": "WriteToSelfLog,WriteToFailureSink",
"clientConfiguration": {
"hostnames": [ "localhost" ],
"username": "serilog",
"password": "serilog",
"port": 5672,
"vHost": "/",
"clientProvidedName": "RabbitMQSink",
"heartbeat": 1000,
"exchange": "LogExchange",
"exchangeType": "direct",
"deliveryMode": "Durable",
"routingKey": "log",
"autoCreateExchange": true,
"maxChannels": 32,
"sendMessageEvents": {
"type": "Net8AppsettingsJsonSample.CustomSendMessageEvents, Net8AppsettingsJsonSample",
"defaultRoutingKey": "logs"
}
},
"sinkConfiguration": {
"batchPostingLimit": 10,
"bufferingTimeLimit": "0.00:00:02.00",
"queueLimit": 1000,
"textFormatter": "Serilog.Formatting.Json.JsonFormatter, Serilog",
"restrictedToMinimumLevel": "Debug",
"emitEventFailure": "WriteToSelfLog,WriteToFailureSink"
},
"failureSinkConfiguration": [
{
"Name": "Console"
Expand Down
6 changes: 3 additions & 3 deletions samples/Net8FromCodeSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
sinkConfiguration.BatchPostingLimit = 100;
sinkConfiguration.EmitEventFailure = EmitEventFailureHandling.WriteToFailureSink | EmitEventFailureHandling.WriteToSelfLog;
sinkConfiguration.RestrictedToMinimumLevel = LogEventLevel.Information;
sinkConfiguration.Period = TimeSpan.FromSeconds(2);
sinkConfiguration.BufferingTimeLimit = TimeSpan.FromSeconds(2);
sinkConfiguration.TextFormatter = new Serilog.Formatting.Json.JsonFormatter();

clientConfiguration.AutoCreateExchange = true;
clientConfiguration.DeliveryMode = RabbitMQDeliveryMode.Durable;
clientConfiguration.Exchange = "LogExchange";
clientConfiguration.ExchangeType = "fanout";
clientConfiguration.ExchangeType = "direct";
clientConfiguration.Hostnames = ["localhost"];
clientConfiguration.Password = "serilog";
clientConfiguration.Username = "serilog";
Expand All @@ -46,7 +46,7 @@
clientConfiguration.AutoCreateExchange = true;
clientConfiguration.DeliveryMode = RabbitMQDeliveryMode.Durable;
clientConfiguration.Exchange = "LogExchange";
clientConfiguration.ExchangeType = "fanout";
clientConfiguration.ExchangeType = "direct";
clientConfiguration.Hostnames = ["localhost"];
clientConfiguration.Password = "serilog";
clientConfiguration.Username = "serilog";
Expand Down
2 changes: 1 addition & 1 deletion samples/NetFrameworkAppSettingsConfigSample/App.config
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<add key="W:serilog:write-to:RabbitMQ.exchangeType" value="fanout" />
<add key="W:serilog:write-to:RabbitMQ.deliveryMode" value="Durable" />
<add key="W:serilog:write-to:RabbitMQ.batchPostingLimit" value="50" />
<add key="W:serilog:write-to:RabbitMQ.period" value="0.00:00:02.00" />
<add key="W:serilog:write-to:RabbitMQ.bufferingTimeLimit" value="0.00:00:02.00" />
<add key="W:serilog:write-to:RabbitMQ.queueLimit" value="10000" />
<add key="W:serilog:write-to:RabbitMQ.formatter" value="Serilog.Formatting.Json.JsonFormatter, Serilog" />
<add key="W:serilog:write-to:RabbitMQ.levelSwitch" value="Warning" />
Expand Down
Loading

0 comments on commit 8cec7bb

Please sign in to comment.