Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shared Exceptions - AuditEventType & Non-Exception Overloads #69

Merged
merged 4 commits into from
Mar 21, 2024
Merged
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
17 changes: 14 additions & 3 deletions Shared/Application/Error/Exceptions/DeadLetterException.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
namespace LantanaGroup.Link.Shared.Application.Error.Exceptions
using LantanaGroup.Link.Shared.Application.Models;

namespace LantanaGroup.Link.Shared.Application.Error.Exceptions
{
public class DeadLetterException : Exception
{
public DeadLetterException(string message) : base(message) { }
public DeadLetterException(string message, Exception? innerEx) : base(message, innerEx) { }
public AuditEventType AuditEventType { get; set; }

public DeadLetterException(string message, AuditEventType auditEventType) : base(message)
{
AuditEventType = auditEventType;
}

public DeadLetterException(string message, AuditEventType auditEventType, Exception? innerEx) : base(message, innerEx)
{
AuditEventType = auditEventType;
}
}
}
17 changes: 14 additions & 3 deletions Shared/Application/Error/Exceptions/TransientException.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
namespace LantanaGroup.Link.Shared.Application.Error.Exceptions
using LantanaGroup.Link.Shared.Application.Models;

namespace LantanaGroup.Link.Shared.Application.Error.Exceptions
{
public class TransientException : Exception
{
public TransientException(string message) : base(message) { }
public TransientException(string message, Exception? innerEx) : base(message, innerEx) { }
public AuditEventType AuditEventType { get; set; }

public TransientException(string message, AuditEventType auditEventType) : base(message)
{
AuditEventType = auditEventType;
}

public TransientException(string message, AuditEventType auditEventType, Exception? innerEx) : base(message, innerEx)
{
AuditEventType = auditEventType;
}
}
}
53 changes: 46 additions & 7 deletions Shared/Application/Error/Handlers/DeadLetterExceptionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using LantanaGroup.Link.Shared.Application.Models.Kafka;
using Microsoft.Extensions.Logging;
using System.Text;
using LantanaGroup.Link.Shared.Application.Error.Exceptions;

namespace LantanaGroup.Link.Shared.Application.Error.Handlers
{
Expand All @@ -27,33 +28,71 @@ public DeadLetterExceptionHandler(ILogger<DeadLetterExceptionHandler<K, V>> logg
ProducerFactory = producerFactory;
}

public virtual void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, string facilityId)
public void HandleException(ConsumeResult<K, V> consumeResult, string facilityId, AuditEventType auditEventType, string message = "")
{
try
{
message = message ?? "";
if (consumeResult == null)
{
Logger.LogError(message: $"DeadLetterExceptionHandler|{ServiceName}|{Topic}: consumeResult is null, cannot produce Audit or DeadLetter events", exception: ex);
Logger.LogError($"{GetType().Name}|{ServiceName}|{Topic}: consumeResult is null, cannot produce Audit or DeadLetter events: " + message);
return;
}

Logger.LogError(message: $"DeadLetterExceptionHandler: Failed to process {ServiceName} Event.", exception: ex);
Logger.LogError($"{GetType().Name}: Failed to process {ServiceName} Event: " + message);

var auditValue = new AuditEventMessage
{
FacilityId = facilityId,
Action = AuditEventType.Query,
Action = auditEventType,
ServiceName = ServiceName,
EventDate = DateTime.UtcNow,
Notes = $"DeadLetterExceptionHandler: processing failure in {ServiceName} \nException Message: {ex.Message}",
Notes = $"{GetType().Name}: processing failure in {ServiceName} \nException Message: {message}",
};

ProduceAuditEvent(auditValue, consumeResult.Message.Headers);
ProduceDeadLetter(consumeResult.Message.Key, consumeResult.Message.Value, consumeResult.Message.Headers, message);
}
catch (Exception e)
{
Logger.LogError(e, $"Error in {GetType().Name}.HandleException: " + e.Message);
throw;
}
}

public virtual void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, AuditEventType auditEventType, string facilityId)
{
var dlEx = new DeadLetterException(ex.Message, auditEventType, ex.InnerException);
HandleException(consumeResult, dlEx, facilityId);
}

