Skip to content

Commit

Permalink
Merge pull request #26 from itsharppro/dev, logging: update scripts […
Browse files Browse the repository at this point in the history
…build-test-force] [pack-all-force]

(#23) logging: add Paralax logging add Paralax CQRS logging ,logging: update scripts [build-test-force] [pack-all-force]
  • Loading branch information
SaintAngeLs authored Sep 21, 2024
2 parents 91cfdaa + 5d88565 commit 3ea3f99
Show file tree
Hide file tree
Showing 16 changed files with 951 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/Paralax.CQRS.Logging/scripts/build-and-pack.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash

echo "Executing post-success scripts for branch $GITHUB_REF_NAME"
echo "Starting build and NuGet package creation for Paralax.CQRS.Logging..."

cd src/Paralax.CQRS.Logging/src

echo "Restoring NuGet packages..."
dotnet restore

PACKAGE_VERSION="1.0.$GITHUB_RUN_NUMBER"
echo "Building and packing the Paralax.CQRS.Logging library..."
dotnet pack -c release /p:PackageVersion=$PACKAGE_VERSION --no-restore -o ./nupkg

PACKAGE_PATH="./nupkg/Paralax.CQRS.Logging.$PACKAGE_VERSION.nupkg"

if [ -f "$PACKAGE_PATH" ]; then
echo "Checking if the package is already signed..."
if dotnet nuget verify "$PACKAGE_PATH" | grep -q 'Package is signed'; then
echo "Package is already signed, skipping signing."
else
echo "Signing the NuGet package..."
dotnet nuget sign "$PACKAGE_PATH" \
--certificate-path "$CERTIFICATE_PATH" \
--timestamper http://timestamp.digicert.com
fi

echo "Uploading Paralax.CQRS.Logging package to NuGet..."
dotnet nuget push "$PACKAGE_PATH" -k "$NUGET_API_KEY" \
-s https://api.nuget.org/v3/index.json --skip-duplicate
echo "Package uploaded to NuGet."
else
echo "Error: Package $PACKAGE_PATH not found."
exit 1
fi
18 changes: 18 additions & 0 deletions src/Paralax.CQRS.Logging/scripts/test-and-collect-coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

echo "Running tests and collecting coverage for Paralax.HTTP..."

cd src/Paralax.HTTP/src

echo "Restoring NuGet packages..."
dotnet restore

echo "Running tests and generating code coverage report..."
dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults

# Check if tests succeeded
if [ $? -ne 0 ]; then
echo "Tests failed. Exiting..."
exit 1
fi

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Paralax.Core;
using Paralax.CQRS.Commands;
using SmartFormat;

namespace Paralax.CQRS.Logging.Decorators
{
[Decorator]
internal sealed class CommandHandlerLoggingDecorator<TCommand> : ICommandHandler<TCommand>
where TCommand : class, ICommand
{
private readonly ICommandHandler<TCommand> _handler;
private readonly ILogger<CommandHandlerLoggingDecorator<TCommand>> _logger;
private readonly IMessageToLogTemplateMapper _mapper;

public CommandHandlerLoggingDecorator(ICommandHandler<TCommand> handler,
ILogger<CommandHandlerLoggingDecorator<TCommand>> logger, IServiceProvider serviceProvider)
{
_handler = handler;
_logger = logger;
_mapper = serviceProvider.GetService<IMessageToLogTemplateMapper>() ?? new EmptyMessageToLogTemplateMapper();
}

public async Task HandleAsync(TCommand command, CancellationToken cancellationToken = default)
{
var template = _mapper.Map(command);

if (template is null)
{
await _handler.HandleAsync(command, cancellationToken);
return;
}

try
{
Log(command, template.Before);

Check warning on line 40 in src/Paralax.CQRS.Logging/src/Decorators/CommandHandlerLoggingDecorator.cs

View workflow job for this annotation

GitHub Actions / publish

Possible null reference argument for parameter 'message' in 'void CommandHandlerLoggingDecorator<TCommand>.Log(TCommand command, string message, bool isError = false)'.
await _handler.HandleAsync(command, cancellationToken);
Log(command, template.After);

Check warning on line 42 in src/Paralax.CQRS.Logging/src/Decorators/CommandHandlerLoggingDecorator.cs

View workflow job for this annotation

GitHub Actions / publish

Possible null reference argument for parameter 'message' in 'void CommandHandlerLoggingDecorator<TCommand>.Log(TCommand command, string message, bool isError = false)'.
}
catch (Exception ex)
{
var exceptionTemplate = template.GetExceptionTemplate(ex);
Log(command, exceptionTemplate, isError: true);

Check warning on line 47 in src/Paralax.CQRS.Logging/src/Decorators/CommandHandlerLoggingDecorator.cs

View workflow job for this annotation

GitHub Actions / publish

Possible null reference argument for parameter 'message' in 'void CommandHandlerLoggingDecorator<TCommand>.Log(TCommand command, string message, bool isError = false)'.
throw;
}
}

private void Log(TCommand command, string message, bool isError = false)
{
if (string.IsNullOrEmpty(message))
{
return;
}

var formattedMessage = Smart.Format(message, command);

if (isError)
{
_logger.LogError(formattedMessage);
}
else
{
_logger.LogInformation(formattedMessage);
}
}

private class EmptyMessageToLogTemplateMapper : IMessageToLogTemplateMapper
{
public HandlerLogTemplate Map<TMessage>(TMessage message) where TMessage : class => null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Paralax.Core;
using Paralax.CQRS.Events;
using SmartFormat;

namespace Paralax.CQRS.Logging.Decorators
{
[Decorator]
internal sealed class EventHandlerLoggingDecorator<TEvent> : IEventHandler<TEvent>
where TEvent : class, IEvent
{
private readonly IEventHandler<TEvent> _handler;
private readonly ILogger<EventHandlerLoggingDecorator<TEvent>> _logger;
private readonly IMessageToLogTemplateMapper _mapper;

public EventHandlerLoggingDecorator(IEventHandler<TEvent> handler,
ILogger<EventHandlerLoggingDecorator<TEvent>> logger, IServiceProvider serviceProvider)
{
_handler = handler;
_logger = logger;
_mapper = serviceProvider.GetService<IMessageToLogTemplateMapper>() ?? new EmptyMessageToLogTemplateMapper();
}

public async Task HandleAsync(TEvent @event, CancellationToken cancellationToken = default)
{
var template = _mapper.Map(@event);

if (template is null)
{
await _handler.HandleAsync(@event, cancellationToken);
return;
}

try
{
Log(@event, template.Before);

Check warning on line 40 in src/Paralax.CQRS.Logging/src/Decorators/EventHandlerLoggingDecorator.cs

View workflow job for this annotation

GitHub Actions / publish

Possible null reference argument for parameter 'message' in 'void EventHandlerLoggingDecorator<TEvent>.Log(TEvent @event, string message, bool isError = false)'.
await _handler.HandleAsync(@event, cancellationToken);
Log(@event, template.After);

Check warning on line 42 in src/Paralax.CQRS.Logging/src/Decorators/EventHandlerLoggingDecorator.cs

View workflow job for this annotation

GitHub Actions / publish

Possible null reference argument for parameter 'message' in 'void EventHandlerLoggingDecorator<TEvent>.Log(TEvent @event, string message, bool isError = false)'.
}
catch (Exception ex)
{
var exceptionTemplate = template.GetExceptionTemplate(ex);
Log(@event, exceptionTemplate, isError: true);
throw;
}
}

private void Log(TEvent @event, string message, bool isError = false)
{
if (string.IsNullOrEmpty(message))
{
return;
}

var formattedMessage = Smart.Format(message, @event);

if (isError)
{
_logger.LogError(formattedMessage);
}
else
{
_logger.LogInformation(formattedMessage);
}
}

private class EmptyMessageToLogTemplateMapper : IMessageToLogTemplateMapper
{
public HandlerLogTemplate Map<TMessage>(TMessage message) where TMessage : class => null;
}
}
}
81 changes: 81 additions & 0 deletions src/Paralax.CQRS.Logging/src/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.DependencyInjection;
using Scrutor;
using Paralax.CQRS.Commands;
using Paralax.CQRS.Events;
using Paralax.CQRS.Logging.Decorators;

namespace Paralax.CQRS.Logging
{
public static class Extensions
{
/// <summary>
/// Adds logging decorators for all command handlers in the specified assembly.
/// </summary>
/// <param name="builder">The <see cref="IParalaxBuilder"/>.</param>
/// <param name="assembly">The assembly to scan for command handlers.</param>
/// <returns>The updated <see cref="IParalaxBuilder"/>.</returns>
public static IParalaxBuilder AddCommandHandlersLogging(this IParalaxBuilder builder, Assembly assembly = null)

Check warning on line 22 in src/Paralax.CQRS.Logging/src/Extensions.cs

View workflow job for this annotation

GitHub Actions / publish

Cannot convert null literal to non-nullable reference type.
=> builder.AddHandlerLogging(typeof(ICommandHandler<>), typeof(CommandHandlerLoggingDecorator<>), assembly);

/// <summary>
/// Adds logging decorators for all event handlers in the specified assembly.
/// </summary>
/// <param name="builder">The <see cref="IParalaxBuilder"/>.</param>
/// <param name="assembly">The assembly to scan for event handlers.</param>
/// <returns>The updated <see cref="IParalaxBuilder"/>.</returns>
public static IParalaxBuilder AddEventHandlersLogging(this IParalaxBuilder builder, Assembly assembly = null)

Check warning on line 31 in src/Paralax.CQRS.Logging/src/Extensions.cs

View workflow job for this annotation

GitHub Actions / publish

Cannot convert null literal to non-nullable reference type.
=> builder.AddHandlerLogging(typeof(IEventHandler<>), typeof(EventHandlerLoggingDecorator<>), assembly);

/// <summary>
/// Generic method to add logging decorators for either command or event handlers.
/// </summary>
private static IParalaxBuilder AddHandlerLogging(this IParalaxBuilder builder, Type handlerType,
Type decoratorType, Assembly assembly = null)

Check warning on line 38 in src/Paralax.CQRS.Logging/src/Extensions.cs

View workflow job for this annotation

GitHub Actions / publish

Cannot convert null literal to non-nullable reference type.
{
assembly ??= Assembly.GetCallingAssembly();

var handlers = assembly
.GetTypes()
.Where(t => t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == handlerType))
.ToList();

handlers.ForEach(handler =>
{
// Find the TryDecorate method and invoke it on the appropriate service
var extensionMethods = GetExtensionMethods();
var tryDecorateMethod = extensionMethods.FirstOrDefault(mi => mi.Name == "TryDecorate" && !mi.IsGenericMethod);

tryDecorateMethod?.Invoke(builder.Services, new object[]
{
builder.Services,
handler.GetInterfaces().FirstOrDefault(),
decoratorType.MakeGenericType(handler.GetInterfaces().FirstOrDefault()?.GenericTypeArguments.First())
});
});

return builder;
}

/// <summary>
/// Retrieves the extension methods for service collection.
/// </summary>
private static IEnumerable<MethodInfo> GetExtensionMethods()
{
var types = typeof(ReplacementBehavior).Assembly.GetTypes();

var query = from type in types
where type.IsSealed && !type.IsGenericType && !type.IsNested
from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
where method.IsDefined(typeof(ExtensionAttribute), false)
where method.GetParameters()[0].ParameterType == typeof(IServiceCollection)
select method;

return query;
}
}
}
48 changes: 48 additions & 0 deletions src/Paralax.CQRS.Logging/src/HandlerLogTemplate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Text.Json;

namespace Paralax.CQRS.Logging
{
public sealed class HandlerLogTemplate
{
public string? Before { get; set; }
public string? After { get; set; }
public IReadOnlyDictionary<Type, string>? OnError { get; set; }

public string? GetExceptionTemplate(Exception ex)
{
var exceptionType = ex.GetType();
if (OnError == null)
{
return null;
}

return OnError.TryGetValue(exceptionType, out var template)
? template
: "An unexpected error occurred.";
}

public string GetBeforeTemplate<TMessage>(TMessage message)
{
var messageType = message?.GetType().Name ?? "UnknownMessage";
return Before != null
? SmartFormat(Before, message)
: $"Starting to handle message of type {messageType}.";
}

public string GetAfterTemplate<TMessage>(TMessage message)
{
var messageType = message?.GetType().Name ?? "UnknownMessage";
return After != null
? SmartFormat(After, message)
: $"Completed handling message of type {messageType}.";
}

private string SmartFormat<TMessage>(string template, TMessage message)
{
// Serialize anonymous types or complex objects as JSON to make them human-readable
return string.Format(template, JsonSerializer.Serialize(message));
}
}
}
8 changes: 8 additions & 0 deletions src/Paralax.CQRS.Logging/src/IMessageToLogTemplateMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Paralax.CQRS.Logging
{
public interface IMessageToLogTemplateMapper
{
HandlerLogTemplate Map<TMessage>(TMessage message) where TMessage : class;
}
}

21 changes: 21 additions & 0 deletions src/Paralax.CQRS.Logging/src/Paralax.CQRS.Logging.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,25 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Paralax" Version="*" />
<PackageReference Include="Paralax.Logging" Version="*" />
<PackageReference Include="Paralax.CQRS.Commands" Version="*" />
<PackageReference Include="Paralax.CQRS.Queries" Version="*" />
<PackageReference Include="Paralax.CQRS.Events" Version="*" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="SmartFormat.NET" Version="3.5.0" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Paralax.CQRS.Logging.Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

</Project>
Loading

0 comments on commit 3ea3f99

Please sign in to comment.