log4net 简介以及简单示例(.net8)

〇、前言

log4net 是一个广泛使用的、功能强大的日志记录库,专为 .NET 平台设计。

它源自 Java 社区中非常流行的日志框架 log4j,并继承了其灵活、高效和可配置的特点。

log4net 允许开发者在 .net 应用程序(包括 ASP.NET、.NET Core、.NET Framework、Windows Forms、WPF 等)中轻松地添加日志功能。

本文就来进行简单的汇总介绍,供参考。

另外,但由于 log4net 是较老的日志框架,其生态在 .NET Core/.NET 5+ 环境中支持有限,因此使用时需注意兼容性。

其实现在更流行的框架有很多值得推荐,例如 Serilog、Microsoft.Extensions.Logging 等等,博主后续也将进行简单的实践。

一、log4net 基础

log4net 的配置通常放在 log4net.config,app.config, web.config 或一个独立的 .xml 文件中。

下面看下一个简单的配置示例:

复制代码
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <!-- 根日志配置 -->
  <root>
    <level value="DEBUG" />
    <appender-ref ref="FileAppender" />
  </root>
  <!-- 屏蔽 Microsoft 开头的所有日志(包括 Hosting.Lifetime) -->
  <logger name="Microsoft" additivity="false">
    <level value="OFF" />
  </logger>
  <!-- 屏蔽 System 开头的日志(可选) -->
  <logger name="System" additivity="false">
    <level value="OFF" />
  </logger>

  <!-- 文件输出器 -->
  <appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
    <!-- 根据操作系统动态设置日志路径 -->
    <file value="logs/logfile" />
    <datePattern value="yyyy-MM-dd'.log'" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10MB" />
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
  </appender>
</log4net>

1.1 配置文件关键节点有哪些?root、logger、appender、level

  • <root>记录器:必要、唯一

<root>是 Logger 层次结构的根节点,所有 Logger 都是它的后代。

必须配置,且只能有一个。

通常在这里设置应用程序的默认日志级别和主要的 Appender。例如:

复制代码
<!-- 根记录器 (Root Logger) -->
<!-- 这是所有 Logger 的父级,必须存在且唯一 -->
<root>
    <!-- 设置根记录器的日志级别 -->
    <level value="DEBUG" />
    <!-- 引用已定义的 Appender,可以添加多个 -->
    <appender-ref ref="ConsoleTestAppender" />
    <appender-ref ref="ConsoleAppender" />
</root>
  • <logger>记录器:非必要、可多个

用于为特定的命名空间或类创建自定义的 Logger。

name 属性通常使用完整的类名或命名空间名(如 MyApp.Services.UserService)。

可以设置与根记录器不同的 level,也可以引用不同的 appender-ref。

复制代码
<!-- 根记录器 (Root) -->
<root>
    <level value="DEBUG" />
    <!-- 根记录器使用 ConsoleAppender -->
    <appender-ref ref="ConsoleAppender" />
</root>

<!-- Logger 1: MyApp.Services 命名空间 -->
<logger name="MyApp.Services">
    <level value="INFO" />
    <!-- 关键:设置了 additivity="false" -->
    <additivity value="false" />
    <!-- 只使用 FileAppender -->
    <appender-ref ref="FileAppender" />
</logger>

<!-- Logger 2: MyApp.Data 命名空间 -->
<logger name="MyApp.Data">
    <level value="DEBUG" />
    <!-- 没有设置 additivity,默认为 true -->
    <!-- 使用自己的 Appender 和 继承根记录器的 Appender -->
    <appender-ref ref="FileAppender" />
</logger>

additivity 属性:

true(默认):日志消息会输出到当前 Logger 的【所有 Appender 以及其所有祖先 Logger 的 Appender 】。

false:日志消息只输出到当前 Logger 明确引用的 Appender,不会传递给父级 Logger。这在需要隔离日志输出时非常有用。

  • <appender>配置:必要、可多个

