.NET Core 日志与异常管理 完整实战指南

.NET Core 内置了标准化日志框架全局异常处理机制 ,无需第三方库即可实现生产级日志记录、异常捕获与处理。下面从基础配置、日志使用、异常处理、最佳实践四个维度,给你最实用的落地方案。

一、核心概念

  1. 日志:记录程序运行状态、请求信息、错误详情,用于排查问题、监控系统;
  2. 异常管理:统一捕获程序未处理的错误,避免程序崩溃,返回友好的错误响应;
  3. .NET Core 原生支持:ILogger 日志接口 + 中间件/过滤器 异常处理。

二、日志配置(原生日志,无需第三方包)

1. 基础配置(Program.cs,.NET 6+ 顶级语句)

.NET Core 默认集成日志,只需在配置文件定义日志级别、输出渠道。

csharp 复制代码
var builder = WebApplication.CreateBuilder(args);

// 1. 配置日志(默认已集成,可自定义扩展)
builder.Logging.ClearProviders(); // 清空默认日志提供者
builder.Logging.AddConsole();    // 输出到控制台
builder.Logging.AddDebug();      // 输出到调试窗口
// 可选:添加文件日志(需安装第三方包,文末推荐)
// builder.Logging.AddFile("Logs/myapp-{Date}.txt");

// 2. 配置日志级别(appsettings.json 优先级更高)
builder.Logging.SetMinimumLevel(LogLevel.Information);

// 添加控制器
builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();

app.Run();

2. 配置文件(appsettings.json)

统一管理日志级别,区分环境、区分组件:

json 复制代码
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",       // 默认级别
      "Microsoft.AspNetCore": "Warning", // 系统框架日志只记录警告及以上
      "MyApp.Controllers": "Debug"    // 自定义命名空间日志级别
    }
  }
}

日志级别(从低到高)Trace < Debug < Information < Warning < Error < Critical

3. 日志使用(依赖注入 ILogger)

在控制器/服务中直接注入 ILogger<T>,无需手动实例化:

csharp 复制代码
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class TestController : ControllerBase
    {
        // 注入日志对象(泛型指定当前类,自动标记日志来源)
        private readonly ILogger<TestController> _logger;

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

        [HttpGet]
        public IActionResult Get()
        {
            try
            {
                // 记录普通日志
                _logger.LogInformation("接口 /api/test 被调用");
                _logger.LogDebug("调试信息:参数为空");
                
                // 模拟异常
                throw new Exception("测试业务异常");
                
                return Ok("成功");
            }
            catch (Exception ex)
            {
                // 记录异常日志(自带堆栈信息)
                _logger.LogError(ex, "接口执行失败:{Message}", ex.Message);
                return StatusCode(500, "服务器内部错误");
            }
        }
    }
}

三、异常管理(全局统一处理,推荐)

不推荐在每个接口写 try-catch全局异常中间件是最佳实践:统一捕获所有未处理异常,返回标准化响应。

1. 自定义全局异常中间件

创建 ExceptionMiddleware.cs

csharp 复制代码
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Text.Json;

namespace MyApp.Middlewares
{
    // 全局异常中间件
    public class ExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<ExceptionMiddleware> _logger;

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

        // 核心处理方法
        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                // 调用后续中间件/接口
                await _next(context);
            }
            catch (Exception ex)
            {
                // 1. 记录异常日志(关键:堆栈、请求路径、时间)
                _logger.LogError(ex, "全局异常捕获 | 请求路径:{Path} | 异常信息:{Message}",
                    context.Request.Path, ex.Message);

                // 2. 统一处理异常响应
                await HandleExceptionAsync(context, ex);
            }
        }

        // 标准化错误响应
        private Task HandleExceptionAsync(HttpContext context, Exception ex)
        {
            // 设置响应类型和状态码
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

            // 生产环境隐藏敏感堆栈,开发环境返回完整信息
            var errorResponse = new
            {
                Code = context.Response.StatusCode,
                Message = ex.Message,
                // 开发环境开启堆栈,生产环境注释
                StackTrace = ex.StackTrace
            };

            // 序列化返回JSON
            return context.Response.WriteAsync(JsonSerializer.Serialize(errorResponse));
        }
    }
}

