Microsoft.Extensions.Logging.HttpLogging 1.1.6
Microsoft.Extensions.Logging.HttpLogging
将 .NET Web 应用的 HTTP 请求/响应日志异步采集并写入 SQL Server,支持中间件采集、后台通道缓冲、存储过程写入、查询与维护。适合本地与企业内部集中化日志场景。
核心特性
- HTTP 日志中间件:自动采集请求/响应的基础信息、头、客户端信息等。
- 异步缓冲:使用有界
Channel(容量 1000,满时丢弃最旧)降低主线程阻塞。 - 后台写入:托管服务批量消费消息并通过存储过程入库。
- 可插拔处理器:实现
IHttpLoggingMessageHandler自定义写入策略(默认 SQL Server 存储过程)。 - 客户端能力:
IHttpLoggingClient提供追加、查询、删除、清空等维护操作。 - 多实例隔离:通过
HttpLoggingOptions.Key支持多路并行(通道/处理器/客户端均按 Key 隔离)。 - 配置监控:
IOptionsMonitor<HttpLoggingOptions>支持运行期更新连接串等配置。
安装
dotnet add package Microsoft.Extensions.Logging.HttpLogging
快速开始
1) 注册日志与中间件(默认处理器)
using Microsoft.Extensions.Logging;
builder.Services.AddLogging(logging =>
{
logging.AddHttpLogging(options =>
{
options.ConnectionString = "Server=localhost;Database=LogsDb;Trusted_Connection=True;TrustServerCertificate=True;";
options.Application = "MyWebApp";
options.TableName = "[dbo].[_Log_Http]";
options.PorcName = "[dbo].[_Log_Http_Append]";
options.SearchPorcName = "[dbo].[_Log_Http_Search]";
options.Key = "Logging.Http"; // 多实例时确保唯一
options.EnableRequest = true;
options.EnableResponse = false;
options.EnableHeaders = true;
options.EnableAgent = true;
options.EnableRequestBody = false; // 默认禁用,避免性能问题
options.EnableResponseBody = false;
options.RequestBodyLogLimit = 1024 * 1024; // 1MB
options.ResponseBodyLogLimit = 1024 * 1024; // 1MB
// 仅记录指定的 HTTP 方法(默认包含常见方法)
options.HttpMethods = new[] { "GET", "POST", "PUT", "DELETE" };
});
});
// 启用 HTTP 日志中间件(需在路由前)
var app = builder.Build();
app.UseHttpAccessLogging(); // 必须在 app.UseRouting() 之前
app.UseRouting();
app.MapGet("/ping", () => "pong");
app.Run();
首次运行若数据库无表/过程,默认处理器不会自动建表;请按下文“表结构与存储过程”准备对象。
2) 使用自定义消息处理器
实现自定义处理器以替代默认 SQL 写入策略:
using Microsoft.Extensions.Logging;
public class MyHttpHandler : IHttpLoggingMessageHandler
{
private readonly ILogger<MyHttpHandler> _logger;
public MyHttpHandler(ILogger<MyHttpHandler> logger) => _logger = logger;
public Task HandleAsync(HttpLoggingMessage message, CancellationToken token = default)
{
// 写入自定义存储(如文件/ES/消息队列/第三方平台)
_logger.LogInformation("{Method} {Path} from {IP}", message.Method, message.Path, message.IPAddress);
return Task.CompletedTask;
}
public void Dispose() { /* 释放资源 */ }
}
// 注册自定义处理器
builder.Services.AddLogging(logging =>
{
logging.AddHttpLogging<MyHttpHandler>(options =>
{
options.Key = "Logging.Http.Custom";
// 若不使用 SQL,可忽略连接串/过程名等
});
});
多实例:不同业务线或环境可使用不同
Key分别配置与隔离。中间件内部按 Key 解析当前配置与通道。
中间件采集说明
- IP 地址优先从代理/网关头部提取:
X-Forwarded-For、X-Real-IP、CF-Connecting-IP、True-Client-IP,取第一个非空;若无则回退HttpContext.Connection.RemoteIpAddress。 - 平台信息优先读取
Sec-CH-UA-Platform,否则解析User-Agent推断常见平台(windows/macos/ios/android/chromeos/linux)。 - 响应体采集使用包装
IHttpResponseBodyFeature,仅在开启EnableResponseBody时记录;请求体采集需开启EnableRequestBody并启用EnableBuffering,读取后会重置流位置。 - 仅采集
HttpMethods中包含的方法(大小写不敏感)。
从配置文件绑定
{
"Logging": {
"Http": {
"ConnectionString": "Server=localhost;Database=LogsDb;Trusted_Connection=True;TrustServerCertificate=True;",
"Application": "MyWebApp",
"TableName": "[dbo].[_Log_Http]",
"PorcName": "[dbo].[_Log_Http_Append]",
"SearchPorcName": "[dbo].[_Log_Http_Search]",
"Key": "Logging.Http",
"EnableRequest": true,
"EnableResponse": false,
"EnableHeaders": true,
"EnableAgent": true,
"EnableRequestBody": false,
"EnableResponseBody": false,
"RequestBodyLogLimit": 1048576,
"ResponseBodyLogLimit": 1048576,
"HttpMethods": ["GET", "POST", "PUT", "DELETE"]
}
}
}
builder.Services.AddLogging(logging =>
{
logging.AddHttpLogging(o => builder.Configuration.GetSection("Logging:Http").Bind(o));
});
app.UseHttpAccessLogging();
HTTP 日志消息结构
HttpLoggingMessage 字段:
- Application:应用名
- Target:业务目标标识(如资源/路由标识),由
HttpLoggingOptions.GenerateTarget(HttpContext)生成 - Machine:机器名
- SystemOs:操作系统信息
- Method:HTTP 方法
- Path:请求路径
- IPAddress:客户端 IP(优先代理头)
- Agent:User-Agent
- Headers:请求/响应头(文本串,按“Key:Value,Key:Value”格式拼接)
- Direction:
Request或Response - Content:请求或响应主体(受
EnableRequestBody/EnableResponseBody与大小限制影响) - Propertys:键值对扩展存储;可通过
HttpLoggingOptions.GenerateProperties(HttpContext, IDictionary<string,string>)添加自定义属性
数据库初始化(IHttpLoggingDatabaseBuilder)
- 通过
IHttpLoggingDatabaseBuilder.Build()生成建表与存储过程脚本(表:TableName;写入过程:PorcName;查询过程:SearchPorcName)。 - 在
HttpLoggingHostedService中会根据当前选项调用数据库脚本构建器以初始化对象(若不存在)。 - 生产建议:审阅并在变更流程中执行脚本,确保权限与索引优化。
客户端维护能力(IHttpLoggingClient)
在控制器或后台任务中注入使用:
app.MapGet("/logs/search", async (HttpLoggingClientFactory factory, string? path) =>
{
var client = factory.CreateClient(); // 默认使用 Key=Logging.Http.SqlServer
var result = await client.SearchAsync(new HttpLoggingSearchOptions
{
Path = path,
PageIndex = 1,
PageSize = 50
});
return Results.Ok(result);
});
app.MapPost("/logs/add", async (HttpLoggingClientFactory factory) =>
{
var client = factory.CreateClient();
await client.AddAsync(new HttpLoggingMessage
{
Application = "MyWebApp",
Method = "POST",
Path = "/logs/add",
Direction = HttpDirection.Request,
Content = "manual"
});
return Results.Ok();
});
app.MapDelete("/logs/{id:long}", async (HttpLoggingClientFactory factory, long id) =>
{
var client = factory.CreateClient();
var affected = await client.DeleteAsync(id);
return Results.Ok(new { affected });
});
app.MapDelete("/logs", async (HttpLoggingClientFactory factory, DateTime? before) =>
{
var client = factory.CreateClient();
var affected = await client.DeleteAsync(new HttpLoggingSearchOptions { EndTime = before });
return Results.Ok(new { affected });
});
app.MapPost("/logs/clear", async (HttpLoggingClientFactory factory) =>
{
var client = factory.CreateClient();
await client.ClearAsyc();
return Results.Ok();
});
- 解析客户端:
var client = factory.CreateClient("Logging.Http");指定 Key 获取对应实例。 - 查询过程:依赖
SearchPorcName返回分页结果集。
参数配置详解(HttpLoggingOptions)
- Key:实例隔离键。默认
Logging.Http。 - ConnectionString:SQL Server 连接串。
- TableName:日志表名,默认
[dbo].[_Log_Http]。 - PorcName:写入存储过程名,默认
[dbo].[_Log_Http_Append]。 - SearchPorcName:查询存储过程名,默认
[dbo].[_Log_Http_Search]。 - Application:应用标识,默认
AppDomain.CurrentDomain.FriendlyName。 - EnableRequest:是否记录请求信息(默认 true)。
- EnableResponse:是否记录响应信息(默认 false)。
- EnableHeaders:是否记录头(默认 true)。
- EnableAgent:是否记录 User-Agent(默认 true)。
- EnableRequestBody:是否记录请求体(默认 false)。
- EnableResponseBody:是否记录响应体(默认 false)。
- RequestBodyLogLimit:请求体记录大小上限(默认 1MB)。
- ResponseBodyLogLimit:响应体记录大小上限(默认 1MB)。
- HttpMethods:需要采集的 HTTP 方法列表(大小写不敏感)。
- GenerateTarget(HttpContext):生成
Target业务标识的委托(可选)。 - GenerateProperties(HttpContext, IDictionary<string,string>):添加自定义属性的委托(可选)。
表结构与存储过程约定
建议表结构(与默认处理器写入字段一致,含必要索引):
- ID bigint identity primary key
- Application nvarchar(128)
- Machine nvarchar(128)
- SystemOs nvarchar(256)
- Method nvarchar(16)
- Path nvarchar(1024)
- IPAddress nvarchar(64)
- Agent nvarchar(512) null
- Headers nvarchar(max) null
- Direction tinyint -- 0=Request,1=Response
- Content nvarchar(max) null
- Time datetime default(getdate())
写入过程 PorcName:入参与 HttpLoggingMessage 字段对应,插入一条记录。
查询过程 SearchPorcName:根据 HttpLoggingSearchOptions 支持模糊与时间范围,返回两个结果集(总数 + 当前页)。
注意:本库不强制自动建表/建过程,生产环境建议手工创建并做索引优化(Path/Time/Method/Application 等)。
进阶说明
- 多实例:不同
Key将创建独立Channel、处理器与客户端;中间件解析指定 Key 的选项与资源。 - 中间件顺序:务必在
UseRouting之前调用UseHttpAccessLogging,以捕获完整请求。 - 连接重建:默认处理器在连接异常或连接串变化时自动重建
SqlConnection。 - Dapper 参数:向存储过程传参时建议使用匿名对象或
DynamicParameters,避免把Propertys等不需要的属性传入过程。 - 性能建议:
- 默认关闭消息体采集;在排障时短期打开并设置大小上限。
- 高并发场景队列满会丢弃最旧日志,需结合业务容忍度评估并可根据源码调整策略。
- 分库或分表可降低单表压力。
常见问题(FAQ)
- 中间件未生效?确保已调用
app.UseHttpAccessLogging()且顺序在UseRouting()前。 - 无法写入 SQL?检查连接串、过程名、权限与过程参数是否一致。
- 如何按环境隔离?使用不同的
Key、不同的表/过程或数据库。 - 客户端真实 IP 异常?确认代理头是否正确传递(X-Forwarded-For 等)。
- 是否支持 .NET 8/10?项目目标为 .NET 8,可在更高版本运行。
许可证
版权所有 © netor.me 本项目遵循 MIT 协议。
联系方式
- 官网: http://netor.me
- 邮箱: support@netor.me
Showing the top 20 packages that depend on Microsoft.Extensions.Logging.HttpLogging.
| Packages | Downloads |
|---|---|
|
Microsoft.Extensions.Logging.Operates
将操作日志记录到数据库,支持高并发缓冲与后台批量入库。自动建表与存储过程,轻松实现集中日志持久化与查询分析。
|
3 |
2025年11月30日
- 新增agent搜索
2025年12月1日
- 新增自定义扩展
2025年12月1日
- 新增记录httpmethod的筛选配置
2025年12月3日
- 重构可以通过 key 隔离 多个实列
- 可以单独使用 AddHttpLoggingClient 只集成客户端
- 自定义指定客户端
- 去掉过度包装的 key 隔离 多个实列
2025年12月4日
- 修复部分bug
.NET 8.0
- Dapper (>= 2.1.66)
- Microsoft.Data.SqlClient (>= 6.0.2)
- Netor.Extensions.StringExtensions (>= 6.1.1)