Clean code, remove warning for future and spot

This commit is contained in:
2025-12-11 14:36:35 +07:00
parent df8c199cce
commit 1426f0b560
17 changed files with 314 additions and 388 deletions

View File

@@ -1,5 +1,3 @@
using Sentry;
namespace Managing.Core.Exceptions;
/// <summary>
@@ -14,14 +12,15 @@ public static class SentryErrorCapture
/// <param name="contextName">A descriptive name for where the error occurred</param>
/// <param name="extraData">Optional dictionary of additional data to include</param>
/// <returns>The Sentry event ID</returns>
public static SentryId CaptureException(Exception exception, string contextName, IDictionary<string, object> extraData = null)
public static SentryId CaptureException(Exception exception, string contextName,
IDictionary<string, object> extraData = null)
{
return SentrySdk.CaptureException(exception, scope =>
{
// Add context information
scope.SetTag("context", contextName);
scope.SetTag("error_type", exception.GetType().Name);
// Add any extra data provided
if (extraData != null)
{
@@ -30,7 +29,7 @@ public static class SentryErrorCapture
scope.SetExtra(kvp.Key, kvp.Value?.ToString() ?? "null");
}
}
// Add extra info from the exception's Data dictionary if available
foreach (var key in exception.Data.Keys)
{
@@ -39,7 +38,7 @@ public static class SentryErrorCapture
scope.SetExtra($"exception_data_{keyStr}", exception.Data[key].ToString());
}
}
// Add a breadcrumb for context
scope.AddBreadcrumb(
message: $"Exception in {contextName}",
@@ -48,7 +47,7 @@ public static class SentryErrorCapture
);
});
}
/// <summary>
/// Enriches an exception with additional context data before throwing
/// </summary>
@@ -64,10 +63,10 @@ public static class SentryErrorCapture
exception.Data[item.Key] = item.Value;
}
}
return exception;
}
/// <summary>
/// Captures a message in Sentry with additional context
/// </summary>
@@ -76,17 +75,18 @@ public static class SentryErrorCapture
/// <param name="contextName">A descriptive name for where the message originated</param>
/// <param name="extraData">Optional dictionary of additional data to include</param>
/// <returns>The Sentry event ID</returns>
public static SentryId CaptureMessage(string message, SentryLevel level, string contextName, IDictionary<string, object> extraData = null)
public static SentryId CaptureMessage(string message, SentryLevel level, string contextName,
IDictionary<string, object> extraData = null)
{
// First capture the message with the specified level
var id = SentrySdk.CaptureMessage(message, level);
// Then add context via a scope
SentrySdk.ConfigureScope(scope =>
{
// Add context information
scope.SetTag("context", contextName);
// Add any extra data provided
if (extraData != null)
{
@@ -95,7 +95,7 @@ public static class SentryErrorCapture
scope.SetExtra(kvp.Key, kvp.Value?.ToString() ?? "null");
}
}
// Add a breadcrumb for context
scope.AddBreadcrumb(
message: $"Message from {contextName}",
@@ -103,7 +103,7 @@ public static class SentryErrorCapture
level: BreadcrumbLevel.Info
);
});
return id;
}
}
}

View File

