Log4Net已停止更新且不支持.NET 6+,新项目应优先选用Microsoft.Extensions.Logging;若需兼容旧系统,须在首次GetLogger前调用XmlConfigurator.Configure()完成唯一初始化,并注意路径、权限及Core环境适配问题。

Log4Net 在 .NET Framework 项目中仍常见,但 Log4Net 已于 2023 年正式进入维护模式(no new features),且不支持 .NET 6+ 的原生 Microsoft.Extensions.Logging 抽象。如果你正在新项目中考虑日志方案,直接用 Microsoft.Extensions.Logging + Console/File 提供程序更稳妥;若必须对接遗留系统或已有 Log4Net 配置,则需严格注意初始化时机与线程安全问题。
如何正确初始化 Log4Net(避免“Logger is null”)
Log4Net 不会自动扫描配置,必须显式调用初始化方法,且只能执行一次 —— 多次调用不报错但会导致配置丢失或行为异常。
-
XmlConfigurator.Configure()仅加载 App.config/Web.config 中的<log4net></log4net>节点,要求配置节已注册(configSections中声明) - 推荐使用
XmlConfigurator.Configure(Assembly.GetCallingAssembly(), new FileInfo("log4net.config"))加载外部 XML 文件,更易维护和替换 - 必须在任何
LogManager.GetLogger()调用之前完成初始化,典型位置是Program.cs(.NET Framework)或Global.asax Application_Start - 在 ASP.NET Core 中无法直接使用此方式,因
LogManager依赖AppDomain,而 Core 使用HostBuilder模型
为什么 GetLogger(typeof(T)) 和 GetLogger("name") 行为不同
Log4Net 的 Logger 实例本质是按名称查找的,GetLogger(typeof(T)) 实际等价于 GetLogger(typeof(T).FullName),而 GetLogger("MyApp.Service") 创建的是独立命名空间下的 Logger。
- 二者日志级别、Appender 绑定完全独立 —— 即使配置中用了
<logger name="MyApp"></logger>,也不会匹配typeof(MyApp.Service.UserService)生成的 logger 名称 - 层级匹配靠点号(
.)分隔,如MyApp.Service是MyApp的子 Logger,可继承父级 Level 和 Appender,但需配置additivity="false"控制是否叠加 - 调试时可用
LogManager.GetCurrentLoggers()查看当前所有已创建的 Logger 名称,避免拼写错误或层级误解
FileAppender 写入失败的常见原因
日志文件打不开、没内容、权限报错,90% 出现在 FileAppender 配置环节。
-
File路径写相对路径(如"logs/app.log")时,实际基于AppDomain.CurrentDomain.BaseDirectory,不是 exe 所在目录(尤其 IIS 部署时 BaseDirectory 是 w3wp.exe 路径) - 未设置
AppendToFile="true"导致每次启动覆盖日志;未设LockingModel type="log4net.Appender.FileAppender+MinimalLock"会导致多进程/多 AppDomain 下写入失败(抛IOException) - Windows 下若路径含中文或空格,需确保 XML 中用双引号包裹,且应用有对应目录写入权限(IIS 应用池标识用户需授权)
- 建议改用
RollingFileAppender,配合MaximumFileSize和MaxSizeRollBackups,避免单文件过大或磁盘占满
与 ASP.NET Core 共存的现实路径
不能把 Log4Net 当作 .NET Core 日志提供程序直接注入,但可通过桥接方式保留旧日志逻辑。
- 用
Microsoft.Extensions.Logging.Log4NetProviderNuGet 包(非官方,如Microsoft.Extensions.Logging.Log4Net.AspNetCore),它把ILogger<t></t>调用转译为 Log4Net 的ILog调用 - 该 Provider 仅转发日志事件,不接管配置 —— 你仍需手动调用
XmlConfigurator.Configure()初始化 Log4Net - 注意:Log4Net 的
PatternLayout格式符(如%date %level %logger - %message)不会自动解析Microsoft.Extensions.Logging的结构化日志参数(logger.LogInformation("User {Id} logged in", userId)中的{Id}),会被原样输出为字符串 - 若需结构化日志,应放弃 Log4Net,改用
Serilog或NLog,它们原生支持Microsoft.Extensions.Logging的 message template 解析
Log4Net 的核心陷阱不在语法,而在生命周期管理 —— 初始化遗漏、多线程争抢配置、路径权限误判、以及对 .NET Core 运行模型的误用。一旦在生产环境出现日志静默,优先检查 LogManager.GetCurrentLoggers() 返回是否为空,再确认 XmlConfigurator 是否真的被执行过,而不是翻配置文件格式。


















