Microsoft.Extensions.Logging.SqlServer 1.1.4
Microsoft.Extensions.Logging.SqlServer
将 .NET 应用日志异步写入 SQL Server,支持高并发缓冲与后台批量入库。自动建表与存储过程,轻松实现集中日志持久化与查询分析。
功能简介
- 异步写入:日志先进入有界通道,后台托管服务消费写库,降低主线程阻塞。
- 自动初始化:首次运行自动创建日志表与存储过程(可自定义名称)。
- 可扩展处理器:默认使用存储过程写入,可实现
ISqlServerMessageHandler自定义持久化策略。 - 多实例隔离:通过
SqlServerOptions.Key支持多个 SQL Server 目标并行。 - 动态过滤:
Filter委托按分类与级别过滤日志。 - 连接重建:异常或连接串变化时自动重建
SqlConnection。 - 高性能缓冲:有界通道容量 1000(满时丢弃最旧日志),保障新日志持续写入。
- 直接查询接口:通过
ISqlServerLoggingClient追加与搜索历史日志。
安装方法
通过 NuGet 安装:
dotnet add package Microsoft.Extensions.Logging.SqlServer
快速开始
1. 基础配置(默认处理器)
using Microsoft.Extensions.Logging;
builder.Services.AddLogging(logging =>
{
logging.AddSqlServer(options =>
{
options.ConnectionString = "Server=localhost;Database=LogsDb;Trusted_Connection=True;TrustServerCertificate=True;";
options.Application = "MyApp";
options.TableName = "[dbo].[_Log_System]";
options.PorcName = "[dbo].[_Log_System_Append]";
options.Key = "Logging.SqlServer";
options.Filter = (category, level) => level >= LogLevel.Information;
});
});
第一次运行将自动建表与存储过程。
2. 自定义消息处理器(系统已默认实现)
public class MyHandler : ISqlServerMessageHandler
{
private readonly IOptionsMonitor<SqlServerOptions> _opt;
private SqlConnection? _conn;
public MyHandler(IOptionsMonitor<SqlServerOptions> opt) => _opt = opt;
public async Task HandleAsync(SqlServerLoggingMessage msg, CancellationToken token = default)
{
_conn ??= new SqlConnection(_opt.CurrentValue.ConnectionString);
if (_conn.State != System.Data.ConnectionState.Open)
await _conn.OpenAsync(token);
var sql = $"INSERT INTO {_opt.CurrentValue.TableName}(Application,Machine,Level,EventId,Category,Message,Source,StackTrace,Time) VALUES(@Application,@Machine,@Level,@EventId,@Category,@Message,@Source,@StackTrace,@Time)";
await _conn.ExecuteAsync(sql, msg);
}
public void Dispose() => _conn?.Dispose();
}
// 注册
builder.Services.AddLogging(logging =>
{
logging.AddSqlServer<MyHandler>(o =>
{
o.ConnectionString = "Server=...;Database=LogsDb;Trusted_Connection=True;";
o.Application = "MyApp";
});
});
3. 从配置绑定 (appsettings.json)
{
"Logging": {
"SqlServer": {
"ConnectionString": "Server=localhost;Database=LogsDb;Trusted_Connection=True;TrustServerCertificate=True;",
"Application": "MyApp",
"TableName": "[dbo].[_Log_System]",
"PorcName": "[dbo].[_Log_System_Append]",
"Key": "Logging.SqlServer"
}
}
}
builder.Services.AddLogging(logging =>
{
logging.AddSqlServer(o => builder.Configuration.GetSection("Logging:SqlServer").Bind(o));
});
使用 ISqlServerLoggingClient(追加与搜索)
ISqlServerLoggingClient 提供对日志数据的直接写入和查询:
1. 解析客户端
// 通过工厂(已注册)获取客户端
var factory = app.Services.GetRequiredService<SqlServerLoggingClientFactory>();
ISqlServerLoggingClient client = factory.CreateClient(); // 默认 Key
若使用多实例:factory.CreateClient("Logging.SqlServer.Other")。
2. 追加日志(绕过 ILogger 写入,可用于迁移场景)
await client.AddAsync(new SqlServerLoggingMessage
{
Application = "MyApp",
Machine = Environment.MachineName,
Level = "Information",
EventId = "Manual",
Category = "DirectWrite",
Message = "手动追加一条日志",
Source = null,
StackTrace = null,
Time = DateTime.Now
});
内部调用写入存储过程
PorcName。
3. 搜索日志(分页 + 模糊 + 时间范围)
var searchOptions = new SqlServerLoggingSearchOptions
{
Application = "MyApp", // 模糊匹配 LIKE '%MyApp%'
Level = "Error", // 可选
Message = "超时", // 模糊匹配消息包含“超时”
StartTime = DateTime.UtcNow.AddHours(-6),
EndTime = DateTime.UtcNow,
PageIndex = 1,
PageSize = 50
};
SqlServerLoggingSearchResult page = await client.SearchAsync(searchOptions);
Console.WriteLine($"Total={page.TotalCount}, Returned={page.Items.Count()}");
foreach (var row in page.Items)
{
Console.WriteLine($"[{row.Time:HH:mm:ss}] {row.Level} {row.Category} {row.Message}");
}
查询依赖建议的存储过程:
_Log_System_Search,需预先创建;返回两个结果集(总数 + 当前页)。
4. 在 Controller / Minimal API 中注入
app.MapGet("/logs", async (SqlServerLoggingClientFactory factory, string? keyword) =>
{
var client = factory.CreateClient();
var result = await client.SearchAsync(new SqlServerLoggingSearchOptions
{
Message = keyword,
PageIndex = 1,
PageSize = 20
});
return Results.Ok(result);
});
5. ISqlServerLoggingClient 适用场景
- 后台管理系统展示与检索内部日志。
- 快速调试:无需依赖外部日志平台即刻查询。
- 特殊导出:将检索结果再次序列化(JSON/CSV)。
- 与告警规则结合:定时扫描 ERROR/CRITICAL。
配置参数说明
| 参数 | 说明 | 示例 |
|---|---|---|
| ConnectionString | SQL Server 连接字符串 | Server=.;Database=Logs;Trusted_Connection=True; |
| Application | 应用标识 | MyApp |
| TableName | 日志表名 | [dbo].[_Log_System] |
| PorcName | 存储过程名(写入) | [dbo].[_Log_System_Append] |
| Key | 多实例区分键 | Logging.SqlServer |
| Filter | 分类+级别过滤 | (cat,lvl)=> lvl>=LogLevel.Info |
| SearchPorcName | 查询存储过程(可选扩展) | [dbo].[_Log_System_Search] |
表结构 (默认自动创建)
字段:ID(bigint identity, PK), Application, Machine, Level, EventId, Category, Message, Source, StackTrace, Time(datetime)。并包含时间/级别/应用/机器索引。
存储过程示例:PorcName 负责插入一条日志记录;SearchPorcName 负责分页模糊搜索(需手工创建)。
优势
- 异步缓冲减轻主线程压力
- 自动建表开箱即用
- 可插拔处理器扩展写入策略
- 连接自动重建提升稳定性
- 支持多实例隔离与过滤
- 客户端接口支持检索与运维场景
注意事项
- 高并发时满队列会丢弃最旧日志(可修改源码调整策略)。
- PorcName 属性命名保持与现有实现一致(拼写 Porc)。
- 查询存储过程需自行部署(示例名
_Log_System_Search)。 - 建议生产环境提前手工创建表与存储过程并做必要索引优化。
许可证
版权所有 © netor.me
本项目遵循 MIT 协议。
联系方式
- 官网: http://netor.me
- 邮箱: support@netor.me
No packages depend on Microsoft.Extensions.Logging.SqlServer.
2025年11月22日
- 支持自定义日志表与存储过程名称,提升了灵活性。
- 支持高并发场景下的日志写入,优化了性能表现。
- 修复了某些情况下日志可能丢失的问题,提升了稳定性。
- 实现了多实例环境下的日志隔离,增强了适用性。
- 添加了sqlloggingclient工具,方便日志表与存储过程的管理。
.NET 8.0
- Dapper (>= 2.1.66)
- Microsoft.Data.SqlClient (>= 6.0.2)
- Microsoft.Extensions.Configuration (>= 9.0.5)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.6)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.6)
- Microsoft.Extensions.Logging.Configuration (>= 9.0.5)
- Netor.Extensions.StringExtensions (>= 6.1.1)