【问题标题】:NLog create new log file per instance of classNLog为每个类实例创建新的日志文件
【发布时间】:2017-06-13 14:00:23
【问题描述】:

我有一个已经使用 NLog 的项目。应用程序创建给定类的实例,将 ILogger 的实例传递给它,然后在实例中调用“执行”方法 - 系统是多线程的,一次最多可以运行 200 个这样的实例,日志文件可以变得大而难以阅读。

我们需要为每个类的实例创建一个新的日志文件,并将其与该实例的输出一起存储(它们每个都有一个唯一的标识符)

我的问题是:是否可以让 NLog 为每个类的实例创建一个新文件?我知道你可以为不同的类创建不同的日志文件,我想要的是一个类的每个实例都有一个不同的日志文件。

我在网上查看过,但找不到太多这方面的信息,如果 NLog 已经满足了它,我不想通过创建自己的日志记录类来重新发明轮子。

下面是一个控制台应用程序,我已经打开它来演示重新配置现有记录器时会发生什么 - 本质上它会更改记录器的所有实例

class Program
{
    static void Main(string[] args)
    {
        BackgroundWorker bw1 = new BackgroundWorker();
        bw1.DoWork += Bw1_DoWork;
        bw1.RunWorkerAsync();

        BackgroundWorker bw2 = new BackgroundWorker();
        bw2.DoWork += Bw2_DoWork;
        bw2.RunWorkerAsync();

        Console.ReadLine();
    }

    private static void Bw1_DoWork(object sender, DoWorkEventArgs e)
    {
        workLoad one = new workLoad("One");
        one.execute();
    }

    private static void Bw2_DoWork(object sender, DoWorkEventArgs e)
    {
        workLoad one = new workLoad("Two");
        one.execute();
    }

}

public class workLoad
{
    private ILogger _logger { get; set; }
    private string _number { get; set; }
    public workLoad(string number)
    {
        _logger = LogManager.GetCurrentClassLogger();
        _number = number;

        var target = (FileTarget) LogManager.Configuration.FindTargetByName("DebugFile");
        target.FileName = $"c:\\temp\\File{number}.txt";
        LogManager.ReconfigExistingLoggers();
    }

    public void execute()
    {
        for (int i = 0; i < 1000; i++)
        {
            _logger.Info(_number + " LOOPING" + i);        
        }

    }
}

这会导致所有输出都转到一个文件 (two.txt),下面是日志文件的示例。

2017-06-13 17:00:42.4156 TestNlog.workLoad 两个 LOOPING0 2017-06-13 17:00:42.4156 TestNlog.workLoad One LOOPING0 2017-06-13 17:00:42.4806 TestNlog.workLoad One LOOPING1 2017-06-13 17:00:42.4746 TestNlog.workLoad 两个 LOOPING1 2017-06-13 17:00:42.4806 TestNlog.workLoad One LOOPING2 2017-06-13 17:00:42.4806 TestNlog.workLoad 两个 LOOPING2 2017-06-13 17:00:42.4946 TestNlog.workLoad One LOOPING3 2017-06-13 17:00:42.4946 TestNlog.workLoad 两个 LOOPING3 2017-06-13 17:00:42.4946 TestNlog.workLoad One LOOPING4 2017-06-13 17:00:42.4946 TestNlog.workLoad 两个 LOOPING4 2017-06-13 17:00:42.5132 TestNlog.workLoad One LOOPING5 2017-06-13 17:00:42.5132 TestNlog.workLoad 两个 LOOPING5 2017-06-13 17:00:42.5132 TestNlog.workLoad One LOOPING6 2017-06-13 17:00:42.5257 TestNlog.workLoad 两个 LOOPING6 2017-06-13 17:00:42.5257 TestNlog.workLoad One LOOPING7 2017-06-13 17:00:42.5257 TestNlog.workLoad 两个 LOOPING7 2017-06-13 17:00:42.5407 TestNlog.workLoad 两个 LOOPING8 2017-06-13 17:00:42.5407 TestNlog.workLoad One LOOPING8 2017-06-13 17:00:42.5407 TestNlog.workLoad 两个 LOOPING9 2017-06-13 17:00:42.5407 TestNlog.workLoad One LOOPING9 2017-06-13 17:00:42.5577 TestNlog.workLoad 两个 LOOPING10 2017-06-13 17:00:42.5577 TestNlog.workLoad One LOOPING10

理想情况下,我会查找从实例一到 One.TXT 的所有内容,以及从实例二到 two.txt 的所有内容(您可以看到像这样运行 200+ 可能是个问题!)

【问题讨论】:

  • 这方面有什么更新吗?
  • 我最终为这些类编写了自己的记录器,并将 nlog 用于更通用的应用程序日志......所以没有更新,抱歉。

标签: c# .net logging nlog


【解决方案1】:

也许可以尝试在文件名中使用${logger}(需要 NLog 4.5 才能使 NLog 文件存档逻辑正常工作):

<targets>
    <target xsi:type="File" name="DebugFile"
         fileName="${basedir}\logs\${logger:shortName=true}.txt" />
</targets>

然后文件名将匹配记录器名称。在创建记录器时,而不是这样做:

_logger = LogManager.GetCurrentClassLogger();

然后这样做:

_logger = LogManager.GetCurrentClassLogger(typeof(workLoad).ToString() + "." + number);

您可以像这样创建记录器(未测试是否有效):

private static object _lock = new object();

