一、为什么要在WPF中整合Log4net
开发过WPF应用的朋友都知道,当程序在客户现场出现问题时,最头疼的就是找不到日志。控制台输出在正式环境中根本看不见,自己写文本日志又容易遇到性能问题。这时候就需要一个成熟的日志框架——Log4net。
Log4net是Apache推出的.NET日志记录组件,它支持多种输出方式(文件、数据库、控制台等),能根据日志级别灵活过滤信息,还能自动按日期或大小分割日志文件。在WPF中整合它,相当于给应用装了个"黑匣子",发生异常时就能快速定位问题。
二、Log4net的安装与基础配置
首先通过NuGet安装Log4net包:
Install-Package log4net
接着在App.xaml.cs中初始化配置。建议将配置文件独立为log4net.config,这样修改时无需重新编译:
// App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// 加载配置文件(确保文件属性设置为"始终复制")
var configFile = new FileInfo("log4net.config");
XmlConfigurator.ConfigureAndWatch(configFile);
// 测试日志输出
ILog logger = LogManager.GetLogger(typeof(App));
logger.Info("应用程序启动完成");
}
}
对应的log4net.config示例:
<!-- 日志输出到文件和控制台 -->
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs/Application.log" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
</log4net>
三、在MVVM模式中的实战应用
在WPF的MVVM架构中,推荐通过依赖注入方式使用日志。以下是结合Prism的示例:
// 注册Log4net服务
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<ILog>(LogManager.GetLogger(typeof(App)));
}
// ViewModel中使用
public class MainViewModel : BindableBase
{
private readonly ILog _logger;
public MainViewModel(ILog logger)
{
_logger = logger;
_logger.Debug("ViewModel初始化开始");
try {
// 业务逻辑代码...
}
catch (Exception ex) {
_logger.Error("业务逻辑异常", ex);
}
}
}
关键技巧:
- 对敏感信息使用
Debug级别,生产环境关闭该级别 - 异常日志一定要包含完整堆栈(
logger.Error(message, exception)) - 耗时操作用
using (LogExtension.TimeLog("操作名称"))封装
四、高级功能与性能优化
4.1 动态调整日志级别
通过配置文件热更新实现动态调整:
var repo = LogManager.GetRepository();
var level = LoggerManager.GetLogger(repo.Name).Level;
level = Level.Debug; // 运行时切换级别
repo.Threshold = level;
repo.RaiseConfigurationChanged();
4.2 异步日志记录
使用AsyncAppender避免阻塞UI线程:
<appender name="AsyncForwarder" type="Log4Net.Async.AsyncForwardingAppender">
<appender-ref ref="RollingFileAppender" />
</appender>
4.3 日志上下文增强
通过ThreadContext添加环境信息:
using(ThreadContext.Stacks["NDC"].Push("用户操作流")) {
_logger.Info("用户点击保存按钮");
}
五、典型问题排查指南
场景1:日志文件未生成
- 检查配置文件路径是否正确
- 确认日志目录有写入权限
- 在代码中添加
log4net.Util.LogLog.InternalDebugging = true开启内部调试
场景2:日志内容不完整
- 检查
<filter>标签是否过滤了某些级别 - 确认没有多个
log4net.config文件冲突
场景3:性能下降明显
- 避免在循环中记录
Debug日志 - 对高频日志改用
BufferingForwardingAppender
六、技术方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| Log4net | 功能全面,社区成熟 | 配置稍复杂 |
| NLog | 配置简单,性能优异 | 高级功能较少 |
| Serilog | 结构化日志支持好 | 依赖较多扩展包 |
七、总结与最佳实践
经过实际项目验证,推荐以下实践方案:
- 开发阶段使用
DEBUG级别+控制台输出 - 生产环境采用
INFO级别+按日分割文件 - 关键业务流程添加
TRACE级别详细日志 - 使用
<filter>过滤敏感信息日志
完整的示例项目结构建议:
/App
/Logs # 日志目录
/Configs
log4net.config # 日志配置
/Services
LoggerService.cs # 日志服务封装
评论