EF Core 6+ 推荐用 DbCommandInterceptor 实现 SQL 日志记录,需在 AddDbContext 配置中注册线程安全实例,重写 CommandExecuting/CommandExecuted;避免 LogTo 的语义日志局限,兼顾参数安全格式化、异步落盘与采样策略。
EF Core 中如何注册 DbCommandInterceptor 实现 SQL 日志记录
EF Core 6+ 推荐用 DbCommandInterceptor 拦截原始 SQL 执行,它比旧版的 IDbCommandTreeInterceptor 更轻量、更直接。注册必须在 DbContextOptionsBuilder 阶段完成,且拦截器实例需是线程安全的(不能带状态,或自行同步)。
常见错误:把拦截器当普通服务注入到构造函数里,或在 OnConfiguring 中重复 AddInterceptors —— 这会导致多次注册、日志重复甚至异常。
- 在
Startup.cs或Program.cs的services.AddDbContext配置中调用.AddInterceptors(new SqlLoggerInterceptor()) - 拦截器类需继承
DbCommandInterceptor,重写CommandExecuting(同步执行前)或CommandExecuted(执行后) - 若只需看 SQL,
CommandExecuting足够;若还要查耗时或结果行数,用CommandExecuted并检查result是否为DbDataReader或int
CommandExecuting 中如何安全提取参数化 SQL 和参数值
EF Core 默认使用参数化查询,DbCommand.CommandText 是带 @param 的模板,DbCommand.Parameters 是 DbParameter 集合。直接拼接易出错,尤其遇到 DBNull.Value、二进制数据或日期时区问题。
别手动遍历 Parameters 拼字符串 —— 容易漏转义、类型不一致、日志格式混乱。推荐用现成工具辅助格式化。
- 用
command.Parameters.Cast().Select(p => $"{p.ParameterName} = {p.Value ?? "NULL"}").ToArray()快速调试,但仅限开发环境 - 生产环境建议用
Microsoft.Data.SqlClient的SqlClientFactory.CreateCommand()+ToString()行为模拟,或引入轻量库如EFCore.SqlServer.Interceptors(非官方)做安全展开 - 注意:
p.Value可能是DateTimeOffset、byte[]、Guid,打印前先ToString("O")或Convert.ToString()避免ToString()返回空或乱码
为什么 LogTo 不够用,还得自己写拦截器
DbContextOptionsBuilder.LogTo 确实能打日志,但它输出的是 EF Core 内部语义(如 “Executing DbCommand [Parameters=[@p0=’xxx’]]”),不包含真实 SQL 文本,也不方便对接 ELK、Seq 等结构化日志系统。