name:Appender 的唯一标识符,供 Logger 引用。

type:Appender 的完全限定类型名。

内部的 <layout> 定义了日志的输出格式。PatternLayout 是最灵活的。

conversionPattern 格式化字符串,常用占位符:

|---------------------|-------------------------------------------------|
| 占位符 | 含义 |
| %date{格式} | 时间戳,如 %date{yyyy-MM-dd HH:mm:ss,fff} |
| %thread | 线程名 |
| %level | 日志级别(DEBUG, INFO 等) |
| %logger{N} | Logger 名称,{N} 表示只显示最后 N 段(如 %logger{1} 只显示类名) |
| %message | 日志消息 |
| %exception | 异常堆栈信息 |
| %newline | 换行 |
| %highlight{pattern} | 在支持的 Appender(如 ColoredConsoleAppender)中为文本添加颜色 |

如下示例,包含节点应用和解释(包含文件输出、控制台输出、数据库输出):

复制代码
<!-- ============ 定义 Appender (输出目标) ============ -->
<!-- Appender 1: 滚动文件输出器 -->
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <!-- 日志文件的路径和名称 -->
    <file value="logs\\application.log" />
    <!-- 是否追加到现有文件 -->
    <appendToFile value="true" />
    <!-- 滚动方式: Size(按大小) 或 Date(按日期) -->
    <rollingStyle value="Size" />
    <!-- 最多保留的备份文件数量 -->
    <maxSizeRollBackups value="5" />
    <!-- 单个日志文件的最大大小 -->
    <maximumFileSize value="10MB" />
    <!-- 归档后文件名是否保持不变 -->
    <staticLogFileName value="true" />
    <!-- 归档模式 -->
    <countDirection value="1" />

    <!-- 布局 (Layout): 定义日志格式 -->
    <layout type="log4net.Layout.PatternLayout">
        <!-- 转换模式 (Conversion Pattern) -->
        <conversionPattern value="%date{yyyy-MM-dd HH:mm:ss,fff} [%thread] %-5level %logger{1} - %message%newline%exception" />
    </layout>
</appender>

<!-- Appender 2: 控制台输出器 -->
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
    <!-- 使用彩色输出 -->
    <target value="Console.Out" />
    <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%highlight{%-5level} %logger{1} - %message%newline" />
    </layout>
</appender>

<!-- Appender 3: 数据库输出器 (示例) -->
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
    <bufferSize value="1" />
    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data" />
    <connectionString value="Data Source=.;Initial Catalog=MyAppLogs;Integrated Security=True;" />
    <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
    <parameter>
        <parameterName value="@log_date" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    <parameter>
        <parameterName value="@thread" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%thread" />
        </layout>
    </parameter>
    <parameter>
        <parameterName value="@log_level" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%level" />
        </layout>
    </parameter>
    <parameter>
        <parameterName value="@logger" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%logger" />
        </layout>
    </parameter>
    <parameter>
        <parameterName value="@message" />
        <dbType value="String" />
        <size value="4000" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%message" />
        </layout>
    </parameter>
    <parameter>
        <parameterName value="@exception" />
        <dbType value="String" />
        <size value="2000" />
        <layout type="log4net.Layout.ExceptionLayout" />
    </parameter>
</appender>
  • <level>配置:

在 <root> 或 <logger> 内部使用。

value 属性设置具体的级别 (DEBUG, INFO, WARN, ERROR, FATAL, OFF, ALL)。

1.2 layout 中有哪些固有参数

