You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:33:26