public static NLog.Logger PrepareLogger(string number)
{
        lock (_lock)
        {
            var fileTarget = LogManager.Configuration.FindTargetByName(number);
            if (fileTarget == null)
            {
                var fileTarget = new FileTarget(number);
                fileTarget.FileName = $"c:\\temp\\File{number}.txt";

                var template = LogManager.Configuration.FindTargetByName("DebugFile");
                if (template != null)
                {
                      fileTarget.Layout = template.Layout;
                }
                LogManager.Configuration.AddTarget(fileTarget);
                var rule = new LoggingRule(number, LogLevel.Debug, fileTarget) { Final = true };
                LogManager.Configuration.LoggingRules.Add(rule);
                LogManager.ReconfigExistingLoggers();
            }
        }
    return LogManager.GetLogger(number);
}

【讨论】:

    【解决方案2】:

    也许你可以让类的构造函数初始化一个新的日志

    【讨论】:

    • 试过了,我可能会遗漏一些东西,但我看不出有什么办法。
    【解决方案3】:

    试试ReconfigExistingLoggers,可能是你最好的选择。

    <targets>
        <target xsi:type="File"
            name="Foo"
            fileName="${basedir}/logs/logfile.txt"
            keepFileOpen="false"
            encoding="iso-8859-2" />
    </targets>
    

    然后

    var target = (FileTarget)LogManager.Configuration.FindTargetByName("Foo");
    target.FileName = $"{change_my_location}/class_instance.txt";
    LogManager.ReconfigExistingLoggers();
    

    从这里的答案: Update NLog target filename at runtime

    【讨论】:

    • 不起作用,使用不起作用的目标更改所有 ILogger 实例的日志文件。我现在用我模拟你的建议的代码来编辑问题,以显示我的意思。
    • 可以并行运行多少个实例? 200?为什么不是数据库目标?你可以SELECT * FROM logs WHERE instance = 'blah'
    • 最多可以有 250 个 - 实际上一次大约是 25 个。以数据库为目标是一个好主意,我没有想到这一点 - 我会把它当作最后的手段 - 我真的很想让它与一个文件一起工作,尽管这样进程的输出和日志都可以存储在一起...这也是用户习惯的。好主意 - 它肯定会在“可能”堆上进行:)
    【解决方案4】:

    您可以在运行时创建自己的目标或向布局添加参数并传递它。

    创建自定义日志文件

        /// <summary>
        /// Creates the custom log file.
        /// Logging of Info and Warning Message Only.
        /// </summary>
        /// <param name="TargetName">Name of the target.</param>
        /// <param name="TargetFileName">Name of the target file.</param>
        /// <param name="LoggerName">Name of the logger.</param>
        public void CreateCustomLogFile(String TargetName, String TargetFileName, String LoggerName = "*")
        {
            try
            {
                var NLogTarget = LogManager.Configuration.FindTargetByName(TargetName);
    
                if (NLogTarget == null) //Don't Re Add the Target
                {
                    NLogTarget = new FileTarget()
                    {
                        Name = TargetName,
                        FileName = TargetFileName,
                        Layout = @"[${date:format=yyyy-MM-dd HH\:mm\:ss}] ${message}"
                    };
    
                    LogManager.Configuration.AddTarget(TargetName, NLogTarget);
                }
    
                var NLogRule = new LoggingRule(LoggerName, NLogTarget);
                NLogRule.EnableLoggingForLevel(LogLevel.Info);
                NLogRule.EnableLoggingForLevel(LogLevel.Warn);
                LogManager.Configuration.LoggingRules.Add(NLogRule);
    
                LogManager.ReconfigExistingLoggers();
            }
            catch { }
        }
    
        /// <summary>
        /// Removes the custom log file.
        /// </summary>
        /// <param name="TargetName">Name of the target.</param>
        public void RemoveCustomLogFile(String TargetName)
        {
            try
            {
                if (LogManager.Configuration.FindTargetByName(TargetName) != null)
                {
                    var Target = LogManager.Configuration.FindTargetByName(TargetName);
    
                    foreach (var rule in LogManager.Configuration.LoggingRules)
                    {
                        rule.Targets.Remove(Target);
                    }
                    LogManager.Configuration.RemoveTarget(TargetName);
                    Target.Dispose();
                    LogManager.ReconfigExistingLoggers();
                }
            }
            catch { }
        }
    

    或在 NLog 布局中添加参数

    <target name="CTSCoreByPortNumber" xsi:type="File" 
         fileName="${logDirectory}/CTS${event-context:item=PortNumber}.log"
         layout="${fulllayout}" />
    

    写入正确日志的示例函数。

        /// <summary>
        /// WriteLog - Overloaded Method to write a Message. 
        /// </summary>
        /// <param name="LogType">LogLevel.Trace
        /// LogLevel.Debug,
        /// LogLevel.Info,
        /// LogLevel.Warn,
        /// LogLevel.Error,
        /// LogLevel.Fatal,</param>
        /// <param name="Msg">Message to write</param>
        /// <param name="args">Arguments</param>
        public void WriteLog(LogLevel LogType, String Msg, object[] args)
        {
            try
            {
                if (args == null)
                    args = new object[0];
    
                LogEventInfo logEvent = new LogEventInfo(LogType, GetCallingMethodName(), null, String.Format(Msg, args), null);
    
                if (PortNumber != 0)
                    logEvent.Properties["PortNumber"] = ".Line_" + PortNumber;
    
                Instance.Log(typeof(Logging), logEvent);
    
                //Instance.Log((LogLevel)LogType, Msg, args);
            }
            catch (Exception) { throw; }
        }
    

    Target 中的 PortNumber 在记录之前由属性代码设置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-28
      • 2011-09-05
      • 1970-01-01
      • 2014-05-20
      • 1970-01-01
      • 2011-02-23
      相关资源
      最近更新 更多