From 98af3048f4a98496e85a8d0ffb731bd4d7387003 Mon Sep 17 00:00:00 2001 From: adimiko Date: Sun, 16 Mar 2025 11:01:40 +0100 Subject: [PATCH] Added logging for results --- .../Internals/WebApiResultMapper.cs | 2 +- source/EasyWay/CommandResult.cs | 8 +++- .../CommandExecutorLoggerDecorator.cs | 17 +++++++-- ...hOperationResultExecutorLoggerDecorator.cs | 38 +++++++++++++++++-- .../Internals/Loggers/EasyWayLogger.cs | 33 ++++++++++++---- .../Queries/QueryExecutorLoggerDecorator.cs | 18 +++++++-- 6 files changed, 95 insertions(+), 21 deletions(-) diff --git a/source/EasyWay.WebApi/Internals/WebApiResultMapper.cs b/source/EasyWay.WebApi/Internals/WebApiResultMapper.cs index 6f61e0a..3e531ba 100644 --- a/source/EasyWay.WebApi/Internals/WebApiResultMapper.cs +++ b/source/EasyWay.WebApi/Internals/WebApiResultMapper.cs @@ -13,7 +13,7 @@ public IResult Map(CommandResult commandResult) { CommandErrorEnum.None => Results.Ok(), CommandErrorEnum.Validation => Results.BadRequest(commandResult.ValidationErrors), - CommandErrorEnum.BrokenBusinessRule => Results.Conflict(new BrokenBusinessRuleExceptionResponse(commandResult.Exception)), + CommandErrorEnum.BrokenBusinessRule => Results.Conflict(new BrokenBusinessRuleExceptionResponse(commandResult.BrokenBusinessRuleException)), CommandErrorEnum.ConcurrencyConflict => Results.StatusCode(409), CommandErrorEnum.OperationCanceled => Results.StatusCode(499), CommandErrorEnum.NotFound => Results.StatusCode(404), diff --git a/source/EasyWay/CommandResult.cs b/source/EasyWay/CommandResult.cs index dac7b4d..17d5c3f 100644 --- a/source/EasyWay/CommandResult.cs +++ b/source/EasyWay/CommandResult.cs @@ -11,6 +11,8 @@ public sealed class CommandResult internal IDictionary ValidationErrors; + internal BrokenBusinessRuleException? BrokenBusinessRuleException; + internal Exception? Exception; private CommandResult() @@ -38,7 +40,7 @@ private CommandResult(BrokenBusinessRuleException brokenBusinessRuleException) { Error = CommandErrorEnum.BrokenBusinessRule; ValidationErrors = new Dictionary(); - Exception = brokenBusinessRuleException; + BrokenBusinessRuleException = brokenBusinessRuleException; } private CommandResult(ConcurrencyException concurrencyException) @@ -81,6 +83,8 @@ public sealed class CommandResult internal IDictionary ValidationErrors; + internal BrokenBusinessRuleException? BrokenBusinessRuleException; + internal Exception? Exception; private CommandResult(TOperationResult operationResult) @@ -112,7 +116,7 @@ private CommandResult(BrokenBusinessRuleException brokenBusinessRuleException) OperationResult = null; Error = CommandErrorEnum.BrokenBusinessRule; ValidationErrors = new Dictionary(); - Exception = brokenBusinessRuleException; + BrokenBusinessRuleException = brokenBusinessRuleException; } private CommandResult(ConcurrencyException concurrencyException) diff --git a/source/EasyWay/Internals/Commands/Commands/CommandExecutorLoggerDecorator.cs b/source/EasyWay/Internals/Commands/Commands/CommandExecutorLoggerDecorator.cs index 4d8a9ea..02b52b7 100644 --- a/source/EasyWay/Internals/Commands/Commands/CommandExecutorLoggerDecorator.cs +++ b/source/EasyWay/Internals/Commands/Commands/CommandExecutorLoggerDecorator.cs @@ -23,7 +23,7 @@ public async Task Execute(TCommand command, Ca { var logger = _serviceProvider.GetRequiredService>(); - //TODO begin scope (correlation Id) + //TODO begin scope (correlation Id, userId) logger.Executing(command); @@ -31,14 +31,25 @@ public async Task Execute(TCommand command, Ca { var result = await _decoratedCommandExecutor.Execute(command, cancellationToken); - logger.Executed(); + switch (result.Error) + { + case CommandErrorEnum.None: logger.Successed(); break; + case CommandErrorEnum.Validation: logger.Validation(result.ValidationErrors); break; + case CommandErrorEnum.BrokenBusinessRule: logger.BrokenBusinessRule(result.BrokenBusinessRuleException.BrokenBusinessRule); break; + case CommandErrorEnum.ConcurrencyConflict: logger.ConcurrencyConflict(result.Exception); break; + case CommandErrorEnum.OperationCanceled: logger.OperationCanceled(); break; + case CommandErrorEnum.NotFound: logger.NotFound(); break; + case CommandErrorEnum.Forbidden: logger.Forbidden(); break; + default: logger.UnexpectedException(result.Exception); break; + } return result; } catch (Exception ex) { logger.UnexpectedException(ex); - throw; + + return CommandResult.UnknownException(ex); } } } diff --git a/source/EasyWay/Internals/Commands/CommandsWithResult/CommandWithOperationResultExecutorLoggerDecorator.cs b/source/EasyWay/Internals/Commands/CommandsWithResult/CommandWithOperationResultExecutorLoggerDecorator.cs index 90bb3c1..ae80484 100644 --- a/source/EasyWay/Internals/Commands/CommandsWithResult/CommandWithOperationResultExecutorLoggerDecorator.cs +++ b/source/EasyWay/Internals/Commands/CommandsWithResult/CommandWithOperationResultExecutorLoggerDecorator.cs @@ -1,4 +1,7 @@ -namespace EasyWay.Internals.Commands.CommandsWithResult +using EasyWay.Internals.Queries.Loggers; +using Microsoft.Extensions.DependencyInjection; + +namespace EasyWay.Internals.Commands.CommandsWithResult { internal sealed class CommandWithOperationResultExecutorLoggerDecorator : ICommandWithOperationResultExecutor { @@ -14,12 +17,41 @@ public CommandWithOperationResultExecutorLoggerDecorator( _serviceProvider = serviceProvider; } - public Task> Command(TCommand command, CancellationToken cancellationToken = default) + public async Task> Command(TCommand command, CancellationToken cancellationToken = default) where TModule : EasyWayModule where TCommand : Command where TOperationResult : OperationResult { - return _decoratedCommandExecutor.Command(command, cancellationToken); + var logger = _serviceProvider.GetRequiredService>(); + + //TODO begin scope (correlation Id, userId) + + logger.Executing(command); + + try + { + var result = await _decoratedCommandExecutor.Command(command, cancellationToken); + + switch (result.Error) + { + case CommandErrorEnum.None: logger.Successed(result.OperationResult); break; + case CommandErrorEnum.Validation: logger.Validation(result.ValidationErrors); break; + case CommandErrorEnum.BrokenBusinessRule: logger.BrokenBusinessRule(result.BrokenBusinessRuleException.BrokenBusinessRule); break; + case CommandErrorEnum.ConcurrencyConflict: logger.ConcurrencyConflict(result.Exception); break; + case CommandErrorEnum.OperationCanceled: logger.OperationCanceled(); break; + case CommandErrorEnum.NotFound: logger.NotFound(); break; + case CommandErrorEnum.Forbidden: logger.Forbidden(); break; + default: logger.UnexpectedException(result.Exception); break; + } + + return result; + } + catch (Exception ex) + { + logger.UnexpectedException(ex); + + return CommandResult.UnknownException(ex); + } } } } diff --git a/source/EasyWay/Internals/Loggers/EasyWayLogger.cs b/source/EasyWay/Internals/Loggers/EasyWayLogger.cs index b0d8019..ee2d8e6 100644 --- a/source/EasyWay/Internals/Loggers/EasyWayLogger.cs +++ b/source/EasyWay/Internals/Loggers/EasyWayLogger.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using EasyWay.Internals.BusinessRules; +using Microsoft.Extensions.Logging; namespace EasyWay.Internals.Queries.Loggers { @@ -9,18 +10,34 @@ internal sealed partial class EasyWayLogger public EasyWayLogger(ILogger logger) => _logger = logger; - //TODO scope logger CorrelationId (ScopeId) - [LoggerMessage(0, LogLevel.Information, "Executing {@component}", SkipEnabledCheck = true)] public partial void Executing(object component); - [LoggerMessage(1, LogLevel.Information, "Executed", SkipEnabledCheck = true)] - public partial void Executed(); + [LoggerMessage(1, LogLevel.Information, "Successed", SkipEnabledCheck = true)] + public partial void Successed(); + + [LoggerMessage(2, LogLevel.Information, "Successed: {@result}", SkipEnabledCheck = true)] + public partial void Successed(object result); + + [LoggerMessage(3, LogLevel.Information, "Validation error: {@validatonErrors}", SkipEnabledCheck = true)] + public partial void Validation(IDictionary validatonErrors); + + [LoggerMessage(4, LogLevel.Information, "Broken business rule: {@brokenBusinessRule}", SkipEnabledCheck = true)] + public partial void BrokenBusinessRule(BusinessRule brokenBusinessRule); + + [LoggerMessage(5, LogLevel.Information, "Not found", SkipEnabledCheck = true)] + public partial void NotFound(); + + [LoggerMessage(6, LogLevel.Information, "Operation canceled", SkipEnabledCheck = true)] + public partial void OperationCanceled(); + + [LoggerMessage(7, LogLevel.Warning, "Forbidden", SkipEnabledCheck = true)] + public partial void Forbidden(); - [LoggerMessage(2, LogLevel.Information, "Failed", SkipEnabledCheck = true)] - public partial void Failed(); + [LoggerMessage(8, LogLevel.Warning, "Concurrency conflict", SkipEnabledCheck = true)] + public partial void ConcurrencyConflict(Exception exception); - [LoggerMessage(3, LogLevel.Error, "Unexpected exception", SkipEnabledCheck = true)] + [LoggerMessage(500, LogLevel.Error, "Unexpected exception", SkipEnabledCheck = true)] public partial void UnexpectedException(Exception exception); } } diff --git a/source/EasyWay/Internals/Queries/QueryExecutorLoggerDecorator.cs b/source/EasyWay/Internals/Queries/QueryExecutorLoggerDecorator.cs index 0af0736..a6b2ea1 100644 --- a/source/EasyWay/Internals/Queries/QueryExecutorLoggerDecorator.cs +++ b/source/EasyWay/Internals/Queries/QueryExecutorLoggerDecorator.cs @@ -1,4 +1,5 @@ using EasyWay.Internals.Queries.Loggers; +using EasyWay.Internals.Queries.Results; using Microsoft.Extensions.DependencyInjection; namespace EasyWay.Internals.Queries @@ -24,7 +25,7 @@ public async Task> Execute( { var logger = _serviceProvider.GetRequiredService>(); - //TODO begin scope (correlation Id) + //TODO begin scope (correlation Id, userId) logger.Executing(query); @@ -32,15 +33,24 @@ public async Task> Execute( { var result = await _decoratedQueryExecutor.Execute(query, cancellationToken); - logger.Executed(); + switch (result.Error) + { + case QueryErrorEnum.None: logger.Successed(result.ReadModel); break; + case QueryErrorEnum.Validation: logger.Validation(result.ValidationErrors); break; + case QueryErrorEnum.OperationCanceled: logger.OperationCanceled(); break; + case QueryErrorEnum.NotFound: logger.NotFound(); break; + case QueryErrorEnum.Forbidden: logger.Forbidden(); break; + default: logger.UnexpectedException(result.Exception); break; + } return result; } catch (Exception ex) { logger.UnexpectedException(ex); - throw; - } + + return QueryResult.UnknownException(ex); + } } } }