WPF(Prism8)中如何用Serilog实现运行时动态子日志配置?
用Serilog在WPF Prism 8中实现动态多项目日志记录
核心思路
完全可以通过Serilog的子日志(Logger.ForContext)结合动态Sink管理实现需求:
- 根日志负责记录全局应用信息(异常、关键操作)到固定的应用日志文件
- 为每个项目创建独立的子日志实例,绑定对应的项目日志文件,通过上下文标记区分项目,实现同时写入根日志和激活的项目日志
具体实现步骤
1. 初始化根日志(应用全局日志)
在Prism的App.xaml.cs中初始化根日志,负责记录所有应用级信息:
public partial class App : PrismApplication { protected override void OnStartup(StartupEventArgs e) { // 初始化根日志 Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .WriteTo.File( path: @"C:\Logs\AppLog-.txt", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}" ) .CreateLogger(); base.OnStartup(e); } protected override void OnExit(ExitEventArgs e) { Log.CloseAndFlush(); base.OnExit(e); } }
2. 封装项目日志管理类
创建单例的ProjectLogManager,管理所有项目的子日志实例并跟踪当前激活项目:
public class ProjectLogManager { private readonly Dictionary<string, ILogger> _projectLoggers = new(); private string _activeProjectId; // 获取当前激活的项目日志(若存在) public ILogger ActiveProjectLogger => _activeProjectId != null && _projectLoggers.TryGetValue(_activeProjectId, out var logger) ? logger : null; // 创建或获取项目日志实例 public ILogger GetOrCreateProjectLogger(string projectId, string projectLogFilePath) { if (_projectLoggers.TryGetValue(projectId, out var existingLogger)) return existingLogger; // 创建绑定项目文件的子日志,继承根日志配置 var projectLogger = Log.Logger.ForContext("ProjectId", projectId) .WriteTo.File( path: projectLogFilePath, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}" ) .CreateLogger(); _projectLoggers.Add(projectId, projectLogger); return projectLogger; } // 设置当前激活项目 public void SetActiveProject(string projectId) { _activeProjectId = projectId; } // 移除项目日志(项目关闭时清理) public void RemoveProjectLogger(string projectId) { if (_projectLoggers.TryGetValue(projectId, out var logger)) { logger.Dispose(); _projectLoggers.Remove(projectId); if (_activeProjectId == projectId) _activeProjectId = null; } } }
3. 在Prism中注入日志管理器
在Prism的容器注册中,将ProjectLogManager注册为单例:
protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<ProjectLogManager>(); // 其他服务注册... }
4. 业务代码中使用日志
在ViewModel或服务中注入ProjectLogManager,同时记录到根日志和激活项目日志:
public class ProjectViewModel : BindableBase { private readonly ILogger _rootLogger; private readonly ProjectLogManager _logManager; public ProjectViewModel(ILogger rootLogger, ProjectLogManager logManager) { _rootLogger = rootLogger; _logManager = logManager; } // 打开项目时创建并激活日志 public void OpenProject(string projectId, string projectFolder) { var logFilePath = Path.Combine(projectFolder, $"ProjectLog-.txt"); _logManager.GetOrCreateProjectLogger(projectId, logFilePath); _logManager.SetActiveProject(projectId); var message = $"项目 {projectId} 已激活"; _rootLogger.Information(message); _logManager.ActiveProjectLogger?.Information(message); } // 处理项目文件时记录日志 public void ProcessProjectFile(string fileName) { try { // 业务操作逻辑... var message = $"成功处理文件: {fileName}"; _rootLogger.Information(message); _logManager.ActiveProjectLogger?.Information(message); } catch (Exception ex) { var errorMsg = $"处理文件 {fileName} 失败"; _rootLogger.Error(ex, errorMsg); _logManager.ActiveProjectLogger?.Error(ex, errorMsg); } } // 关闭项目时清理日志 public void CloseProject(string projectId) { _logManager.RemoveProjectLogger(projectId); _rootLogger.Information($"项目 {projectId} 已关闭"); } }
5. 可选:添加项目日志过滤器
如果需要确保项目日志只记录对应项目的内容,可在创建子日志时添加过滤器:
var projectLogger = Log.Logger.ForContext("ProjectId", projectId) .Filter.ByIncludingOnly(evt => evt.Properties.ContainsKey("ProjectId") && evt.Properties["ProjectId"].ToString() == $"\"{projectId}\"") .WriteTo.File(...) .CreateLogger();
关键说明
- 子日志
Logger.ForContext会继承根日志的所有配置,同时可添加独立的Sink和过滤器 - 项目关闭时务必调用
RemoveProjectLogger清理资源,避免内存泄漏 - 切换激活项目只需调用
SetActiveProject,后续日志会自动写入新的项目文件
内容的提问来源于stack exchange,提问作者EuroEager
相关产品推荐
相关产品推荐

