Skip to content

Commit

Permalink
V5.1.0 (#76)
Browse files Browse the repository at this point in the history
* feature(##72) Improve Logging: Fix Duplicate Entries, Implement High-Performance, Structured #72

* Exposed `HandlerType` in `MessageReceptionBuilder`

* v5.1.0

---------

Co-authored-by: Mohamed Amine Bchir <[email protected]>
Co-authored-by: Anis Tissaoui <[email protected]>
Co-authored-by: Benjamin SPETH <[email protected]>
  • Loading branch information
4 people authored Aug 23, 2024
1 parent 42bc53a commit 96d8322
Show file tree
Hide file tree
Showing 25 changed files with 589 additions and 136 deletions.
7 changes: 7 additions & 0 deletions Ev.ServiceBus.sln
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ev.ServiceBus.AsyncApi", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ev.ServiceBus.AsyncApi.UnitTests", "tests\Ev.ServiceBus.AsyncApi.UnitTests\Ev.ServiceBus.AsyncApi.UnitTests.csproj", "{6C298506-031B-46BE-9F51-83D2D0209C71}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ev.ServiceBus.Apm", "src\Ev.ServiceBus.Apm\Ev.ServiceBus.Apm.csproj", "{A193CD34-7193-495B-9320-60B41892BC94}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -115,6 +117,10 @@ Global
{6C298506-031B-46BE-9F51-83D2D0209C71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C298506-031B-46BE-9F51-83D2D0209C71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C298506-031B-46BE-9F51-83D2D0209C71}.Release|Any CPU.Build.0 = Release|Any CPU
{A193CD34-7193-495B-9320-60B41892BC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A193CD34-7193-495B-9320-60B41892BC94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A193CD34-7193-495B-9320-60B41892BC94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A193CD34-7193-495B-9320-60B41892BC94}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{4C7DEB15-4C1B-4534-AE17-94E8515EB7EC} = {8EC286EB-A71F-4431-ACA4-E92C79975817}
Expand All @@ -131,5 +137,6 @@ Global
{C174493E-8890-4C67-8C77-971DE935AD17} = {C4692485-7C68-42B7-A35E-601EBFCE3725}
{2F843D4F-D177-4F71-A49B-E5C789936A93} = {8EC286EB-A71F-4431-ACA4-E92C79975817}
{6C298506-031B-46BE-9F51-83D2D0209C71} = {C4692485-7C68-42B7-A35E-601EBFCE3725}
{A193CD34-7193-495B-9320-60B41892BC94} = {8EC286EB-A71F-4431-ACA4-E92C79975817}
EndGlobalSection
EndGlobal
34 changes: 34 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 5.1.0
- Added
- MessageReceptionBuilder
- Exposed `HandlerType` in `MessageReceptionBuilder`.
- Exposed `PayloadType` in `MessageReceptionBuilder`.
- new categorized Logging
- Ev.ServiceBus.LoggingExtensions.MessageProcessing : For logs related to message processing
- Ev.ServiceBus.LoggingExtensions.ServiceBusClientManagement : For logs related to creation of client / disposition of clients
- Ev.ServiceBus.LoggingExtensions.ServiceBusEngine : For logs related to the initialization of the Host
- Ev.ServiceBus.HealthChecks.LoggingExtensionsHealthChecks : For logs related to service bus health checks registration
- New telemetryOptions
- ActivitySource (Ev.ServiceBus) for message processing
- Ev.ServiceBus.Apm : Elastic Apm integration
- Modified
- Reduce number of log entries and duplicate exception logging
- Use of high performance logging mechanism

`[Breaking]`
The exception catched during IMessageHandler was changed to FailedToProcessMessageException
Original exception is stored in the inner exception if you are using IExceptionHandler use this to get original message

```csharp
public class CustomExceptionHandler : IExceptionHandler
{
public Task HandleExceptionAsync(ProcessErrorEventArgs args)
{
var original = exceptionEvent.Exception is FailedToProcessMessageException wrapperException
? wrapperException.InnerException!
: exceptionEvent.Exception!;
return Task.CompletedTask;
}
}
```

## 5.0.0
- Removed obsolete methods and related code :
- `services.RegisterServiceBusQueue("queueName");`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ public class MessageReceptionBuilder
{
private readonly MessageReceptionRegistration _registration;

public Type HandlerType => _registration.HandlerType;

public Type PayloadType => _registration.PayloadType;

public MessageReceptionBuilder(ClientOptions clientOptions, Type payloadType, Type handlerType)
{
_registration = new MessageReceptionRegistration(clientOptions, payloadType, handlerType);
Expand Down
12 changes: 12 additions & 0 deletions src/Ev.ServiceBus.Abstractions/Listeners/ITransactionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
using Ev.ServiceBus.Abstractions.MessageReception;

namespace Ev.ServiceBus.Abstractions.Listeners;

public interface ITransactionManager
{
public Task RunWithInTransaction(
MessageExecutionContext executionContext,
Func<Task> transaction);
}
41 changes: 41 additions & 0 deletions src/Ev.ServiceBus.Abstractions/MessageReception/MessageContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Threading;
using Azure.Messaging.ServiceBus;
using Ev.ServiceBus.Abstractions.Extensions;
using Ev.ServiceBus.Abstractions.MessageReception;

// ReSharper disable once CheckNamespace
namespace Ev.ServiceBus.Abstractions;
Expand Down Expand Up @@ -35,4 +37,43 @@ public MessageContext(ProcessMessageEventArgs args, ClientType clientType, strin

public string? PayloadTypeId { get; internal set; }
public MessageReceptionRegistration? ReceptionRegistration { get; internal set; }

public MessageExecutionContext ReadExecutionContext()
{
var clientType = GetClientType();
var contextResourceId = GetContextResourceId();
var messageMessageId = GetMessageId();
var contextPayloadTypeId = GetPayloadTypeId();
var sessionArgsSessionId = GetSessionId();
var handlerTypeFullName = GetHandlerTypeFullName();

return new MessageExecutionContext
{
ClientType = clientType,
ResourceId = contextResourceId,
MessageId = messageMessageId,
PayloadTypeId = contextPayloadTypeId,
SessionId = sessionArgsSessionId,
HandlerName = handlerTypeFullName,
DiagnosticId = Message.GetDiagnosticId()
};
}

private string GetHandlerTypeFullName()
=> ReceptionRegistration?.HandlerType.FullName ?? "none";

private string GetSessionId()
=> SessionArgs?.SessionId ?? "none";

private string GetPayloadTypeId()
=> PayloadTypeId ?? "none";

private string GetMessageId()
=> Message.MessageId;

private string GetContextResourceId()
=> ResourceId;

private string GetClientType()
=> ClientType.ToString();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Ev.ServiceBus.Abstractions.MessageReception;

public class MessageExecutionContext
{
public string? ClientType { get; set; }
public string? ResourceId { get; set; }
public string? PayloadTypeId { get; set; }
public string? MessageId { get; set; }
public string? SessionId { get; set; }
public string? HandlerName { get; set; }

public string? DiagnosticId { get; set; }

public string ExecutionName => $"{ClientType}/{ResourceId}/{PayloadTypeId}";
}
43 changes: 43 additions & 0 deletions src/Ev.ServiceBus.Apm/ApmTransactionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Elastic.Apm;
using Elastic.Apm.Api;
using Ev.ServiceBus.Abstractions.Listeners;
using Ev.ServiceBus.Abstractions.MessageReception;

namespace Ev.ServiceBus.Apm;

public class ApmTransactionManager : ITransactionManager
{
public async Task RunWithInTransaction(MessageExecutionContext executionContext, Func<Task> transaction)
{
if (IsTraceEnabled())
{
await Agent.Tracer.CaptureTransaction(
executionContext.ExecutionName,
ApiConstants.TypeMessaging,
async () =>
{
Agent.Tracer.CurrentTransaction?
.SetLabel(nameof(executionContext.ClientType), executionContext.ClientType);
Agent.Tracer.CurrentTransaction?
.SetLabel(nameof(executionContext.ResourceId), executionContext.ResourceId);
Agent.Tracer.CurrentTransaction?
.SetLabel(nameof(executionContext.PayloadTypeId), executionContext.PayloadTypeId);
Agent.Tracer.CurrentTransaction?
.SetLabel(nameof(executionContext.HandlerName), executionContext.HandlerName);
Agent.Tracer.CurrentTransaction?
.SetLabel(nameof(executionContext.SessionId), executionContext.SessionId);
Agent.Tracer.CurrentTransaction?
.SetLabel(nameof(executionContext.MessageId), executionContext.MessageId);
await transaction();
},
DistributedTracingData.TryDeserializeFromString(executionContext.DiagnosticId));
}
else
{
await transaction();
}
}

private static bool IsTraceEnabled()
=> Agent.IsConfigured && Agent.Config.Enabled && Agent.Tracer is not null;
}
18 changes: 18 additions & 0 deletions src/Ev.ServiceBus.Apm/Ev.ServiceBus.Apm.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Ev.Servicebus.Apm</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Ev.ServiceBus\Ev.ServiceBus.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Elastic.Apm" Version="1.19.0" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions src/Ev.ServiceBus.Apm/ServiceBusBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Ev.ServiceBus.Abstractions.Listeners;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Ev.ServiceBus.Apm;

public static class ServiceBusBuilderExtensions
{
public static ServiceBusBuilder UseApm(this ServiceBusBuilder builder)
{
builder.Services.RemoveAll<ITransactionManager>();
builder.Services.AddSingleton<ITransactionManager, ApmTransactionManager>();
return builder;
}
}
20 changes: 20 additions & 0 deletions src/Ev.ServiceBus.HealthChecks/LoggingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using Microsoft.Extensions.Logging;

namespace Ev.ServiceBus.HealthChecks;

public static class LoggingExtensions
{
public record HealthChecks();

private static readonly Action<ILogger, string, string, Exception?> LogAddingHealthCheck =
LoggerMessage.Define<string, string>(
LogLevel.Information,
new EventId(1, nameof(AddingHealthCheck)),
"Adding health check for {EVSB_Client} {EVSB_ResourceId}"
);

public static void AddingHealthCheck(this ILogger logger, string resourceId, string client)
=> LogAddingHealthCheck(logger,client, resourceId, default);

}
10 changes: 5 additions & 5 deletions src/Ev.ServiceBus.HealthChecks/RegistrationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ namespace Ev.ServiceBus.HealthChecks;

public class RegistrationService : IConfigureOptions<HealthCheckServiceOptions>
{
private readonly ILogger<RegistrationService> _logger;
private readonly ILogger<LoggingExtensions.HealthChecks> _logger;
private readonly IOptions<ServiceBusOptions> _serviceBusOptions;

public RegistrationService(IOptions<ServiceBusOptions> serviceBusOptions, ILogger<RegistrationService> logger)
public RegistrationService(IOptions<ServiceBusOptions> serviceBusOptions, ILogger<LoggingExtensions.HealthChecks> logger)
{
_serviceBusOptions = serviceBusOptions;
_logger = logger;
Expand Down Expand Up @@ -41,7 +41,7 @@ public void Configure(HealthCheckServiceOptions options)
var queues = resourceGroup.Where(o => o is QueueOptions).Cast<QueueOptions>().GroupBy(o => o.QueueName.ToLower());
foreach (var group in queues)
{
_logger.LogInformation("[Ev.ServiceBus.HealthChecks] Adding health check for {ResourceType} {ResourceName}", "queue", group.Key);
_logger.AddingHealthCheck("Queue", group.Key);
options.Registrations.Add(new HealthCheckRegistration($"Queue:{group.Key}",
sp => (IHealthCheck) new AzureServiceBusQueueHealthCheck(connectionString, group.Key),
null, HealthChecksBuilderExtensions.HealthCheckTags, null));
Expand All @@ -50,7 +50,7 @@ public void Configure(HealthCheckServiceOptions options)
var topics = resourceGroup.Where(o => o is TopicOptions).Cast<TopicOptions>().GroupBy(o => o.TopicName.ToLower());
foreach (var group in topics)
{
_logger.LogInformation("[Ev.ServiceBus.HealthChecks] Adding health check for {ResourceType} {ResourceName}", "topic", group.Key);
_logger.AddingHealthCheck("Topic", group.Key);
options.Registrations.Add(new HealthCheckRegistration($"Topic:{group.Key}",
sp => (IHealthCheck) new AzureServiceBusTopicHealthCheck(connectionString, group.Key),
null, HealthChecksBuilderExtensions.HealthCheckTags, null));
Expand All @@ -62,7 +62,7 @@ public void Configure(HealthCheckServiceOptions options)
.GroupBy(o => new { TopicName = o.TopicName.ToLower(), SubscriptionName = o.SubscriptionName.ToLower() });
foreach (var group in subscriptions)
{
_logger.LogInformation("[Ev.ServiceBus.HealthChecks] Adding health check for {ResourceType} {ResourceName} (related to topic {TopicName})", "subscription", group.Key.SubscriptionName, group.Key.TopicName);
_logger.AddingHealthCheck("Subscription", $"{group.Key.TopicName}/Subscriptions/{group.Key.SubscriptionName}");
options.Registrations.Add(new HealthCheckRegistration($"Subscription:{group.Key.TopicName}/Subscriptions/{group.Key.SubscriptionName}",
sp => (IHealthCheck) new AzureServiceBusSubscriptionHealthCheck(connectionString,
group.Key.TopicName, group.Key.SubscriptionName),
Expand Down
2 changes: 1 addition & 1 deletion src/Ev.ServiceBus/Dispatch/DispatchSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private async Task BatchAndSendMessages(MessagesPerResource dispatches, Cancella
continue;
}

throw new ArgumentOutOfRangeException($"A message is too big to fit in a single batch");
throw new ArgumentOutOfRangeException("A message is too big to fit in a single batch");
}

foreach (var pageMessages in batches)
Expand Down
21 changes: 21 additions & 0 deletions src/Ev.ServiceBus/Exceptions/FailedToProcessMessageException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Ev.ServiceBus.Exceptions;

public class FailedToProcessMessageException(
string? clientType,
string? resourceId,
string? messageId,
string? payloadTypeId,
string? sessionId,
string? handlerName,
Exception innerException)
: Exception("Failed to process Message", innerException)
{
public string? ClientType { get; } = clientType;
public string? ResourceId { get; } = resourceId;
public string? MessageId { get; } = messageId;
public string? PayloadTypeId { get; } = payloadTypeId;
public string? SessionId { get; } = sessionId;
public string? HandlerName { get; } = handlerName;
}
Loading

0 comments on commit 96d8322

Please sign in to comment.