@@ -1,9 +1,8 @@
using System.Net;
using System.Text.Json;
using Managing.Core.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Sentry;
using Managing.Core.Exceptions;
namespace Managing.Core.Middleawares;
@@ -11,13 +10,13 @@ public class GlobalErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalErrorHandlingMiddleware> _logger;
public GlobalErrorHandlingMiddleware(RequestDelegate next, ILogger<GlobalErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
@@ -29,12 +28,12 @@ public class GlobalErrorHandlingMiddleware
await HandleExceptionAsync(context, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
HttpStatusCode status;
string errorMessage;
// Determine the appropriate status code based on exception type
status = exception switch
{
@@ -43,41 +42,41 @@ public class GlobalErrorHandlingMiddleware
ValidationException => HttpStatusCode.BadRequest,
FormatException => HttpStatusCode.BadRequest,
InvalidOperationException => HttpStatusCode.BadRequest,
// 401 Unauthorized
UnauthorizedAccessException => HttpStatusCode.Unauthorized,
// 403 Forbidden
ForbiddenException => HttpStatusCode.Forbidden,
// 404 Not Found
KeyNotFoundException => HttpStatusCode.NotFound,
FileNotFoundException => HttpStatusCode.NotFound,
DirectoryNotFoundException => HttpStatusCode.NotFound,
NotFoundException => HttpStatusCode.NotFound,
// 408 Request Timeout
TimeoutException => HttpStatusCode.RequestTimeout,
// 409 Conflict
ConflictException => HttpStatusCode.Conflict,
// 429 Too Many Requests
RateLimitExceededException => HttpStatusCode.TooManyRequests,
// 501 Not Implemented
NotImplementedException => HttpStatusCode.NotImplemented,
// 503 Service Unavailable
ServiceUnavailableException => HttpStatusCode.ServiceUnavailable,
// 500 Internal Server Error (default)
_ => HttpStatusCode.InternalServerError
};
// Log the error with appropriate severity based on status code
var isServerError = (int)status >= 500;
if (isServerError)
{
_logger.LogError(exception, "Server Error: {StatusCode} on {Path}", (int)status, context.Request.Path);
@@ -86,29 +85,29 @@ public class GlobalErrorHandlingMiddleware
{
_logger.LogWarning(exception, "Client Error: {StatusCode} on {Path}", (int)status, context.Request.Path);
}
// Capture exception in Sentry with request context
var sentryId = SentrySdk.CaptureException(exception, scope =>
{
// Add HTTP request information
scope.SetTag("http.method", context.Request.Method);
scope.SetTag("http.url", context.Request.Path);
// Add request details
scope.SetExtra("query_string", context.Request.QueryString.ToString());
// Add custom tags to help with filtering
scope.SetTag("error_type", exception.GetType().Name);
scope.SetTag("status_code", ((int)status).ToString());
scope.SetTag("host", context.Request.Host.ToString());
scope.SetTag("path", context.Request.Path.ToString());
// Add any correlation IDs if available
if (context.Request.Headers.TryGetValue("X-Correlation-ID", out var correlationId))
{
scope.SetTag("correlation_id", correlationId.ToString());
}
// Additional context based on exception type
if (exception is ValidationException)
{
@@ -118,7 +117,7 @@ public class GlobalErrorHandlingMiddleware
{
scope.SetTag("error_category", "not_found");
}
// Add additional context from exception data if available
foreach (var key in exception.Data.Keys)
{
@@ -127,7 +126,7 @@ public class GlobalErrorHandlingMiddleware
scope.SetExtra(keyStr, exception.Data[key].ToString());
}
}
// Add breadcrumb for the request
scope.AddBreadcrumb(
message: $"Request to {context.Request.Path}",
@@ -135,7 +134,7 @@ public class GlobalErrorHandlingMiddleware
level: BreadcrumbLevel.Info
);
});
// Use a more user-friendly error message in production
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
{
@@ -154,7 +153,7 @@ public class GlobalErrorHandlingMiddleware
{
errorMessage = exception.Message;
}
// Create the error response
var errorResponse = new ErrorResponse
{
@@ -162,23 +161,23 @@ public class GlobalErrorHandlingMiddleware
Message = errorMessage,
TraceId = sentryId.ToString()
};
// Only include stack trace in development environment
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "Production")
{
errorResponse.StackTrace = exception.StackTrace;
}
var result = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)status;
return context.Response.WriteAsync(result);
}
// Custom error response class
private class ErrorResponse
{

View File

@@ -2,7 +2,6 @@ using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Sentry;
namespace Managing.Core.Middleawares;