← Back to Portfolio

User-Based Global Exception Handling & Logging in ASP.NET Core Web API

Author: Dasan G | Date: May 15, 2025 | Category: ASP.NET Core API

1. 🎯 Introduction

When operating high-throughput production Web APIs, unhandled exceptions can disrupt system stability and hide critical bug tracebacks. Standard generic 500 server error pages lack necessary context. To diagnose production issues efficiently, developers need User-Based Global Error Logging that captures:

2. 🛡️ Building the User-Based Exception Handler (.NET 8/9 Standard)

In modern .NET Core, implementing IExceptionHandler provides a centralized pipeline mechanism to catch and record all unhandled middleware exceptions.

GlobalExceptionHandler.cs Implementation

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

public class UserGlobalExceptionHandler : IExceptionHandler
{
    private readonly ILogger<UserGlobalExceptionHandler> _logger;

    public UserGlobalExceptionHandler(ILogger<UserGlobalExceptionHandler> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        // 1. Extract User Identity Details
        var userId = httpContext.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "Anonymous";
        var userEmail = httpContext.User?.FindFirst(ClaimTypes.Email)?.Value ?? "N/A";
        var clientIp = httpContext.Connection.RemoteIpAddress?.ToString() ?? "Unknown IP";

        // 2. Capture Request Telemetry
        var requestPath = httpContext.Request.Path;
        var requestMethod = httpContext.Request.Method;
        var queryString = httpContext.Request.QueryString.HasValue ? httpContext.Request.QueryString.Value : "None";

        // 3. Log Structured Critical Error Log
        _logger.LogError(
            exception,
            "CRITICAL API EXCEPTION | User: {UserId} ({UserEmail}) | IP: {ClientIp} | Route: {Method} {Path}?{Query} | Exception: {Message}",
            userId, userEmail, clientIp, requestMethod, requestPath, queryString, exception.Message);

        // 4. Construct RFC 7807 Standardized ProblemDetails JSON
        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status500InternalServerError,
            Title = "An unexpected server error occurred.",
            Detail = "Our technical team has been notified. Please quote the reference ID below when contacting support.",
            Instance = $"{requestMethod} {requestPath}"
        };

        problemDetails.Extensions["referenceId"] = Guid.NewGuid().ToString();
        problemDetails.Extensions["timestamp"] = DateTime.UtcNow;
        problemDetails.Extensions["userRef"] = userId;

        httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
        await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken);

        // Return true to signify the exception has been safely handled
        return true;
    }
}

3. ⚙️ Program.cs Pipeline Configuration

Register the custom handler and exception handling middleware in Program.cs:

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Register User Global Exception Handler
builder.Services.AddExceptionHandler<UserGlobalExceptionHandler>();
builder.Services.AddProblemDetails();

var app = builder.Build();

// Enable Global Exception Handler Middleware early in the HTTP pipeline
app.UseExceptionHandler();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();

4. 📊 Sample Log Output & Response Payload

When an unhandled error occurs in any controller endpoint, structured logs capture the user reference instantly:

[ERROR] CRITICAL API EXCEPTION | User: USR_9841 (dasan@example.com) | IP: 106.51.72.18 | Route: POST /api/v1/orders?coupon=FALL2025 | Exception: Sequence contains no elements

The client receives a sanitized, secure ProblemDetails JSON response without revealing internal stack traces:

{
  "status": 500,
  "title": "An unexpected server error occurred.",
  "detail": "Our technical team has been notified. Please quote the reference ID below when contacting support.",
  "instance": "POST /api/v1/orders",
  "referenceId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "timestamp": "2025-05-15T14:30:00Z",
  "userRef": "USR_9841"
}

5. 🏁 Conclusion

Implementing user-based global exception handling transforms debugging from searching through anonymous error dumps to instantly knowing which user triggered the bug, on which route, and under what conditions. This setup drastically reduces MTTR (Mean Time to Resolution) in production enterprise environments.

← Back to Portfolio