diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index ee340e3d..c560f1f0 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-format": {
- "version": "5.0.142902",
+ "version": "5.0.206801",
"commands": [
"dotnet-format"
]
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 4068214d..d09e6c67 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -23,7 +23,7 @@ jobs:
with:
dotnet-version: 5.0.x
- name: restore format
- run: dotnet tool install dotnet-format --version 5.0.142902+8012f4ede1bb9a4e90ac01efd204b0f1ee428f10 --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
+ run: dotnet tool restore --configfile NuGet.config
- name: check format
run: dotnet format --check ./Motor.NET.sln
diff --git a/Motor.NET.sln b/Motor.NET.sln
index e17d730d..311d8b80 100644
--- a/Motor.NET.sln
+++ b/Motor.NET.sln
@@ -88,10 +88,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{3D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsumeAndPublishWithRabbitMQ", "examples\ConsumeAndPublishWithRabbitMQ\ConsumeAndPublishWithRabbitMQ.csproj", "{946D0F5D-EC69-49A7-A7A9-16B4A0648247}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Metrics", "examples\Metrics\Metrics.csproj", "{66983F53-AA09-46B7-839C-F23BAE73433C}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetricsExample", "examples\MetricsExample\MetricsExample.csproj", "{66983F53-AA09-46B7-839C-F23BAE73433C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsumeAndPublishWithKafka", "examples\ConsumeAndPublishWithKafka\ConsumeAndPublishWithKafka.csproj", "{FA64170E-BF10-4670-84C6-1EF36E143E08}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsumeAndMultiOutputPublisherWithRabbitMQ", "examples\ConsumeAndMultiOutputPublisherWithRabbitMQ\ConsumeAndMultiOutputPublisherWithRabbitMQ.csproj", "{F8386DEA-8905-4157-B949-CC151F3FB8D2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetryExample", "examples\OpenTelemetryExample\OpenTelemetryExample.csproj", "{E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -558,6 +562,30 @@ Global
{FA64170E-BF10-4670-84C6-1EF36E143E08}.Release|x64.Build.0 = Release|Any CPU
{FA64170E-BF10-4670-84C6-1EF36E143E08}.Release|x86.ActiveCfg = Release|Any CPU
{FA64170E-BF10-4670-84C6-1EF36E143E08}.Release|x86.Build.0 = Release|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Debug|x64.Build.0 = Debug|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Debug|x86.Build.0 = Debug|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Release|x64.ActiveCfg = Release|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Release|x64.Build.0 = Release|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Release|x86.ActiveCfg = Release|Any CPU
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2}.Release|x86.Build.0 = Release|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Debug|x64.Build.0 = Debug|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Debug|x86.Build.0 = Debug|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Release|x64.ActiveCfg = Release|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Release|x64.Build.0 = Release|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Release|x86.ActiveCfg = Release|Any CPU
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -601,6 +629,8 @@ Global
{946D0F5D-EC69-49A7-A7A9-16B4A0648247} = {3DC7D216-6908-4759-B86F-759FDAE393D9}
{66983F53-AA09-46B7-839C-F23BAE73433C} = {3DC7D216-6908-4759-B86F-759FDAE393D9}
{FA64170E-BF10-4670-84C6-1EF36E143E08} = {3DC7D216-6908-4759-B86F-759FDAE393D9}
+ {F8386DEA-8905-4157-B949-CC151F3FB8D2} = {3DC7D216-6908-4759-B86F-759FDAE393D9}
+ {E7F2E446-E2F5-4AD9-8328-42F1B5B4E245} = {3DC7D216-6908-4759-B86F-759FDAE393D9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5E91C34C-3AEC-4084-BA02-753C9236AA34}
diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 00000000..47f01f5d
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Readme.md b/Readme.md
index 877b4433..c4e48b08 100644
--- a/Readme.md
+++ b/Readme.md
@@ -16,7 +16,9 @@ You find working examples for different use-cases under the [examples](./example
- [Consume and publish to RabbitMQ](./examples/ConsumeAndPublishWithRabbitMQ)
- [Consume and publish to Kafka](./examples/ConsumeAndPublishWithKafka)
-- [Create a service with metrics](./examples/Metrics)
+- [Consume and publish multiple messages at once to RabbitMQ](./examples/ConsumeAndMultiOutputPublishWithRabbitMQ)
+- [Create a service with metrics](./examples/MetricsExample)
+- [Create a service with custom traces](./examples/OpenTelemetryExample)
## Support Matrix
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/ConsumeAndMultiOutputPublisherWithRabbitMQ.csproj b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/ConsumeAndMultiOutputPublisherWithRabbitMQ.csproj
new file mode 100644
index 00000000..7640af21
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/ConsumeAndMultiOutputPublisherWithRabbitMQ.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net5.0
+ Motor.NET
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Model/InputMessage.cs b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Model/InputMessage.cs
new file mode 100644
index 00000000..7e1ff72b
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Model/InputMessage.cs
@@ -0,0 +1,8 @@
+namespace ConsumeAndMultiOutputPublisherWithRabbitMQ.Model
+{
+ public record InputMessage
+ {
+ public string FancyText { get; set; } = "FooBar";
+ public int FancyNumber { get; set; } = 42;
+ }
+}
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Model/OutputMessage.cs b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Model/OutputMessage.cs
new file mode 100644
index 00000000..5621f276
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Model/OutputMessage.cs
@@ -0,0 +1,8 @@
+namespace ConsumeAndMultiOutputPublisherWithRabbitMQ.Model
+{
+ public record OutputMessage
+ {
+ public string NotSoFancyText { get; set; }
+ public int NotSoFancyNumber { get; set; }
+ }
+}
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/MultiOutputService.cs b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/MultiOutputService.cs
new file mode 100644
index 00000000..49f84a3b
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/MultiOutputService.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using ConsumeAndMultiOutputPublisherWithRabbitMQ.Model;
+using Motor.Extensions.Hosting.Abstractions;
+
+namespace ConsumeAndMultiOutputPublisherWithRabbitMQ
+{
+ public class MultiOutputService : IMultiOutputService
+ {
+ // Handle incoming messages
+ public Task>> ConvertMessageAsync(
+ MotorCloudEvent inputEvent,
+ CancellationToken token = default)
+ {
+ // Get the input message from the cloud event
+ var input = inputEvent.TypedData;
+
+ // Do your magic here .....
+ var output = MagicFunc(input);
+
+ // Create a new cloud event from your output message which is automatically published and return a new task.
+ var outputEvent = output.Select(singleEvent => inputEvent.CreateNew(singleEvent));
+ return Task.FromResult(outputEvent);
+ }
+
+ private static IEnumerable MagicFunc(InputMessage input)
+ {
+ if (string.IsNullOrEmpty(input.FancyText))
+ {
+ // Reject message in RabbitMQ queue (Any ArgumentException can be used to reject to messages.).
+ throw new ArgumentNullException("FancyText is empty");
+ }
+
+ return new List
+ {
+ new()
+ {
+ NotSoFancyText = input.FancyText.Reverse().ToString(),
+ NotSoFancyNumber = input.FancyNumber * -1,
+ },
+ new()
+ {
+ NotSoFancyText = input.FancyText,
+ NotSoFancyNumber = input.FancyNumber * -2,
+ },
+ };
+ }
+ }
+}
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Program.cs b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Program.cs
new file mode 100644
index 00000000..8bfecc67
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Program.cs
@@ -0,0 +1,37 @@
+using ConsumeAndMultiOutputPublisherWithRabbitMQ;
+using ConsumeAndMultiOutputPublisherWithRabbitMQ.Model;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Motor.Extensions.Conversion.SystemJson;
+using Motor.Extensions.Hosting.Abstractions;
+using Motor.Extensions.Hosting.Consumer;
+using Motor.Extensions.Hosting.Publisher;
+using Motor.Extensions.Hosting.RabbitMQ;
+using Motor.Extensions.Utilities;
+
+await MotorHost.CreateDefaultBuilder()
+ // Configure the types of the input and output messages
+ .ConfigureMultiOutputService()
+ .ConfigureServices((_, services) =>
+ {
+ // Add a handler for the input message which returns an output message
+ // This handler is called for every new incoming message
+ services.AddTransient, MultiOutputService>();
+ })
+ // Add the incomming communication module.
+ .ConfigureConsumer((_, builder) =>
+ {
+ // In this case the messages are received from RabbitMQ
+ builder.AddRabbitMQ();
+ // The encoding of the incoming message, such that the handler is able to deserialize the message
+ builder.AddSystemJson();
+ })
+ // Add the outgoing communication module.
+ .ConfigurePublisher((_, builder) =>
+ {
+ // In this case the messages are send to RabbitMQ
+ builder.AddRabbitMQ();
+ // The encoding of the outgoing message, such that the handler is able to serialize the message
+ builder.AddSystemJson();
+ })
+ .RunConsoleAsync();
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Properties/launchSettings.json b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Properties/launchSettings.json
new file mode 100644
index 00000000..d16586d2
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:58904/",
+ "sslPort": 44350
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "ConsumeAndPublishWithRabbitMQ": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/appsettings.Production.json b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/appsettings.Production.json
new file mode 100644
index 00000000..8a646989
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/appsettings.Production.json
@@ -0,0 +1,17 @@
+{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Information"
+ }
+ },
+ "RabbitMQConsumer": {
+ "Queue": {
+ "Name": "ExampleProductionQueue"
+ }
+ },
+ "RabbitMQPublisher": {
+ "PublishingTarget": {
+ "RoutingKey": "production"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/appsettings.json b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/appsettings.json
new file mode 100644
index 00000000..e773aac7
--- /dev/null
+++ b/examples/ConsumeAndMultiOutputPublisherWithRabbitMQ/appsettings.json
@@ -0,0 +1,38 @@
+{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information",
+ "System": "Warning"
+ }
+ }
+ },
+ "RabbitMQConsumer": {
+ "Host": "localhost",
+ "VirtualHost": "/",
+ "User": "guest",
+ "Password": "guest",
+ "Queue": {
+ "Name": "ExampleQueue",
+ "Bindings": [
+ {
+ "Exchange": "amq.topic",
+ "RoutingKey": "input"
+ }
+ ]
+ },
+ "PrefetchCount": 10
+ },
+ "RabbitMQPublisher": {
+ "Host": "localhost",
+ "VirtualHost": "/",
+ "User": "guest",
+ "Password": "guest",
+ "PublishingTarget": {
+ "Exchange": "amq.topic",
+ "RoutingKey": "ouput"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/Metrics/DifferentNamespace/ServiceInDifferentNamespace.cs b/examples/MetricsExample/DifferentNamespace/ServiceInDifferentNamespace.cs
similarity index 81%
rename from examples/Metrics/DifferentNamespace/ServiceInDifferentNamespace.cs
rename to examples/MetricsExample/DifferentNamespace/ServiceInDifferentNamespace.cs
index b6e4564b..335f13dc 100644
--- a/examples/Metrics/DifferentNamespace/ServiceInDifferentNamespace.cs
+++ b/examples/MetricsExample/DifferentNamespace/ServiceInDifferentNamespace.cs
@@ -1,7 +1,7 @@
using Motor.Extensions.Diagnostics.Metrics.Abstractions;
using Prometheus.Client.Abstractions;
-namespace Metrics.DifferentNamespace
+namespace MetricsExample.DifferentNamespace
{
public interface IServiceInDifferentNamespace
{
@@ -14,7 +14,7 @@ public class ServiceInDifferentNamespace : IServiceInDifferentNamespace
public ServiceInDifferentNamespace(IMetricsFactory? metricsFactory)
{
- // Resulting label in Prometheus: metrics_differentnamespace_counter_in_different_namespace
+ // Resulting label in Prometheus: metricsexample_differentnamespace_counter_in_different_namespace
_counter = metricsFactory?.CreateCounter("counter_in_different_namespace", "This counts something else.");
}
diff --git a/examples/Metrics/Metrics.csproj b/examples/MetricsExample/MetricsExample.csproj
similarity index 100%
rename from examples/Metrics/Metrics.csproj
rename to examples/MetricsExample/MetricsExample.csproj
diff --git a/examples/Metrics/Model/InputMessage.cs b/examples/MetricsExample/Model/InputMessage.cs
similarity index 83%
rename from examples/Metrics/Model/InputMessage.cs
rename to examples/MetricsExample/Model/InputMessage.cs
index 4dd178f9..5fc3680d 100644
--- a/examples/Metrics/Model/InputMessage.cs
+++ b/examples/MetricsExample/Model/InputMessage.cs
@@ -1,4 +1,4 @@
-namespace Metrics.Model
+namespace MetricsExample.Model
{
public record InputMessage
{
diff --git a/examples/Metrics/Program.cs b/examples/MetricsExample/Program.cs
similarity index 92%
rename from examples/Metrics/Program.cs
rename to examples/MetricsExample/Program.cs
index caa32ed5..8091ce3f 100644
--- a/examples/Metrics/Program.cs
+++ b/examples/MetricsExample/Program.cs
@@ -1,6 +1,6 @@
-using Metrics;
-using Metrics.DifferentNamespace;
-using Metrics.Model;
+using MetricsExample;
+using MetricsExample.DifferentNamespace;
+using MetricsExample.Model;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Motor.Extensions.Conversion.SystemJson;
diff --git a/examples/Metrics/Properties/launchSettings.json b/examples/MetricsExample/Properties/launchSettings.json
similarity index 100%
rename from examples/Metrics/Properties/launchSettings.json
rename to examples/MetricsExample/Properties/launchSettings.json
diff --git a/examples/Metrics/ServiceWithMetrics.cs b/examples/MetricsExample/ServiceWithMetrics.cs
similarity index 87%
rename from examples/Metrics/ServiceWithMetrics.cs
rename to examples/MetricsExample/ServiceWithMetrics.cs
index a1092126..3c9cbf67 100644
--- a/examples/Metrics/ServiceWithMetrics.cs
+++ b/examples/MetricsExample/ServiceWithMetrics.cs
@@ -1,12 +1,12 @@
using System.Threading;
using System.Threading.Tasks;
-using Metrics.DifferentNamespace;
-using Metrics.Model;
+using MetricsExample.DifferentNamespace;
+using MetricsExample.Model;
using Motor.Extensions.Diagnostics.Metrics.Abstractions;
using Motor.Extensions.Hosting.Abstractions;
using Prometheus.Client.Abstractions;
-namespace Metrics
+namespace MetricsExample
{
public class ServiceWithMetrics : INoOutputService
{
@@ -19,11 +19,11 @@ public ServiceWithMetrics(IMetricsFactory metricFactory,
{
_serviceInDifferentNamespace = serviceInDifferentNamespace;
- // Resulting label in Prometheus: metrics_emtpy_string_total
+ // Resulting label in Prometheus: metricsexample_emtpy_string_total
_counter = metricFactory?.CreateCounter("empty_string_total",
"Counts the total number of recieved empty strings.");
- // Resulting label in Prometheus: metrics_fancy_number
+ // Resulting label in Prometheus: metricsexample_fancy_number
_summary = metricFactory?.CreateSummary("fancy_number",
"Shows the distribution of fancy numbers.");
}
diff --git a/examples/Metrics/appsettings.Production.json b/examples/MetricsExample/appsettings.Production.json
similarity index 100%
rename from examples/Metrics/appsettings.Production.json
rename to examples/MetricsExample/appsettings.Production.json
diff --git a/examples/Metrics/appsettings.json b/examples/MetricsExample/appsettings.json
similarity index 100%
rename from examples/Metrics/appsettings.json
rename to examples/MetricsExample/appsettings.json
diff --git a/examples/OpenTelemetryExample/Model/InputMessage.cs b/examples/OpenTelemetryExample/Model/InputMessage.cs
new file mode 100644
index 00000000..67189c66
--- /dev/null
+++ b/examples/OpenTelemetryExample/Model/InputMessage.cs
@@ -0,0 +1,8 @@
+namespace OpenTelemetryExample.Model
+{
+ public record InputMessage
+ {
+ public string FancyText { get; set; } = "FooBar";
+ public int FancyNumber { get; set; } = 42;
+ }
+}
diff --git a/examples/OpenTelemetryExample/Model/OutputMessage.cs b/examples/OpenTelemetryExample/Model/OutputMessage.cs
new file mode 100644
index 00000000..f3101f65
--- /dev/null
+++ b/examples/OpenTelemetryExample/Model/OutputMessage.cs
@@ -0,0 +1,8 @@
+namespace OpenTelemetryExample.Model
+{
+ public record OutputMessage
+ {
+ public string NotSoFancyText { get; set; }
+ public int NotSoFancyNumber { get; set; }
+ }
+}
diff --git a/examples/OpenTelemetryExample/OpenTelemetryExample.csproj b/examples/OpenTelemetryExample/OpenTelemetryExample.csproj
new file mode 100644
index 00000000..36f1408e
--- /dev/null
+++ b/examples/OpenTelemetryExample/OpenTelemetryExample.csproj
@@ -0,0 +1,27 @@
+
+
+
+ Exe
+ net5.0
+ Motor.NET
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/examples/OpenTelemetryExample/Program.cs b/examples/OpenTelemetryExample/Program.cs
new file mode 100644
index 00000000..9d4ff3e8
--- /dev/null
+++ b/examples/OpenTelemetryExample/Program.cs
@@ -0,0 +1,37 @@
+using OpenTelemetryExample;
+using OpenTelemetryExample.Model;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Motor.Extensions.Conversion.SystemJson;
+using Motor.Extensions.Hosting.Abstractions;
+using Motor.Extensions.Hosting.Consumer;
+using Motor.Extensions.Hosting.Publisher;
+using Motor.Extensions.Hosting.RabbitMQ;
+using Motor.Extensions.Utilities;
+
+await MotorHost.CreateDefaultBuilder()
+ // Configure the types of the input and output messages
+ .ConfigureSingleOutputService()
+ .ConfigureServices((_, services) =>
+ {
+ // Add a handler for the input message which returns an output message
+ // This handler is called for every new incoming message
+ services.AddTransient, SingleOutputService>();
+ })
+ // Add the incomming communication module.
+ .ConfigureConsumer((_, builder) =>
+ {
+ // In this case the messages are received from RabbitMQ
+ builder.AddRabbitMQ();
+ // The encoding of the incoming message, such that the handler is able to deserialize the message
+ builder.AddSystemJson();
+ })
+ // Add the outgoing communication module.
+ .ConfigurePublisher((_, builder) =>
+ {
+ // In this case the messages are send to RabbitMQ
+ builder.AddRabbitMQ();
+ // The encoding of the outgoing message, such that the handler is able to serialize the message
+ builder.AddSystemJson();
+ })
+ .RunConsoleAsync();
diff --git a/examples/OpenTelemetryExample/Properties/launchSettings.json b/examples/OpenTelemetryExample/Properties/launchSettings.json
new file mode 100644
index 00000000..d16586d2
--- /dev/null
+++ b/examples/OpenTelemetryExample/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:58904/",
+ "sslPort": 44350
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "ConsumeAndPublishWithRabbitMQ": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/OpenTelemetryExample/SingleOutputService.cs b/examples/OpenTelemetryExample/SingleOutputService.cs
new file mode 100644
index 00000000..3fdfb33f
--- /dev/null
+++ b/examples/OpenTelemetryExample/SingleOutputService.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using CloudNative.CloudEvents.Extensions;
+using Motor.Extensions.Diagnostics.Tracing;
+using OpenTelemetryExample.Model;
+using Motor.Extensions.Hosting.Abstractions;
+
+namespace OpenTelemetryExample
+{
+ public class SingleOutputService : ISingleOutputService
+ {
+ private static readonly ActivitySource ActivitySource = new(nameof(OpenTelemetryExample));
+
+ // Handle incoming messages
+ public Task> ConvertMessageAsync(
+ MotorCloudEvent inputEvent,
+ CancellationToken token = default)
+ {
+ // Get the input message from the cloud event
+ var input = inputEvent.TypedData;
+
+ if (string.IsNullOrEmpty(input.FancyText))
+ {
+ // Reject message in RabbitMQ queue (Any ArgumentException can be used to reject to messages.).
+ throw new ArgumentNullException("FancyText is empty");
+ }
+
+ // Extract ActivityContext from the incoming CloudEvent
+ var parentContext = inputEvent.Extension()?.GetActivityContext() ?? default;
+
+ // Create new Activity with extracted ActivityContext as parent
+ using var activity =
+ ActivitySource.StartActivity(nameof(MagicFunc), ActivityKind.Consumer, parentContext);
+ activity?.SetTag("fancyText", input.FancyText);
+
+ // Add Trace for MagicFunc with a tag
+ OutputMessage output;
+ using (activity?.Start())
+ {
+ // Do your magic here .....
+ output = MagicFunc(input);
+ }
+
+ // Create a new cloud event from your output message which is automatically published and return a new task.
+ var outputEvent = inputEvent.CreateNew(output);
+ return Task.FromResult(outputEvent);
+ }
+
+ private static OutputMessage MagicFunc(InputMessage input)
+ {
+ var output = new OutputMessage
+ {
+ NotSoFancyText = input.FancyText.Reverse().ToString(),
+ NotSoFancyNumber = input.FancyNumber * -1,
+ };
+ return output;
+ }
+ }
+}
diff --git a/examples/OpenTelemetryExample/appsettings.Development.json b/examples/OpenTelemetryExample/appsettings.Development.json
new file mode 100644
index 00000000..4c4df39c
--- /dev/null
+++ b/examples/OpenTelemetryExample/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Information"
+ }
+ },
+ "JaegerExporter": {
+ "AgentHost": "localhost"
+ }
+}
\ No newline at end of file
diff --git a/examples/OpenTelemetryExample/appsettings.Production.json b/examples/OpenTelemetryExample/appsettings.Production.json
new file mode 100644
index 00000000..050047f7
--- /dev/null
+++ b/examples/OpenTelemetryExample/appsettings.Production.json
@@ -0,0 +1,20 @@
+{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Information"
+ }
+ },
+ "OpenTelemetry": {
+ "SamplingProbability": 0.001
+ },
+ "RabbitMQConsumer": {
+ "Queue": {
+ "Name": "ExampleProductionQueue"
+ }
+ },
+ "RabbitMQPublisher": {
+ "PublishingTarget": {
+ "RoutingKey": "production"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/OpenTelemetryExample/appsettings.json b/examples/OpenTelemetryExample/appsettings.json
new file mode 100644
index 00000000..64f53acd
--- /dev/null
+++ b/examples/OpenTelemetryExample/appsettings.json
@@ -0,0 +1,45 @@
+{
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information",
+ "System": "Warning"
+ }
+ }
+ },
+ "JaegerExporter": {
+ "AgentHost": "agent-host.localhost"
+ },
+ "OpenTelemetry": {
+ "SamplingProbability": 1,
+ "Sources": ["OpenTelemetryExample", "MotorNet.OpenTelemetry"]
+ },
+ "RabbitMQConsumer": {
+ "Host": "localhost",
+ "VirtualHost": "/",
+ "User": "guest",
+ "Password": "guest",
+ "Queue": {
+ "Name": "ExampleQueue",
+ "Bindings": [
+ {
+ "Exchange": "amq.topic",
+ "RoutingKey": "input"
+ }
+ ]
+ },
+ "PrefetchCount": 10
+ },
+ "RabbitMQPublisher": {
+ "Host": "localhost",
+ "VirtualHost": "/",
+ "User": "guest",
+ "Password": "guest",
+ "PublishingTarget": {
+ "Exchange": "amq.topic",
+ "RoutingKey": "ouput"
+ }
+ }
+}
\ No newline at end of file