.NET 8 WebAPI 集成 Serilog 与 SqlSugar 将日志保存到 SQL Server 完整教程
本文档将指导你从零开始创建一个 .NET 8 WebAPI 项目,并集成 Serilog 日志框架,通过自定义 SqlSugar Sink 将日志持久化到本地 SQL Server 数据库。最终实现业务数据与日志数据统一使用 SqlSugar ORM 管理,同时保持日志写入的异步与非阻塞特性。
1. 项目目标
- 创建一个名为
SerilogWebAPI的 .NET 8 WebAPI 项目 - 使用 Serilog 作为日志记录器
- 通过自定义 SqlSugar Sink 将日志写入 SQL Server 数据库
- 利用 SqlSugar 的 CodeFirst 功能自动创建日志表
- 提供测试接口验证日志是否成功写入
2. 环境准备
- .NET 8 SDK(下载地址)
- SQL Server 数据库(本地开发可使用 Visual Studio 自带的 LocalDB,或安装 SQL Server Express)
- 代码编辑器(推荐 Visual Studio 2022、VS Code 或 Rider)
3. 创建项目
打开终端(命令提示符、PowerShell 或 Visual Studio 的包管理器控制台),执行以下命令:
dotnet new webapi -n SerilogWebAPI
cd SerilogWebAPI
这将在当前目录下创建名为 SerilogWebAPI 的 WebAPI 项目,并进入项目文件夹。
4. 安装 NuGet 包
我们需要两个核心包:
- Serilog.AspNetCore:Serilog 与 ASP.NET Core 的集成
- SqlSugarCore:SqlSugar ORM,用于操作 SQL Server
在项目目录下运行:
dotnet add package Serilog.AspNetCore
dotnet add package SqlSugarCore
如果使用 Visual Studio,可以通过“管理 NuGet 程序包”搜索并安装这两个包。
5. 配置数据库连接字符串
打开 appsettings.json 文件,添加 ConnectionStrings 节点,指定本地 SQL Server 的连接字符串。这里以 SQL Server LocalDB 为例:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=SerilogWebAPIDb;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
说明:如果你使用的是独立 SQL Server 实例,请替换为对应的连接字符串,例如:
"Server=localhost;Database=SerilogWebAPIDb;User Id=sa;Password=your_password;"开发环境推荐使用 Windows 身份验证(Trusted_Connection=True)。
6. 创建日志实体类
在项目根目录下新建文件夹 Models,然后创建 SysLog.cs 文件,用于映射数据库中的日志表。
using SqlSugar;
using System;
namespace SerilogWebAPI.Models
{
[SugarTable("SysLogs")] // 指定数据库表名
public class SysLog
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[SugarColumn(IsNullable = false)]
public DateTime Timestamp { get; set; }
[SugarColumn(Length = 50, IsNullable = false)]
public string Level { get; set; }
[SugarColumn(Length = int.MaxValue, IsNullable = true)]
public string Message { get; set; }
[SugarColumn(Length = int.MaxValue, IsNullable = true)]
public string Exception { get; set; }
[SugarColumn(Length = int.MaxValue, IsNullable = true)]
public string Properties { get; set; } // 存储结构化日志的 JSON
}
}
[SugarTable]指定表名,不指定则使用类名。[SugarColumn]中的IsPrimaryKey和IsIdentity用于配置主键自增。Properties字段用于存储日志的附加属性(如请求路径、用户等),以 JSON 格式保存。
7. 创建自定义 Serilog Sink
自定义 Sink 需要实现 Serilog.Core.ILogEventSink 接口。在项目中新建文件夹 Sinks,添加类 SqlSugarSink.cs。
using Serilog.Core;
using Serilog.Events;
using SqlSugar;
using System;
using System.Text.Json;
using System.Threading.Tasks;
using SerilogWebAPI.Models;
namespace SerilogWebAPI.Sinks
{
public class SqlSugarSink : ILogEventSink
{
private readonly ISqlSugarClient _db;
private readonly IFormatProvider _formatProvider;
public SqlSugarSink(ISqlSugarClient db, IFormatProvider formatProvider = null)
{
_db = db;
_formatProvider = formatProvider;
}
public void Emit(LogEvent logEvent)
{
// 将 LogEvent 转换为实体
var logEntry = new SysLog
{
Timestamp = logEvent.Timestamp.DateTime,
Level = logEvent.Level.ToString(),
Message = logEvent.RenderMessage(_formatProvider),
Exception = logEvent.Exception?.ToString(),
Properties = JsonSerializer.Serialize(logEvent.Properties)
};
// 异步插入,避免阻塞主线程
Task.Run(async () =>
{
try
{
await _db.Insertable(logEntry).ExecuteCommandAsync();
}
catch (Exception ex)
{
// 日志写入失败时,将错误输出到控制台(避免递归)
Console.WriteLine($"Failed to write log to database: {ex.Message}");
}
});
}
}
}
Emit方法会在每条日志被记录时调用。- 我们使用
Task.Run进行异步插入,确保日志写入不会影响业务请求的响应时间。 - 异常被捕获并输出到控制台,防止因数据库问题导致应用程序崩溃。
8. 配置 Program.cs
修改 Program.cs,完成以下关键步骤:
- 读取连接字符串
- 创建 SqlSugar 客户端(单例)
- 自动创建日志表(CodeFirst)
- 配置 Serilog 全局日志,使用自定义 Sink
- 注册 SqlSugar 到 DI 容器
- 启用 Serilog
完整代码如下:
using Serilog;
using SqlSugar;
using SerilogWebAPI.Models;
using SerilogWebAPI.Sinks;
var builder = WebApplication.CreateBuilder(args);
// 1. 获取连接字符串
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
// 2. 创建 SqlSugar 客户端
ISqlSugarClient db = new SqlSugarClient(new ConnectionConfig
{
ConnectionString = connectionString,
DbType = DbType.SqlServer,
IsAutoCloseConnection = true,
// 可在此配置更多参数
});
// 3. 自动创建日志表(如果不存在)
db.CodeFirst.InitTables(typeof(SysLog));
// 4. 配置 Serilog
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information() // 最低日志级别
.WriteTo.Sink(new SqlSugarSink(db)) // 使用自定义 Sink
.Enrich.FromLogContext() // 允许从日志上下文中添加属性
.CreateLogger();
// 5. 使用 Serilog 作为日志提供程序
builder.Host.UseSerilog();
// 6. 注册 SqlSugar 到 DI 容器
builder.Services.AddSingleton<ISqlSugarClient>(db);
// 添加控制器和 Swagger 支持
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 确保应用停止时刷新 Serilog 日志
app.Lifetime.ApplicationStopped.Register(Log.CloseAndFlush);
// 配置 HTTP 请求管道
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
9. 创建测试 Controller
为了验证日志写入是否生效,在 Controllers 文件夹下创建 TestController.cs:
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using SerilogWebAPI.Models;
namespace SerilogWebAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
private readonly ISqlSugarClient _db;
public TestController(ILogger<TestController> logger, ISqlSugarClient db)
{
_logger = logger;
_db = db;
}
[HttpGet("log-test")]
public IActionResult LogTest()
{
_logger.LogTrace("Trace 日志 (不会被记录,级别太低)");
_logger.LogDebug("Debug 日志");
_logger.LogInformation("Information 日志,当前时间:{Now}", DateTime.Now);
_logger.LogWarning("Warning 日志");
_logger.LogError("Error 日志,错误详情:{Error}", "示例错误");
// 模拟异常日志
try
{
throw new InvalidOperationException("手动抛出的异常");
}
catch (Exception ex)
{
_logger.LogError(ex, "捕获到异常");
}
return Ok("日志已写入,请检查数据库 SysLogs 表。");
}
[HttpGet("check-db")]
public async Task<IActionResult> CheckDb()
{
var count = await _db.Queryable<SysLog>().CountAsync();
return Ok($"当前日志表共有 {count} 条记录。");
}
}
}
LogTest方法会记录不同级别的日志,并故意抛出一个异常进行捕获记录。CheckDb方法用于快速查看日志表中的记录总数,验证 SqlSugar 是否正常工作。
10. 运行并验证
启动项目
在终端中执行:
dotnet run
项目启动后,控制台会显示监听地址(如 https://localhost:5001),Swagger 页面也会自动打开(如果配置了 Swagger)。
测试日志写入
- 打开浏览器访问 Swagger UI(例如
https://localhost:5001/swagger)。 - 找到
Test控制器的GET /Test/log-test接口,点击 Try it out 然后 Execute。 - 看到响应返回
"日志已写入..."后,说明接口调用成功。
检查数据库
- 使用 SQL Server Management Studio (SSMS) 或 Visual Studio 的 SQL Server 对象资源管理器,连接到
(localdb)\mssqllocaldb(或你的数据库实例)。 - 找到数据库
SerilogWebAPIDb,展开表,应该能看到自动创建的SysLogs表。 - 打开表查看数据,应该包含多条记录,包括
Information、Warning、Error级别的日志,以及异常堆栈信息。Properties字段会存储类似{"Now":"2025-03-03T14:30:00.123Z"}的 JSON 字符串。
也可以调用 /Test/check-db 接口查看日志总数,确认数据已写入。
11. 补充说明
日志级别
在 Program.cs 中,我们设置了 MinimumLevel.Information(),因此只有 Information 及以上级别(Warning、Error、Fatal)的日志会被记录。如需记录 Debug 或 Trace,可改为:
.MinimumLevel.Debug() // 或 .Verbose()
自动建表
db.CodeFirst.InitTables(typeof(SysLog)); 会在数据库不存在 SysLogs 表时自动创建,但不会更新已有表的结构。如果后续需要添加字段,可以手动修改表,或使用迁移工具。SqlSugar 也支持 SplitTable 等高级功能,可按需学习。
异常处理
自定义 Sink 中的 try-catch 确保了日志写入失败不会影响主业务流程,错误信息输出到控制台。生产环境中,可以考虑将此类错误记录到 Windows 事件日志或其他可靠位置。
丰富日志内容
Serilog 支持通过 Enrichers 添加更多上下文信息,例如:
.Enrich.WithMachineName() // 添加机器名
.Enrich.WithThreadId() // 添加线程 ID
.Enrich.WithEnvironmentName() // 添加环境名
需要安装对应的 NuGet 包(如 Serilog.Enrichers.Environment、Serilog.Enrichers.Thread)并在配置中调用相应方法。
性能考虑
当前实现中,每条日志都触发一次异步数据库插入。对于高并发场景,建议使用批量写入或队列机制。SqlSugar 支持批量插入,可以通过修改 Sink 内部逻辑,积累一定数量的日志后批量写入,以减少数据库压力。
12. 结语
至此,你已经成功在 .NET 8 WebAPI 项目中集成了 Serilog,并使用自定义 SqlSugar Sink 将日志保存到 SQL Server。这种方法既保持了数据库访问层的统一,又允许你灵活控制日志表结构。你可以根据实际需求调整日志实体、添加更多 Enricher 或优化写入性能。
如果你有任何问题或改进建议,欢迎在评论区留言讨论。Happy coding!