欢迎光临
我们一直在努力

WinForm + log4net 企业级日志实战

群里有问我一个请教一个问题。我顺便写一个模板。并写出文档。

在实际项目开发中,我越来越意识到:

日志不是“能用就行”,而是必须“可维护、可分流、可扩展”。

因此我专门写了一个 WinForm + log4net 完整示例项目,实现:

  • ✅ 多级别日志分流
  • ✅ 控制台输出
  • ✅ 按日期滚动文件
  • ✅ 错误独立文件
  • ✅ Windows 事件日志写入
  • ✅ Fatal 邮件告警
  • ✅ 程序入口统一初始化
  • ✅ 配置文件热更新

下面我把完整实现思路 + 全部核心代码详细拆解。


一、日志初始化 —— 必须在程序入口完成

很多人喜欢在窗体里初始化日志,我不这么做。

我统一在 Program.cs 里完成配置加载。

Program.cs

using log4net;
using log4net.Config;
using System;
using System.IO;
using System.Windows.Forms;

namespace WinFormLog4netDemo
{
internal static class Program
{
[STAThread]
static void Main()
{
// ✅ 程序启动第一步:初始化日志系统
ConfigureLog4net();

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}

/// <summary>
/// 统一配置 log4net
/// </summary>
private static void ConfigureLog4net()
{
try
{
// 1. 定位配置文件
string configPath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"log4net.config");

FileInfo configFile = new FileInfo(configPath);

// 2. 检查配置文件是否存在
if (!configFile.Exists)
{
throw new FileNotFoundException("log4net 配置文件不存在", configPath);
}

// 3. 加载并监控配置文件(支持热更新)
XmlConfigurator.ConfigureAndWatch(configFile);

// 4. 写一条启动日志
ILog logger = LogManager.GetLogger(typeof(Program));
logger.Info("应用程序启动,log4net 配置加载成功");
}
catch (Exception ex)
{
MessageBox.Show(
$"log4net 配置初始化失败:{ex.Message}\\n{ex.StackTrace}",
"致命错误",
MessageBoxButtons.OK,
MessageBoxIcon.Stop
);

Environment.Exit(1);
}
}
}
}

为什么使用 ConfigureAndWatch?

因为它支持:

  • 修改 log4net.config 后自动生效
  • 不需要重启程序
  • 非常适合生产环境

二、日志调用层 —— Form1 实现

我在窗体中定义一个静态日志实例:

private static readonly ILog _logger =
LogManager.GetLogger(typeof(Form1));

使用当前类作为 logger 名称,方便定位日志来源。


Form1.cs 完整代码

using System;
using System.Windows.Forms;
using log4net;

