跳转到主要内容
杨月昌的软件博客
返回

C# Log4Net 学习笔记:将日志实时记录到数据库

在 C/S 架构的项目中,本地文件日志(.txt)虽然方便查看,但难以进行统计分析。通过 Log4Net 的 ADONetAppender,我们可以将异常信息和运行日志直接写入 SQL Server,便于后续通过 SQL 语句快速排查问题。


一、 数据准备:创建日志表

首先,我们需要在 SQL Server 中准备一张承载日志的表。由于日志数据增长极快,建议为 LogDate 字段建立索引。

CREATE TABLE [dbo].[LogDetail](
    [LogID] [INT] IDENTITY(1,1) NOT NULL,
    [LogDate] [DATETIME] NOT NULL,   -- 日志记录时间
    [LogThread] [VARCHAR](50) NULL,  -- 线程 ID
    [LogLevel] [VARCHAR](50) NULL,   -- 日志级别 (INFO/ERROR等)
    [Logger] [VARCHAR](255) NULL,    -- 记录器名称
    [LogMessage] [NVARCHAR](MAX) NULL, -- 日志详细内容
    CONSTRAINT [PK_LogDetail] PRIMARY KEY CLUSTERED ([LogID] ASC)
) ON [PRIMARY];

二、 Log4Net 核心配置解析

Log4Net 的强大之处在于其插件式架构。ADONetAppender 负责将 LoggingEvent 转化为数据库的一条记录。

2.1 配置文件 (ConfigFile/Log4NetToDB.config)

关键参数说明:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
    <bufferSize value="0" /> <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <connectionString value="Data Source=.;Initial Catalog=TestDB;User ID=sa;Password=********;" />
    
    <commandText value="INSERT INTO LogDetail (LogDate, LogLevel, LogThread, Logger, LogMessage) 
                        VALUES (@LogDate, @LogLevel, @LogThread, @Logger, @LogMessage)" />
    
    <parameter>
      <parameterName value="@LogDate" />
      <dbType value="DateTime" />
      <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    
    <parameter>
      <parameterName value="@LogMessage" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%message" />
      </layout>
    </parameter>
    
    </appender>

  <root>
    <level value="ALL" />
    <appender-ref ref="ADONetAppender" />
  </root>
</log4net>

三、 高级进阶:自定义日志内容处理

有时我们需要记录的不止是一个简单的字符串,而是一个复杂的对象。通过重写 PatternLayoutConverter,我们可以动态地从对象属性中提取值。

3.1 核心逻辑实现

CustomLayoutConverter.cs 负责反射提取自定义对象中的属性:

protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
    if (Option != null)
    {
        // 反射提取 LogContent.LogMessage
        var propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(Option);
        var value = propertyInfo?.GetValue(loggingEvent.MessageObject, null);
        WriteObject(writer, loggingEvent.Repository, value);
    }
}

四、 性能与避坑建议

  1. 异步写入:数据库写入是 IO 密集型操作。在高吞吐系统中,建议在外层嵌套一个 AsyncForwardingAppender(来自 log4net.Async 库),避免写日志阻塞业务线程。
  2. 数据库连接安全:不要在配置文件中明文存储密码。可以使用宝塔面板的加密功能或 .NET 自带的 configProtectionProvider
  3. 表维护计划:日志表会迅速膨胀。建议建立 SQL 代理作业,定期清理(如仅保留 30 天)或归档旧日志:
DELETE FROM LogDetail WHERE LogDate < DATEADD(day, -30, GETDATE());

总结

通过 Log4Net 记录日志到数据库,可以将离散的错误信息转化为有价值的资产。配合简单的 Grafana 或是自定义的后台界面,你就能实时监控系统的健康状况。



上一篇
如何在 Windows 系统中手动安装和配置 PNPM(2026 进阶版)
下一篇
使用 Next.js 和 Supabase 进行全栈开发:构建现代博客系统