diff --git a/CR.Exceptions.AspNet.UnitTests/CR.Exceptions.AspNet.Tests.csproj b/CR.Exceptions.AspNet.Tests/CR.Exceptions.AspNet.Tests.csproj similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/CR.Exceptions.AspNet.Tests.csproj rename to CR.Exceptions.AspNet.Tests/CR.Exceptions.AspNet.Tests.csproj diff --git a/CR.Exceptions.AspNet.UnitTests/Component/CrExceptionHandlerTests.cs b/CR.Exceptions.AspNet.Tests/Component/CrExceptionHandlerTests.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/Component/CrExceptionHandlerTests.cs rename to CR.Exceptions.AspNet.Tests/Component/CrExceptionHandlerTests.cs diff --git a/CR.Exceptions.AspNet.UnitTests/Component/LogLevelMapTests.cs b/CR.Exceptions.AspNet.Tests/Component/LogLevelMapTests.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/Component/LogLevelMapTests.cs rename to CR.Exceptions.AspNet.Tests/Component/LogLevelMapTests.cs diff --git a/CR.Exceptions.AspNet.UnitTests/Component/StatusCodeMapTests.cs b/CR.Exceptions.AspNet.Tests/Component/StatusCodeMapTests.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/Component/StatusCodeMapTests.cs rename to CR.Exceptions.AspNet.Tests/Component/StatusCodeMapTests.cs diff --git a/CR.Exceptions.AspNet.UnitTests/TestNotFoundException.cs b/CR.Exceptions.AspNet.Tests/TestNotFoundException.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/TestNotFoundException.cs rename to CR.Exceptions.AspNet.Tests/TestNotFoundException.cs diff --git a/CR.Exceptions.AspNet.UnitTests/TestUnregisteredException.cs b/CR.Exceptions.AspNet.Tests/TestUnregisteredException.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/TestUnregisteredException.cs rename to CR.Exceptions.AspNet.Tests/TestUnregisteredException.cs diff --git a/CR.Exceptions.AspNet.UnitTests/Unit/LogLevelMapBuilderTests.cs b/CR.Exceptions.AspNet.Tests/Unit/LogLevelMapBuilderTests.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/Unit/LogLevelMapBuilderTests.cs rename to CR.Exceptions.AspNet.Tests/Unit/LogLevelMapBuilderTests.cs diff --git a/CR.Exceptions.AspNet.UnitTests/Unit/StatusCodeMapBuilderTests.cs b/CR.Exceptions.AspNet.Tests/Unit/StatusCodeMapBuilderTests.cs similarity index 100% rename from CR.Exceptions.AspNet.UnitTests/Unit/StatusCodeMapBuilderTests.cs rename to CR.Exceptions.AspNet.Tests/Unit/StatusCodeMapBuilderTests.cs diff --git a/CR.Exceptions.AspNet/CrExceptionHandler.cs b/CR.Exceptions.AspNet/CrExceptionHandler.cs index 7cc15a8..2e21e36 100644 --- a/CR.Exceptions.AspNet/CrExceptionHandler.cs +++ b/CR.Exceptions.AspNet/CrExceptionHandler.cs @@ -60,12 +60,17 @@ public async ValueTask TryHandleAsync(HttpContext httpContext, Exception e _logger.LogMissingHttpStatusMapping(exception, exceptionTypeName); } - var logLevel = _logLevelMap.TryFind(crException, out var level) ? level : LogLevel.Debug; - _logger.LogApplicationException(logLevel, exception, exceptionTypeName); + if (!_logLevelMap.TryFind(crException, out var logLevel)) + { + logLevel = LogLevel.Debug; + _logger.LogMissingLogLevelMapping(exceptionTypeName, logLevel); + } + + _logger.LogCrExceptionOccurred(logLevel, exception, exceptionTypeName); } else { - _logger.LogUnhandledException(exception, exceptionTypeName); + _logger.LogUnknownException(exception, exceptionTypeName); } httpContext.Response.StatusCode = statusCode; @@ -81,6 +86,7 @@ public async ValueTask TryHandleAsync(HttpContext httpContext, Exception e Instance = httpContext.Request.Path }, }; + AddProblemDetailsExtension(problemDetailsContext.ProblemDetails, ProblemDetailsExtensionNames.Errors, errors); return await TryWriteResponseAsync(exception, problemDetailsContext); diff --git a/CR.Exceptions.AspNet/CrExceptionHandlerLogExtensions.cs b/CR.Exceptions.AspNet/CrExceptionHandlerLogExtensions.cs index a4f4e6f..a82a7d5 100644 --- a/CR.Exceptions.AspNet/CrExceptionHandlerLogExtensions.cs +++ b/CR.Exceptions.AspNet/CrExceptionHandlerLogExtensions.cs @@ -2,7 +2,7 @@ namespace CR.Exceptions.AspNet; -public static partial class CrExceptionHandlerLogExtensions +internal static partial class CrExceptionHandlerLogExtensions { private static class LogIds { @@ -10,10 +10,11 @@ private static class LogIds public const int ResponseAlreadyStarted = BaseId + 1; public const int MissingHttpStatusMapping = BaseId + 2; - public const int UnhandledException = BaseId + 3; + public const int UnknownException = BaseId + 3; public const int ProblemDetailsExtensionOverwritten = BaseId + 4; public const int FailedToWriteProblemDetails = BaseId + 5; - public const int ApplicationException = BaseId + 6; + public const int CrExceptionOccurred = BaseId + 6; + public const int MissingLogLevelMapping = BaseId + 7; } [LoggerMessage( @@ -29,10 +30,10 @@ private static class LogIds public static partial void LogMissingHttpStatusMapping(this ILogger logger, Exception exception, string? exceptionType); [LoggerMessage( - EventId = LogIds.UnhandledException, + EventId = LogIds.UnknownException, Level = LogLevel.Error, - Message = "An unexpected exception of type '{ExceptionType}' occurred.")] - public static partial void LogUnhandledException(this ILogger logger, Exception exception, string? exceptionType); + Message = "Unknown exception of type '{ExceptionType}' occurred. Default internal errors will be used.")] + public static partial void LogUnknownException(this ILogger logger, Exception exception, string? exceptionType); [LoggerMessage( EventId = LogIds.ProblemDetailsExtensionOverwritten, @@ -47,7 +48,13 @@ private static class LogIds public static partial void LogFailedToWriteProblemDetails(this ILogger logger, Exception exception); [LoggerMessage( - EventId = LogIds.ApplicationException, - Message = "Application exception of type '{ExceptionType}' occurred.")] - public static partial void LogApplicationException(this ILogger logger, LogLevel level, Exception exception, string? exceptionType); + EventId = LogIds.CrExceptionOccurred, + Message = "CR exception of type '{ExceptionType}' occurred.")] + public static partial void LogCrExceptionOccurred(this ILogger logger, LogLevel level, Exception exception, string? exceptionType); + + [LoggerMessage( + EventId = LogIds.MissingLogLevelMapping, + Level = LogLevel.Debug, + Message = "No log level mapping found for exception type '{ExceptionType}'. Using fallback log level '{FallbackLogLevel}'.")] + public static partial void LogMissingLogLevelMapping(this ILogger logger, string? exceptionType, LogLevel fallbackLogLevel); } \ No newline at end of file diff --git a/CR.Exceptions.UnitTests/CR.Exceptions.Tests.csproj b/CR.Exceptions.Tests/CR.Exceptions.Tests.csproj similarity index 100% rename from CR.Exceptions.UnitTests/CR.Exceptions.Tests.csproj rename to CR.Exceptions.Tests/CR.Exceptions.Tests.csproj diff --git a/CR.Exceptions.UnitTests/Component/ErrorMapTests.cs b/CR.Exceptions.Tests/Component/ErrorMapTests.cs similarity index 100% rename from CR.Exceptions.UnitTests/Component/ErrorMapTests.cs rename to CR.Exceptions.Tests/Component/ErrorMapTests.cs diff --git a/CR.Exceptions.UnitTests/Component/ExceptionFactoryTests.cs b/CR.Exceptions.Tests/Component/ExceptionFactoryTests.cs similarity index 100% rename from CR.Exceptions.UnitTests/Component/ExceptionFactoryTests.cs rename to CR.Exceptions.Tests/Component/ExceptionFactoryTests.cs diff --git a/CR.Exceptions.UnitTests/TestException.cs b/CR.Exceptions.Tests/TestException.cs similarity index 100% rename from CR.Exceptions.UnitTests/TestException.cs rename to CR.Exceptions.Tests/TestException.cs diff --git a/CR.Exceptions.UnitTests/Unit/ErrorMapBuilderTests.cs b/CR.Exceptions.Tests/Unit/ErrorMapBuilderTests.cs similarity index 100% rename from CR.Exceptions.UnitTests/Unit/ErrorMapBuilderTests.cs rename to CR.Exceptions.Tests/Unit/ErrorMapBuilderTests.cs diff --git a/CR.Exceptions.UnitTests/Unit/ExceptionFactoryBuilderTests.cs b/CR.Exceptions.Tests/Unit/ExceptionFactoryBuilderTests.cs similarity index 100% rename from CR.Exceptions.UnitTests/Unit/ExceptionFactoryBuilderTests.cs rename to CR.Exceptions.Tests/Unit/ExceptionFactoryBuilderTests.cs diff --git a/CR.Exceptions.slnx b/CR.Exceptions.slnx index 2d7e57f..34ade86 100644 --- a/CR.Exceptions.slnx +++ b/CR.Exceptions.slnx @@ -2,8 +2,8 @@ - + - + diff --git a/CR.Exceptions/CR.Exceptions.csproj b/CR.Exceptions/CR.Exceptions.csproj index 21cbfe4..b40b4fb 100644 --- a/CR.Exceptions/CR.Exceptions.csproj +++ b/CR.Exceptions/CR.Exceptions.csproj @@ -10,7 +10,7 @@ Core .NET exception framework providing base error models, an exception factory, and error mapping. $(NuGetPackagePrefix).Exceptions - exceptions;exception-handling;error-handling;error-factory;error-mapping;domain-exceptions + exceptions;exception-handling;exception-factory;error-handling;error-factory;error-mapping; README.md diff --git a/CR.Exceptions/Extensions/ImmutableArrayExtensions.cs b/CR.Exceptions/Extensions/ImmutableArrayExtensions.cs index a3e99b7..3fa7157 100644 --- a/CR.Exceptions/Extensions/ImmutableArrayExtensions.cs +++ b/CR.Exceptions/Extensions/ImmutableArrayExtensions.cs @@ -1,23 +1,24 @@ using System.Collections.Immutable; +using System.Runtime.CompilerServices; namespace CR.Exceptions.Extensions; public static class ImmutableArrayExtensions { - extension(ImmutableArray source) where TSource : class? + extension(ImmutableArray source) { - public void ThrowIfEmptyOrContainsNull() + public void ThrowIfEmptyOrContainsNull([CallerArgumentExpression(nameof(source))] string? paramName = null) { if (source.IsDefaultOrEmpty) { - throw new ArgumentException("The array cannot be empty.", nameof(source)); + throw new ArgumentException("The array cannot be empty.", paramName); } for (var i = 0; i < source.Length; i++) { if (source[i] is null) { - throw new ArgumentNullException(nameof(source), $"The array element[{i}] is null."); + throw new ArgumentNullException(paramName, $"The array element[{i}] is null."); } } } diff --git a/README.md b/README.md index 1eb1643..acb840f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ A lightweight framework for defining application errors, creating typed exceptions, and exposing consistent error responses across application boundaries. -The repository contains the following modules: - ## Modules ### CR.Exceptions