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 协议。


联系方式

No packages depend on Microsoft.Extensions.Logging.SqlServer.

2025年11月22日 - 支持自定义日志表与存储过程名称,提升了灵活性。 - 支持高并发场景下的日志写入,优化了性能表现。 - 修复了某些情况下日志可能丢失的问题,提升了稳定性。 - 实现了多实例环境下的日志隔离,增强了适用性。 - 添加了sqlloggingclient工具,方便日志表与存储过程的管理。

Version Downloads Last updated
1.1.4 4 12/03/2025
1.1.3 3 12/03/2025
1.1.2 3 12/03/2025
1.1.1 3 11/30/2025
1.1.0 3 11/22/2025
1.0.9 2 11/22/2025
1.0.8 2 11/22/2025
1.0.7 9 09/02/2025
1.0.6 7 09/02/2025
1.0.5 7 09/02/2025