在 WPF 应用程序中统一控制日志是一个常见需求,特别是在需要监控应用状态、调试问题或记录用户操作时。以下是实现统一日志控制的几种方法:
- 使用第三方日志库(推荐)
推荐库 - NLog:功能强大,支持多种目标(文件、数据库、控制台等)和配置方式。
- Serilog:结构化日志记录,与ASP.NET Core 集成良好。
- log4net:经典日志库,配置灵活。
示例:使用 NLog
- 安装包:
Install-Package NLog
Install-Package NLog.Config
- 配置 NLog(NLog.config):
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="logfile" xsi:type="File" fileName="log.txt" />
<target name="logconsole" xsi:type="Console" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile,logconsole" />
</rules>
</nlog>
- 在 App.xaml.cs 中初始化:
public partial class App : Application
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
protected override void OnStartup(StartupEventArgs e)
{
try
{
// 应用程序启动逻辑
Logger.Info("Application started");
base.OnStartup(e);
}
catch (Exception ex)
{
Logger.Fatal(ex, "Application startup failed");
throw;
}
}
protected override void OnExit(ExitEventArgs e)
{
Logger.Info("Application exited");
NLog.LogManager.Shutdown(); // 确保日志完全写入
base.OnExit(e);
}
}
- 在 ViewModel 或 Service 中使用:
public class MainViewModel
{
private readonly NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
public void SaveData()
{
try
{
_logger.Debug("Saving data...");
// 业务逻辑
_logger.Info("Data saved successfully");
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to save data");
}
}
}
- 自定义日志服务(轻量级方案)
如果你不想引入第三方库,可以创建一个简单的单例日志服务:
public class LoggerService
{
private static readonly Lazy<LoggerService> _instance =
new Lazy<LoggerService>(() => new LoggerService());
public static LoggerService Instance => _instance.Value;
private readonly string _logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "app.log");
private readonly object _lockObject = new object();
private LoggerService() { }
public void Log(string message, LogLevel level = LogLevel.Info)
{
var logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
// 写入文件
lock (_lockObject)
{
try
{
File.AppendAllLines(_logFilePath, new[] { logEntry });
}
catch (Exception ex)
{
// 日志写入失败的处理
Console.WriteLine($"Failed to write log: {ex.Message}");
}
}
// 也可以同时输出到控制台
Console.WriteLine(logEntry);
}
public void Debug(string message) => Log(message, LogLevel.Debug);
public void Info(string message) => Log(message, LogLevel.Info);
public void Warning(string message) => Log(message, LogLevel.Warning);
public void Error(string message, Exception ex = null)
{
var errorMessage = ex != null ? $"{message} - {ex}" : message;
Log(errorMessage, LogLevel.Error);
}
}
public enum LogLevel
{
Debug,
Info,
Warning,
Error,
Fatal
}
使用方式:
// 在App.xaml.cs中初始化
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
LoggerService.Instance.Info("Application started");
base.OnStartup(e);
}
}
// 在其他地方使用
public class MainViewModel
{
public void DoSomething()
{
LoggerService.Instance.Info("Doing something...");
try
{
// 业务逻辑
}
catch (Exception ex)
{
LoggerService.Instance.Error("Operation failed", ex);
}
}
}
- 在 UI 中显示实时日志(可选)
如果你需要在 WPF 界面中实时显示日志,可以创建一个日志视图:
<!-- MainWindow.xaml -->
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<!-- 主内容区域 -->
<Button Content="Do Something" Command="{Binding DoSomethingCommand}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<!-- 日志显示区域 -->
<TextBox Grid.Row="1" Text="{Binding LogText, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True" AcceptsReturn="True" VerticalScrollBarVisibility="Auto"/>
</Grid>
</Window>
// MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
private readonly StringBuilder _logBuilder = new StringBuilder();
private string _logText;
public string LogText
{
get => _logText;
set
{
_logText = value;
OnPropertyChanged(nameof(LogText));
}
}
public ICommand DoSomethingCommand { get; }
public MainViewModel()
{
DoSomethingCommand = new RelayCommand(DoSomething);
// 注册日志事件(使用NLog或自定义服务)
LoggerService.Instance.Logged += OnLogged;
}
private void OnLogged(string message)
{
// 在UI线程更新日志
Application.Current.Dispatcher.Invoke(() =>
{
_logBuilder.AppendLine(message);
LogText = _logBuilder.ToString();
});
}
private void DoSomething()
{
LoggerService.Instance.Info("Button clicked");
// 业务逻辑
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
- 配置日志级别和目标
无论是使用第三方库还是自定义服务,都建议支持动态配置: - 日志级别:控制记录哪些级别的日志(Debug、Info、Warning 等)。
- 日志目标:决定日志输出到文件、数据库、控制台还是 UI 界面。
例如,在 NLog 中可以通过修改配置文件动态调整日志行为,无需重新编译代码。
总结
推荐使用第三方日志库(如 NLog),因为它们提供了更完善的功能和更好的性能。对于简单应用,自定义日志服务也是一个不错的选择。实时日志显示可以帮助调试和监控应用状态,但要注意性能问题(特别是频繁写入时)。