2. 注册中间件(Program.cs)

必须放在所有中间件最前面,才能捕获所有异常:

csharp 复制代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Logging.AddConsole();

var app = builder.Build();

// 注册全局异常中间件(第一行!)
app.UseMiddleware<ExceptionMiddleware>();

app.UseHttpsRedirection();
app.UseRouting();
app.MapControllers();

app.Run();

3. 进阶:自定义业务异常

区分系统异常业务异常,返回不同状态码:

csharp 复制代码
// 自定义业务异常类
public class BusinessException : Exception
{
    public int Code { get; set; }
    public BusinessException(string message, int code = 400) : base(message)
    {
        Code = code;
    }
}

// 中间件中适配自定义异常
private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
    context.Response.ContentType = "application/json";
    
    // 业务异常返回400,系统异常返回500
    if (ex is BusinessException busEx)
    {
        context.Response.StatusCode = busEx.Code;
    }
    else
    {
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    }

    // ... 省略响应代码
}

四、生产级增强(第三方日志库)

原生日志仅支持控制台/调试输出,Serilog 是 .NET Core 最流行的日志库,支持文件、数据库、ELK 等持久化存储。

Serilog 快速集成

  1. 安装 NuGet 包:
bash 复制代码
Install-Package Serilog.AspNetCore
Install-Package Serilog.Sinks.File
  1. Program.cs 配置:
csharp 复制代码
using Serilog;

// 初始化日志(程序启动前配置)
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft", LogLevel.Warning)
    .Enrich.FromLogContext()
    // 按天生成日志文件,自动滚动清理
    .WriteTo.File("Logs/log-.txt", rollingInterval: RollingInterval.Day)
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);

// 替换原生日志为 Serilog
builder.Host.UseSerilog();

try
{
    builder.Services.AddControllers();
    var app = builder.Build();
    app.UseMiddleware<ExceptionMiddleware>();
    app.MapControllers();
    app.Run();
}
catch (Exception ex)
{
    // 捕获程序启动异常
    Log.Fatal(ex, "程序启动失败");
}
finally
{
    Log.CloseAndFlush(); // 确保日志写入完成
}

五、最佳实践总结

日志规范

  1. 禁止 使用 Console.WriteLine(),必须用 ILogger
  2. 敏感信息(密码、身份证)绝不记录
  3. 关键业务(登录、支付、订单)必须记录 Information 日志;
  4. 异常必须记录 LogError(ex, "描述"),保留堆栈信息。

异常规范

  1. 全局唯一异常处理 ,不分散 try-catch
  2. 业务异常用自定义异常类,系统异常统一捕获;
  3. 生产环境不返回堆栈信息,只返回友好提示;
  4. 所有异常必须记录日志,方便排查。

总结

  1. 日志 :依赖注入 ILogger<T>,配置文件控制级别,Serilog 实现持久化;
  2. 异常:全局异常中间件统一捕获,自定义业务异常区分错误类型;
  3. 这套方案是 .NET Core 官方推荐的生产级标准方案,简单、稳定、易维护。
相关推荐
迷藏4942 小时前
# 发散创新:用 Rust实现高性能测试框架的底层逻辑与实战演练
java·开发语言·后端·python·rust
小码哥_常2 小时前
MySQL高级SQL秘籍:性能飞升之路
后端
Victor3562 小时前
MongoDB(64)如何优化写操作性能?
后端
浮尘笔记2 小时前
PHP中常规通用接口验签加密规则设计
开发语言·后端·网络安全·php
Victor3562 小时前
MongoDB(63)如何配置数据压缩?
后端
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Springboot的在线考试系统为例,包含答辩的问题和答案
java·spring boot·后端
神奇小汤圆2 小时前
京东大模型二面:RAG系统在实际部署中可能面临哪些挑战?
后端
ruxingli2 小时前
GoLang channel管道
开发语言·后端·golang
dovens2 小时前
SpringBoot 集成 Activiti 7 工作流引擎
java·spring boot·后端