690 字
3 分钟
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)#

关键参数说明:

  • bufferSize: 设置为 0 表示即时写入。在生产高并发环境下,建议设为 10-100 以减轻数据库压力(利用批处理)。
  • connectionType: 需指向对应的数据库驱动程序。
<?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 或是自定义的后台界面,你就能实时监控系统的健康状况。

C# Log4Net 学习笔记:将日志实时记录到数据库
https://sw.rscclub.website/posts/csharplog4netsqlserver/
作者
杨月昌
发布于
2021-12-18
许可协议
CC BY-NC-SA 4.0