public virtual void HandleException(ConsumeResult<K, V> consumeResult, DeadLetterException ex, string facilityId)
{
try
{
if (consumeResult == null)
{
Logger.LogError(message: $"{GetType().Name}|{ServiceName}|{Topic}: consumeResult is null, cannot produce Audit or DeadLetter events", exception: ex);
return;
}

Logger.LogError(message: $"{GetType().Name}: Failed to process {ServiceName} Event.", exception: ex);

var auditValue = new AuditEventMessage
{
FacilityId = facilityId,
Action = ex.AuditEventType,
ServiceName = ServiceName,
EventDate = DateTime.UtcNow,
Notes = $"{GetType().Name}: processing failure in {ServiceName} \nException Message: {ex.Message}",
};

ProduceAuditEvent(auditValue, consumeResult.Message.Headers);
ProduceDeadLetter(consumeResult.Message.Key, consumeResult.Message.Value, consumeResult.Message.Headers, ex.Message);
}
catch (Exception e)
{
Logger.LogError(e,"Error in DeadLetterExceptionHandler.HandleException: " + e.Message);
Logger.LogError(e, $"Error in {GetType().Name}.HandleException: " + e.Message);
throw;
}
}
Expand All @@ -74,7 +113,7 @@ public virtual void ProduceDeadLetter(K key, V value, Headers headers, string ex
if (string.IsNullOrWhiteSpace(Topic))
{
throw new Exception(
$"DeadLetterExceptionHandler.Topic has not been configured. Cannot Produce Dead Letter Event for {ServiceName}");
$"{GetType().Name}.Topic has not been configured. Cannot Produce Dead Letter Event for {ServiceName}");
}

headers.Add("X-Exception-Message", Encoding.UTF8.GetBytes(exceptionMessage));
Expand Down
54 changes: 47 additions & 7 deletions Shared/Application/Error/Handlers/TransientExceptionHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Confluent.Kafka;
using LantanaGroup.Link.Shared.Application.Error.Exceptions;
using LantanaGroup.Link.Shared.Application.Error.Interfaces;
using LantanaGroup.Link.Shared.Application.Interfaces;
using LantanaGroup.Link.Shared.Application.Models;
Expand Down Expand Up @@ -26,25 +27,26 @@ public TransientExceptionHandler(ILogger<TransientExceptionHandler<K, V>> logger
ProducerFactory = producerFactory;
}

public virtual void HandleException(ConsumeResult<K, V>? consumeResult, Exception ex, string facilityId)
public void HandleException(ConsumeResult<K, V> consumeResult, string facilityId, AuditEventType auditEventType, string message = "")
{
try
{
message = message ?? "";
if (consumeResult == null)
{
Logger.LogError(message: $"TransientExceptionHandler|{ServiceName}|{Topic}: consumeResult is null, cannot produce Audit or Retry events", exception: ex);
Logger.LogError($"{GetType().Name}|{ServiceName}|{Topic}: consumeResult is null, cannot produce Audit or Retry events: " + message);
return;
}

Logger.LogError(message: $"TransientExceptionHandler: Failed to process {ServiceName} Event.", exception: ex);
Logger.LogError($"{GetType().Name}: Failed to process {ServiceName} Event: " + message);

var auditValue = new AuditEventMessage
{
FacilityId = facilityId,
Action = AuditEventType.Query,
Action = auditEventType,
ServiceName = ServiceName,
EventDate = DateTime.UtcNow,
Notes = $"TransientExceptionHandler: processing failure in {ServiceName} \nException Message: {ex.Message}",
Notes = $"{GetType().Name}: processing failure in {ServiceName} \nException Message: {message}",
};

ProduceAuditEvent(auditValue, consumeResult.Message.Headers);
Expand All @@ -53,7 +55,45 @@ public virtual void HandleException(ConsumeResult<K, V>? consumeResult, Exceptio
}
catch (Exception e)
{
Logger.LogError(e, "Error in TransientExceptionHandler.HandleException: " + e.Message);
Logger.LogError(e, $"Error in {GetType().Name}.HandleException: " + e.Message);
throw;
}
}

public virtual void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, AuditEventType auditEventType, string facilityId)
{
var tEx = new TransientException(ex.Message, auditEventType, ex.InnerException);
HandleException(consumeResult, tEx, facilityId);
}

