log4net 不再推荐用于新项目,因多年未更新、不支持 .net core ilogger 标准、配置易错、多线程下可能返回 null、与 di 冲突、无作用域支持、异步写入需额外封装;推荐使用 microsoft.extensions.logging,更轻量、易配置、原生支持结构化日志、轮转和脱敏。

Log4Net 不再推荐用于新项目,.NET 6+ 原生 Microsoft.Extensions.Logging 更轻量、更易配置、无外部依赖。
为什么 Log4Net 在新项目里不建议用
Log4Net 已多年未更新(最后稳定版 2017 年),不支持 .NET Core 的 ILogger 标准接口,需手动桥接;配置 XML 易出错,log4net.Config.XmlConfigurator.Configure() 调用时机不对会导致日志静默丢失;多线程下若未正确初始化,LogManager.GetLogger() 可能返回 null 或空实例。
常见错误现象:logger.Info("test") 执行后文件没生成、控制台无输出、甚至程序启动报 NullReferenceException。
- 它依赖静态初始化器,和 ASP.NET Core 的 DI 生命周期天然冲突
- 没有内置作用域(Scope)支持,无法自动记录请求 ID、用户 ID 等上下文
- 异步写入需额外封装,否则高并发下 I/O 阻塞主线程
用 Microsoft.Extensions.Logging 写文件日志(最简可行)
不需要 NuGet 引入 Log4Net,只需添加 Microsoft.Extensions.Logging.Console 和 Microsoft.Extensions.Logging.File(后者是社区维护的轻量扩展,非微软官方,但稳定)。
实操步骤:
- 安装包:
dotnet add package Microsoft.Extensions.Logging.Console+dotnet add package Microsoft.Extensions.Logging.File - 在
Program.cs中配置(.NET 6+):var builder = WebApplication.CreateBuilder(args); builder.Logging.ClearProviders(); builder.Logging.AddConsole(); builder.Logging.AddFile("logs/app-{Date}.txt"); - 使用时直接注入
ILogger<myservice></myservice>,或通过ILoggerFactory创建,无需静态类或全局 Logger 实例
注意:AddFile 默认仅写入 Error 级别以上;如需 Info 级别,需加过滤器:
builder.Logging.AddFile(o => o.LogLevel = LogLevel.Information);
自定义日志格式与结构化数据(避免字符串拼接)
别再写 logger.LogInformation($"User {id} logged in at {DateTime.Now}") —— 这种写法丢失结构化能力,后续无法按 id 字段检索。
正确方式是用占位符 + 参数对象:
logger.LogInformation("User {UserId} logged in from {IpAddress}", userId, ipAddress);
这样生成的 JSON 日志(配合 JsonConsoleFormatter)会包含独立字段 "UserId":123 和 "IpAddress":"192.168.1.5",方便 ELK 或 Seq 查询。
- 字段名必须用花括号包裹,且不能含空格或特殊符号
- 参数顺序必须严格匹配占位符顺序
- 若传入
null,对应字段值为null,不会抛异常
生产环境绕不开的两个细节
一是日志轮转(rolling):别让单个文件无限增长。用 Microsoft.Extensions.Logging.File 时,设置 MaxFileSize 和 MaxRollingFiles 即可:
builder.Logging.AddFile(o => {
o.FileName = "logs/app-{Date}.txt";
o.MaxFileSize = 10 * 1024 * 1024; // 10MB
o.MaxRollingFiles = 5;
});
二是敏感信息过滤:密码、token、身份证号等绝不能原样进日志。不要依赖“开发时删掉那行 logger”,而应在日志提供器层拦截:
- 实现
IExternalScopeProvider或包装ILogger,对日志消息做正则脱敏 - 更稳妥的是,在业务代码中显式调用
Sanitize()方法处理参数,再传给 logger - 尤其注意 Exception.ToString() 可能含堆栈里的原始 SQL 或 HTTP Body,应改用
ex.Message或白名单属性提取
轮转策略和字段脱敏这两项,Log4Net 默认不提供开箱即用方案,而原生日志生态有现成中间件和约定做法——省下的不是代码量,是排查线上问题的时间。










