NLog如何创建携带全部appender配置的logger完整副本?
NLog 对等功能实现说明
NLog 完全支持实现你当前基于 log4net 的 Logger 复制、保留全部输出配置的需求,且实现逻辑比 log4net 更轻量,不需要手动操作独立 Repository 层级。
核心概念映射
先对齐两个组件的对等概念,避免理解偏差:
- log4net 的 Appender = NLog 的 Target(日志输出目标)
- log4net 的 Repository/Hierarchy = NLog 的 LogFactory(默认使用全局
LogManager,也可创建独立实例做完全配置隔离) - log4net 的日志级别、过滤器绑定逻辑 = NLog 的 LoggingRule
对应实现代码
你原有三段逻辑可以直接按如下方式替换为 NLog 实现:
1. 插件Logger入口方法
using NLog; using NLog.Config; using NLog.Targets; private const string DEFAULT_ADDON = "DefaultAddon"; private static readonly object _logConfigLock = new object(); public static Logger CreateAddonLog(string addon) { // NLog 配置建议在应用启动时全局加载一次即可,这里保留原有动态加载逻辑做兼容 if (LogManager.Configuration == null || !LogManager.Configuration.AllTargets.Any()) { LogManager.LoadConfiguration(GetLogFilePath()); } if (string.IsNullOrEmpty(addon)) { return LogManager.GetLogger(DEFAULT_ADDON); } return GetOrCreateAddonLogger(addon, DEFAULT_ADDON); }
2. 插件Logger创建/缓存逻辑
不需要像log4net那样创建独立Repository,通过动态添加日志规则即可实现配置隔离,性能开销更低:
private static Logger GetOrCreateAddonLogger(string addonName, string sourceLoggerName) { var loggerName = $"{sourceLoggerName}.{addonName}"; var currentConfig = LogManager.Configuration; lock (_logConfigLock) { // 已存在对应规则直接返回缓存Logger if (currentConfig.FindRuleByName(loggerName) != null) { return LogManager.GetLogger(loggerName); } // 克隆源Logger关联的所有输出目标 var clonedTargets = CloneTargets(addonName, sourceLoggerName).ToArray(); // 继承源Logger的最低输出级别 var sourceLogger = LogManager.GetLogger(sourceLoggerName); LogLevel minLevel = sourceLogger.IsFatalEnabled ? LogLevel.Fatal : sourceLogger.IsErrorEnabled ? LogLevel.Error : sourceLogger.IsWarnEnabled ? LogLevel.Warn : sourceLogger.IsInfoEnabled ? LogLevel.Info : sourceLogger.IsDebugEnabled ? LogLevel.Debug : LogLevel.Trace; // 创建绑定当前插件Logger的规则 var addonRule = new LoggingRule(loggerName, minLevel, clonedTargets) { RuleName = loggerName }; // 如需复制源规则的过滤器,可遍历源规则的Filters集合加入addonRule即可 currentConfig.AddRule(addonRule); LogManager.ReconfigExistingLoggers(); } return LogManager.GetLogger(loggerName); }
3. 输出目标克隆逻辑
和你原有逻辑一致,对需要修改属性的EventLog目标新建实例复制配置,其他无修改的目标直接复用即可:
private static IEnumerable<Target> CloneTargets(string instanceName, string sourceLoggerName) { var currentConfig = LogManager.Configuration; var sourceRules = currentConfig.GetRulesForLoggerName(sourceLoggerName); var sourceTargets = sourceRules.SelectMany(r => r.Targets).Distinct(); foreach (var target in sourceTargets) { if (target is EventLogTarget srcEventLog) { var clonedEventLog = new EventLogTarget { Name = $"{srcEventLog.Name}{instanceName}", Log = srcEventLog.Log, Layout = srcEventLog.Layout, MachineName = srcEventLog.MachineName, Source = $"{srcEventLog.Source}-{instanceName}", // 对应原逻辑的ApplicationName MaxKilobytes = srcEventLog.MaxKilobytes }; // 复制原有过滤器 foreach (var filter in srcEventLog.Filters) { clonedEventLog.Filters.Add(filter); } yield return clonedEventLog; } else { // 不需要修改属性的目标直接复用,减少重复初始化开销 yield return target; } } }
补充说明
- 如果你需要和log4net创建独立Repository完全一致的强隔离效果,可以新建独立
LogFactory实例,该实例的配置、Logger、Target完全和全局LogManager隔离,适合插件完全独立管控日志的场景:var isolatedLogFactory = new LogFactory(new LoggingConfiguration()); // 可单独给isolatedLogFactory加载配置、添加规则、创建Logger - 动态调整配置后调用
LogManager.ReconfigExistingLoggers()即可让所有改动生效,不需要手动标记配置完成状态。 - 如果需要全量深度克隆所有Target而不是复用实例,给对应Target类型做成员wise克隆即可,NLog所有内置Target都支持这种方式,不会出现状态冲突。
内容的提问来源于stack exchange,提问作者lijevosmetalo
相关产品推荐
相关产品推荐