namespace WinFormLog4netDemo
{
public partial class Form1 : Form
{
// ✅ 使用当前类作为日志名称
private static readonly ILog _logger =
LogManager.GetLogger(typeof(Form1));

public Form1()
{
InitializeComponent();
// 日志已在 Program 中完成初始化
}

/// <summary>
/// Debug 日志
/// </summary>
private void btnDebugLog_Click(object sender, EventArgs e)
{
_logger.Debug("用户点击了 Debug 按钮,调试信息:参数 x=123,参数 y=456");
ShowSuccessTip("Debug 日志已记录!");
}

/// <summary>
/// Info 日志
/// </summary>
private void btnInfoLog_Click(object sender, EventArgs e)
{
_logger.InfoFormat(
"用户[{0}]在[{1:yyyy-MM-dd HH:mm:ss}]点击了 Info 按钮",
Environment.UserName,
DateTime.Now
);
ShowSuccessTip("Info 日志已记录!");
}

/// <summary>
/// Warn 日志
/// </summary>
private void btnWarnLog_Click(object sender, EventArgs e)
{
_logger.Warn("用户输入的参数格式不规范(预期:数字,实际:字符串),已自动修正");
ShowWarningTip("Warn 日志已记录!");
}

/// <summary>
/// Error 日志(含异常堆栈)
/// </summary>
private void btnErrorLog_Click(object sender, EventArgs e)
{
try
{
int a = 10;
int b = 0;
int result = a / b;
}
catch (Exception ex)
{
_logger.Error("除法运算出现异常,已捕获并处理", ex);
ShowErrorTip("Error 日志已记录(含异常堆栈)!");
}
}

/// <summary>
/// Fatal 日志
/// </summary>
private void btnFatalLog_Click(object sender, EventArgs e)
{
_logger.Fatal("数据库连接池耗尽,无法执行任何数据库操作,程序即将终止");
ShowFatalTip("Fatal 日志已记录!");
}

#region 统一提示方法

private void ShowSuccessTip(string message)
{
MessageBox.Show(message, "操作成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}

private void ShowWarningTip(string message)
{
MessageBox.Show(message, "警告",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}

private void ShowErrorTip(string message)
{
MessageBox.Show(message, "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}

private void ShowFatalTip(string message)
{
MessageBox.Show(message, "致命错误",
MessageBoxButtons.OK, MessageBoxIcon.Stop);
}

#endregion
}
}


三、核心配置文件 log4net.config

这是整套日志系统的灵魂。


1️⃣ Debug → 控制台输出

<appender name="DebugConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%-5level] %logger – %message%newline" />
</layout>
<filter type="log4net.Filter.LevelMatchFilter">
<levelToMatch value="DEBUG" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>

特点: 只匹配 DEBUG,不让其他级别混入。


2️⃣ Info / Warn → 按日期滚动文件

<appender name="InfoWarnFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs/InfoWarn/" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="yyyyMMdd'.log'" />
<staticLogFileName value="false" />
<maxSizeRollBackups value="30" />
<encoding value="utf-8" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%-5level] %logger [%thread] – %message%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="INFO" />
<levelMax value="WARN" />
</filter>
</appender>


3️⃣ Error / Fatal → 单独错误文件

<appender name="ErrorFatalFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs/ErrorFatal/" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="yyyyMMdd'.log'" />
<staticLogFileName value="false" />
<encoding value="utf-8" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%-5level] %logger [%thread] – %message%newline%exception" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
</appender>


4️⃣ Windows 事件日志输出

<appender name="ErrorEventAppender" type="log4net.Appender.EventLogAppender">
<logName value="Application" />
<applicationName value="WinFormLog4netDemo" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%-5level] %logger – %message%newline%exception" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
</appender>


5️⃣ Fatal 邮件告警

<appender name="FatalMailAppender" type="log4net.Appender.SmtpAppender">
<to value="admin@your-domain.com" />
<from value="alert@your-domain.com" />
<subject value="【WinForm 程序致命错误告警】" />
<smtpHost value="smtp.qq.com" />
<smtpPort value="587" />
<enableSsl value="true" />
<smtpUsername value="alert@your-domain.com" />
<smtpPassword value="你的SMTP授权码" />
<bufferSize value="1" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [FATAL] %logger%newline%message%newline%exception" />
</layout>
<filter type="log4net.Filter.LevelMatchFilter">
<levelToMatch value="FATAL" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>


Root 节点

<root>
<level value="ALL" />
<appender-ref ref="DebugConsoleAppender" />
<appender-ref ref="InfoWarnFileAppender" />
<appender-ref ref="ErrorFatalFileAppender" />
<appender-ref ref="ErrorEventAppender" />
<appender-ref ref="FatalMailAppender" />
</root>


四、我的设计原则总结

在这个示例中,我遵循了三点原则:

  • 日志分级必须清晰
  • 错误必须独立文件
  • 致命问题必须告警
  • 这不是“写日志”, 而是“设计日志系统”。

    五、注意

  • 群友一直弄不起。原因是:log4net.config 配置文件的属性改了一下,复制输出到目录,始终复制 在这里插入图片描述 在这里插入图片描述
  • 如果实现下面功能需要以管理员运行 在这里插入图片描述

  • 👋 关注我!持续分享 C# 实战技巧、代码示例 & 技术干货

    赞(0)
    未经允许不得转载:171主机测评 » WinForm + log4net 企业级日志实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址