public virtual void HandleException(ConsumeResult<K, V>? consumeResult, TransientException ex, string facilityId)
{
try
{
if (consumeResult == null)
{
Logger.LogError(message: $"{GetType().Name}|{ServiceName}|{Topic}: consumeResult is null, cannot produce Audit or Retry events", exception: ex);
return;
}

Logger.LogError(message: $"{GetType().Name}: Failed to process {ServiceName} Event.", exception: ex);

var auditValue = new AuditEventMessage
{
FacilityId = facilityId,
Action = ex.AuditEventType,
ServiceName = ServiceName,
EventDate = DateTime.UtcNow,
Notes = $"{GetType().Name}: processing failure in {ServiceName} \nException Message: {ex.Message}",
};

ProduceAuditEvent(auditValue, consumeResult.Message.Headers);
ProduceRetryScheduledEvent(consumeResult.Message.Key, consumeResult.Message.Value,
consumeResult.Message.Headers);
}
catch (Exception e)
{
Logger.LogError(e, $"Error in {GetType().Name}.HandleException: " + e.Message);
throw;
}
}
Expand All @@ -74,7 +114,7 @@ public virtual void ProduceRetryScheduledEvent(K key, V value, Headers headers)
if (string.IsNullOrWhiteSpace(Topic))
{
throw new Exception(
$"TransientExceptionHandler.Topic has not been configured. Cannot Produce Retry Event for {ServiceName}");
$"{GetType().Name}.Topic has not been configured. Cannot Produce Retry Event for {ServiceName}");
}

using var producer = ProducerFactory.CreateProducer(new ProducerConfig());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Confluent.Kafka;
using LantanaGroup.Link.Shared.Application.Error.Exceptions;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Models.Kafka;

namespace LantanaGroup.Link.Shared.Application.Error.Interfaces
Expand All @@ -15,7 +17,9 @@ public interface IDeadLetterExceptionHandler<K, V>
/// </summary>
public string ServiceName { get; set; }

void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, string facilityId);
void HandleException(ConsumeResult<K, V> consumeResult, string facilityId, AuditEventType auditEventType, string message = "");
void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, AuditEventType auditEventType, string facilityId);
void HandleException(ConsumeResult<K, V> consumeResult, DeadLetterException ex, string facilityId);
void ProduceAuditEvent(AuditEventMessage auditValue, Headers headers);
void ProduceDeadLetter(K key, V value, Headers headers, string exceptionMessage);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Confluent.Kafka;
using LantanaGroup.Link.Shared.Application.Error.Exceptions;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Models.Kafka;

namespace LantanaGroup.Link.Shared.Application.Error.Interfaces
Expand All @@ -15,7 +17,9 @@ public interface ITransientExceptionHandler<K, V>
/// </summary>
public string ServiceName { get; set; }

void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, string facilityId);
void HandleException(ConsumeResult<K, V> consumeResult, string facilityId, AuditEventType auditEventType, string message = "");
void HandleException(ConsumeResult<K, V> consumeResult, Exception ex, AuditEventType auditEventType, string facilityId);
void HandleException(ConsumeResult<K, V> consumeResult, TransientException ex, string facilityId);
void ProduceAuditEvent(AuditEventMessage auditValue, Headers headers);
void ProduceRetryScheduledEvent(K key, V value, Headers headers);
}
Expand Down
2 changes: 1 addition & 1 deletion Shared/Shared.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<Description>Shared library for Lantana Link</Description>
<Product>NHSN Link Shared Library</Product>
<!--<Version>1.1.1</Version>-->
<VersionPrefix>1.2.1</VersionPrefix>
<VersionPrefix>2.1.1</VersionPrefix>
<PackageId>com.lantanagroup.link.Shared</PackageId>
</PropertyGroup>

Expand Down
Loading