参数 含义 示例 其他要点
%m 或 %message 日志消息(loggingEvent.MessageObject) 用户登录成功
%n 换行符(平台相关:Windows 是 \r\n,Unix 是 \n) 换行
%t 或 %thread 线程名 MainThread
%p 或 %level 日志级别(Level) INFO, ERROR %-5level:左对齐,最小宽度 5(如 INFO ) %5level:右对齐,最小宽度 5(如 INFO)
%c 或 %logger 记录器名称(Logger Name) MyApp.Services.UserService %.10logger:最多显示 logger 的前 10 个字符 %-40[ %logger{1} ]:左对齐 40 字符,只显示 logger 最后一级
%C 调用者类名(需要 StackTrace 支持,性能较差) UserService
%M 调用者方法名(同上,需 StackTrace) Login
%F 调用者源文件名(需调试信息 .pdb) UserService.cs
%L 调用者行号(需 .pdb) 45
%l 完整位置信息 = %F:%L UserService.cs:45
%d 或 %date 日期时间 %d{yyyy-MM-dd HH:mm:ss} → 2025-10-14 23:30:00 %d{yyyy-MM-dd HH:mm:ss.fff} %date{ISO8601}
%r 从程序启动到记录日志所经过的毫秒数 12345
%P 当前进程 ID 1234
%x 或 %ndc NDC(Nested Diagnostic Context)栈内容 User123
%X{key} MDC(Mapped Diagnostic Context)中的键值 %X{userid} → 123
%exception 异常信息(包括类型、消息、堆栈) System.NullReferenceException: Object reference not set...
%property{key} Log4net 全局属性或日志事件属性 %property{hostname}, %property{ThreadName}
%newline 换行符(同 %n) 换行
%type 日志事件的类型(通常是 LoggingEvent) log4net.Core.LoggingEvent
%location 位置信息(等价于 %C.%M(%F:%L)) UserService.Login(UserService.cs:45)
转换符 注意事项
%C, %M, %F, %L, %l, %location 需要生成 StackTrace,影响性能,建议仅在调试环境使用
%X{key}, %property{key} 可用于添加上下文信息(如用户ID、请求ID)
%exception 只有当 exception 不为 null 时才输出

二、简单示例测试

2.1 打印出各个级别的日志信息

1)首先引用两个依赖包:log4netMicrosoft.Extensions.Logging.Log4Net.AspNetCore

2)在项目主目录下添加配置文件 log4net.config:

复制代码
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <!-- 根日志配置 -->
  <root>
    <level value="DEBUG" />
    <appender-ref ref="FileAppender" />
  </root>
  <!-- 屏蔽 Microsoft 开头的所有日志(包括 Hosting.Lifetime) -->
  <logger name="Microsoft" additivity="false">
    <level value="OFF" />
  </logger>
  <!-- 屏蔽 System 开头的日志(可选) -->
  <logger name="System" additivity="false">
    <level value="OFF" />
  </logger>

  <!-- 文件输出器 -->
  <appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
    <!-- 根据操作系统动态设置日志路径 -->
    <file value="logs/logfile" />
    <datePattern value="yyyy-MM-dd'.log'" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10MB" />
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
  </appender>
</log4net>

3)在 Program.cs 文件中添加一行代码builder.Logging.AddLog4Net("log4net.config");

作用是,将 log4net 日志框架集成到 .NET 的内置日志系统(Microsoft.Extensions.Logging)中,并使用指定的配置文件进行初始化。

复制代码
var builder = WebApplication.CreateBuilder(args);

builder.Logging.AddLog4Net("log4net.config"); // 此行

// Add services to the container.
builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseAuthorization();

app.MapControllers();
app.Run();

4)最后,在控制器中如下配置,进行日志记录:

复制代码
using log4net;
using Microsoft.AspNetCore.Mvc;

namespace Test.WebAPI._8._0.Log4net.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public void Get()
        {
            _logger.LogInformation("信息");
            _logger.LogError("错误");
            _logger.LogWarning("警告");
            _logger.LogDebug("调试");
        }
    }
}

5)验证

启动项目,并通过第三方接口测试应用,调用 Get 接口,触发日志记录。

复制代码
2025-10-09 21:51:04,811 [12] INFO  Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController - 信息
2025-10-09 21:51:04,839 [12] ERROR Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController - 错误
2025-10-09 21:51:04,841 [12] WARN  Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController - 警告
2025-10-09 21:51:04,842 [12] DEBUG Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController - 调试

