feat: 實作完整錯誤日誌系統

- 新增 ErrorHandlingMiddleware 全域錯誤處理
- 升級日誌配置到 Debug 層級
- 添加詳細的認證流程日誌
- 改進 AuthController 錯誤處理
- 包含請求詳情和錯誤堆疊追蹤
- 支援結構化日誌記錄

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
鄭沛軒 2025-09-17 00:44:29 +08:00
parent c94cf75838
commit e5cb336667
4 changed files with 111 additions and 5 deletions

View File

@ -35,9 +35,17 @@ public class AuthController : ControllerBase
{
try
{
_logger.LogInformation("Registration attempt for user: {Username}, Email: {Email}",
request.Username, request.Email);
// 驗證請求
if (!ModelState.IsValid)
{
_logger.LogWarning("Invalid model state for registration: {@ModelErrors}",
ModelState.Where(x => x.Value?.Errors.Count > 0)
.ToDictionary(x => x.Key, x => x.Value?.Errors.Select(e => e.ErrorMessage)));
return BadRequest(new { Success = false, Error = "Invalid request data" });
}
// 檢查Email是否已存在
if (await _context.Users.AnyAsync(u => u.Email == request.Email))
@ -90,11 +98,17 @@ public class AuthController : ControllerBase
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during user registration");
_logger.LogError(ex, "Error during user registration. Request: {@Request}", new {
Username = request.Username,
Email = request.Email,
StackTrace = ex.StackTrace,
InnerException = ex.InnerException?.Message
});
return StatusCode(500, new
{
Success = false,
Error = "Registration failed",
Details = ex.Message,
Timestamp = DateTime.UtcNow
});
}
@ -105,9 +119,16 @@ public class AuthController : ControllerBase
{
try
{
_logger.LogInformation("Login attempt for email: {Email}", request.Email);
// 驗證請求
if (!ModelState.IsValid)
{
_logger.LogWarning("Invalid model state for login: {@ModelErrors}",
ModelState.Where(x => x.Value?.Errors.Count > 0)
.ToDictionary(x => x.Key, x => x.Value?.Errors.Select(e => e.ErrorMessage)));
return BadRequest(new { Success = false, Error = "Invalid request data" });
}
// 查找用戶
var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == request.Email);
@ -143,11 +164,16 @@ public class AuthController : ControllerBase
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during user login");
_logger.LogError(ex, "Error during user login. Request: {@Request}", new {
Email = request.Email,
StackTrace = ex.StackTrace,
InnerException = ex.InnerException?.Message
});
return StatusCode(500, new
{
Success = false,
Error = "Login failed",
Details = ex.Message,
Timestamp = DateTime.UtcNow
});
}

View File

@ -0,0 +1,66 @@
using System.Net;
using System.Text.Json;
namespace DramaLing.Api.Middleware;
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred during request execution. Request: {Method} {Path}",
context.Request.Method, context.Request.Path);
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
var errorResponse = new
{
Success = false,
Error = "An error occurred while processing your request",
Details = exception.Message,
Timestamp = DateTime.UtcNow,
RequestId = context.TraceIdentifier
};
var statusCode = exception switch
{
ArgumentException => HttpStatusCode.BadRequest,
UnauthorizedAccessException => HttpStatusCode.Unauthorized,
KeyNotFoundException => HttpStatusCode.NotFound,
NotImplementedException => HttpStatusCode.NotImplemented,
TimeoutException => HttpStatusCode.RequestTimeout,
_ => HttpStatusCode.InternalServerError
};
context.Response.StatusCode = (int)statusCode;
var jsonResponse = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
_logger.LogError("Error response sent: {StatusCode} - {Response}", statusCode, jsonResponse);
await context.Response.WriteAsync(jsonResponse);
}
}

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Data;
using DramaLing.Api.Services;
using DramaLing.Api.Middleware;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
@ -111,6 +112,10 @@ builder.Services.AddSwaggerGen(c =>
var app = builder.Build();
// Configure the HTTP request pipeline.
// 全域錯誤處理中介軟體 (必須放在最前面)
app.UseMiddleware<ErrorHandlingMiddleware>();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();

View File

@ -1,9 +1,18 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
"Default": "Debug",
"Microsoft.AspNetCore": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"DramaLing.Api": "Debug",
"System": "Information",
"Microsoft": "Information"
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Debug"
}
}
},
"AllowedHosts": "*",