You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Entity Framework Core的API控制器实现日志自动化?

Hey there! I totally get how tedious it is to repeat logging code across every controller action—especially when you can’t use a base controller since you’re inheriting from ControllerBase. Let’s look at some clean, automated solutions that will eliminate that repetitive work:

1. Use an Action Filter (Best for Controller-Specific Logging)

Action filters let you intercept the execution of controller actions, so you can add logging logic once and apply it to all (or selected) controllers/actions. This is perfect for your API controller workflow.

Step 1: Create a Custom Action Filter

This filter will log when an action starts, completes successfully, or fails, and automatically capture controller/action names, parameters, execution time, and status codes:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

public class ActionLoggingFilter : IActionFilter
{
    private readonly ILogger<ActionLoggingFilter> _logger;
    private readonly Stopwatch _stopwatch = new();

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

    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Capture request details before the action runs
        var controllerName = context.RouteData.Values["controller"]?.ToString();
        var actionName = context.RouteData.Values["action"]?.ToString();
        var parameters = string.Join(", ", context.ActionArguments.Select(kv => $"{kv.Key}={kv.Value}"));

        _logger.LogInformation("Starting action: {Controller}.{Action} | Parameters: {Parameters}",
            controllerName, actionName, parameters);
        
        _stopwatch.Start();
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        _stopwatch.Stop();
        var controllerName = context.RouteData.Values["controller"]?.ToString();
        var actionName = context.RouteData.Values["action"]?.ToString();
        var statusCode = context.HttpContext.Response.StatusCode;
        var elapsedMs = _stopwatch.ElapsedMilliseconds;

        // Handle success vs failure cases
        if (context.Exception != null)
        {
            _logger.LogError(context.Exception, "Action {Controller}.{Action} failed | Elapsed: {ElapsedMs}ms | Status Code: {StatusCode}",
                controllerName, actionName, elapsedMs, statusCode);
        }
        else
        {
            // Check for custom business logs (like your "station not found" message)
            if (context.HttpContext.Items.TryGetValue("BusinessLog", out var businessMessage))
            {
                _logger.LogInformation("Action {Controller}.{Action} completed | Elapsed: {ElapsedMs}ms | Status Code: {StatusCode} | Details: {BusinessMessage}",
                    controllerName, actionName, elapsedMs, statusCode, businessMessage);
            }
            else
            {
                _logger.LogInformation("Action {Controller}.{Action} completed successfully | Elapsed: {ElapsedMs}ms | Status Code: {StatusCode}",
                    controllerName, actionName, elapsedMs, statusCode);
            }
        }
    }
}

Step 2: Register the Filter Globally

Add this to your Program.cs to apply the filter to all controller actions:

builder.Services.AddControllers(options =>
{
    options.Filters.Add<ActionLoggingFilter>();
});

Or, if you want to apply it selectively, turn it into an attribute:

public class LogActionAttribute : TypeFilterAttribute
{
    public LogActionAttribute() : base(typeof(ActionLoggingFilter)) { }
}

Then use it on controllers or individual actions:

[Route("api/[controller]")]
[ApiController]
[LogAction] // Applies to all actions in this controller
public class PetrolStationController : ControllerBase
{
    // ... your existing controller code
}

Step 3: Add Custom Business Logs (If Needed)

If you still need to log specific business details (like "stationID does not exist"), you can pass that info to the filter using HttpContext.Items:

[HttpGet("GetStationNodeObject/{stationId}")]
public async Task<ActionResult<StationList>> GetStationLocation(Guid stationID)
{
    var stationList = await _context.StationList.FindAsync(stationID);
    if (stationList == null)
    {
        // Pass custom message to the filter
        HttpContext.Items["BusinessLog"] = $"Station ID {stationID} does not exist";
        return NotFound();
    }

    var petrolStation = await _stationService.GetStationLocation(stationID);
    return petrolStation;
}

2. Use Middleware (For Global Request Logging)

If you want to log all incoming HTTP requests (not just controller actions), middleware is a great option. It sits in the request pipeline and can log every request/response pair.

Create the Middleware

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        var controllerName = context.Request.RouteValues["controller"]?.ToString();
        var actionName = context.Request.RouteValues["action"]?.ToString();
        var requestPath = context.Request.Path;
        var requestMethod = context.Request.Method;

        try
        {
            _logger.LogInformation("Received request: {Method} {Path} | Controller: {Controller}, Action: {Action}",
                requestMethod, requestPath, controllerName, actionName);

            // Pass the request to the next middleware/controller
            await _next(context);

            stopwatch.Stop();
            _logger.LogInformation("Request completed: {Method} {Path} | Status Code: {StatusCode} | Elapsed: {ElapsedMs}ms",
                requestMethod, requestPath, context.Response.StatusCode, stopwatch.ElapsedMilliseconds);
        }
        catch (Exception ex)
        {
            stopwatch.Stop();
            _logger.LogError(ex, "Request failed: {Method} {Path} | Controller: {Controller}, Action: {Action} | Elapsed: {ElapsedMs}ms",
                requestMethod, requestPath, controllerName, actionName, stopwatch.ElapsedMilliseconds);
            throw;
        }
    }
}

Register the Middleware

Add this to Program.cs (make sure it’s between UseRouting and UseEndpoints):

app.UseRouting();

// Add the logging middleware here
app.UseMiddleware<RequestLoggingMiddleware>();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

Key Tips for Better Logging

  • Use Structured Logging: Instead of concatenating strings (like your original Message), use the placeholder syntax ({Controller}, {Action}) so log systems (like Serilog, Application Insights) can parse and filter logs easily.
  • Avoid Redundant Data: The filter/middleware automatically captures controller/action names, so you don’t need to set a Controller property manually.
  • Centralize Log Configuration: Configure your logger (e.g., in Program.cs) to output to the right destinations (console, files, cloud services) once, instead of per controller.

内容的提问来源于stack exchange,提问作者SkyeBoniwell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 09:32:51