2.2 如何输出 json 格式的字符串

其实实现起来很简单,就是创建自定义 PatternLayoutConverter 并注册到 Layout。

作用就是,把需要输出的内容进行格式化,转义成字符串,避免把这个信息塞进 json 的一个字段后出现格式校验失败。

如下一个转换器的示例:

复制代码
using log4net.Core;
using log4net.Layout;
using log4net.Layout.Pattern;
using System.Web;

namespace Test.WebAPI._8._0.Log4net.PublicClass
{
    public class JsonPatternLayout : PatternLayout
    {
        public JsonPatternLayout()
        {
            // 注册自定义转换器名称,在配置文件中使用这个 %escapedmessage 来指代要记录的日志内容
            this.AddConverter("escapedmessage", typeof(EscapedMessagePatternConverter));
        }
    }
    public class EscapedMessagePatternConverter : PatternLayoutConverter
    {
        protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
        {
            string message = loggingEvent.RenderedMessage ?? string.Empty;
            // 使用 JavaScriptStringEncode 进行转义,适合嵌入 JSON
            string escapedMessage = HttpUtility.JavaScriptStringEncode(message);
            writer.Write(escapedMessage);
        }
    }
}

然后在 log4net.config 配置文件中,把日志的输出格式变更下:

复制代码
<layout type="Test.WebAPI._8._0.Log4net.PublicClass.JsonPatternLayout">
   <conversionPattern value="{&quot;time&quot;:&quot;%date{yyyy-MM-dd HH:mm:ss}&quot;, &quot;level&quot;:&quot;%level&quot;, &quot;message&quot;:&quot;%escapedmessage&quot;, &quot;logger&quot;:&quot;%logger&quot;}%n"/>
</layout>

注意其中的自定义参数引用:%escapedmessage。

如下是输出示例:

复制代码
{"time":"2025-10-14 21:56:43", "level":"INFO", "message":"信息", "logger":"Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController"}
{"time":"2025-10-14 21:58:16", "level":"ERROR", "message":"错误", "logger":"Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController"}
{"time":"2025-10-14 21:58:17", "level":"WARN", "message":"警告", "logger":"Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController"}
{"time":"2025-10-14 21:58:18", "level":"DEBUG", "message":"调试", "logger":"Test.WebAPI._8._0.Log4net.Controllers.WeatherForecastController"}

参考:https://blog.csdn.net/qq_61632126/article/details/136475013

三、遇到的一些问题

3.1 Debug 日志没有打印出来,其他级别正常

主要是因为项目配置里的信息,默认为 Information,此级别高于 Debug,因此需要如下修改。

复制代码
// appsetting.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information", // 注意这里,配置需改为 Debug
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
// appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information", // 注意这里,配置需改为 Debug
      "Microsoft.AspNetCore": "Warning"
    }
  }
}
相关推荐
间彧3 小时前
Spring Boot分布式WebSocket实现指南:项目实战与代码详解
后端
间彧3 小时前
Spring Boot集成WebSocket项目实战详解
后端
该用户已不存在5 小时前
工具用得好,Python写得妙,9个效率工具你值得拥有
后端·python·编程语言
im_AMBER6 小时前
Web 开发 30
前端·笔记·后端·学习·web
码事漫谈6 小时前
LLVM IR深度技术解析:架构、优化与应用
后端
码事漫谈6 小时前
C++ 中的类型转换:深入理解 static_cast 与 C风格转换的本质区别
后端
小蒜学长6 小时前
springboot餐厅信息管理系统设计(代码+数据库+LW)
java·数据库·spring boot·后端
Chh432247 小时前
React 新版
后端
Miracle6587 小时前
【征文计划】Rokid CXR-M SDK全解析:从设备连接到语音交互的AR协同开发指南
后端