【问题标题】:Logging from multiple processes with nLog missing logs and inconsistent archives从具有 nLog 丢失日志和不一致存档的多个进程进行日志记录
【发布时间】:2014-05-31 08:44:47
【问题描述】:

我有一个在 Windows 服务器上运行的进程,来自多个用户的多个会话(可能多达 50 个并发用户),我想:

  1. 将所有进程的日志记录到单个日志文件中
  2. 将日志大小限制为 1.5MB(用于我们的测试)
  3. 记录器的性能可以接受

所以我想试试 nLog:

<?xml version="1.0" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
        internalLogFile="file.txt">
        <targets async="false">
          <target name="regFile" xsi:type="File"
                  layout="${longdate} [${windows-identity}][${processid}]${threadname} ${uppercase:${level}} ${callsite} - ${message}${onexception:${newline}${exception:format=tostring}}" 
                  fileName="${basedir}/logs/test.log" 
                  archiveFileName="${basedir}/logs/test.{#####}.log"
                  archiveAboveSize="102400"
                  archiveNumbering="Rolling"
                  maxArchiveFiles="14"
                  concurrentWrites="true"
                  keepFileOpen="true"               
                  autoFlush="true"                                      
                          />
        </targets>
        <rules>
                <logger name="*" minlevel="Trace" writeTo="regFile" />
        </rules>
</nlog>

我还写了一个小测试器:

class Program
    {
        private static Logger m_log = LogManager.GetCurrentClassLogger();

        static void Main(string[] args)
        {
            // Load logger configuration
            var assemblyLocation = Assembly.GetExecutingAssembly().Location;
            var assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            if (assemblyDirectory != null)
            {
                var logConfig = new FileInfo(Path.Combine(assemblyDirectory, "nlogConfig.xml"));
                NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(logConfig.FullName, true);
            }
            if (args.Length == 1)
            {
                var sw = Stopwatch.StartNew();
                var size = Int32.Parse(args[0]);
                m_log.Info("Will launch {0} process and wait for them to come back...",size);                               
                var handles = new WaitHandle[size];  
                for (int i = 0; i < size; i++)
                {
                    var p = Process.Start(Assembly.GetExecutingAssembly().Location);
                    var processHandle = OpenProcess(
                            ProcessAccessFlags.Synchronize, false, p.Id);
                    if (processHandle != IntPtr.Zero)
                    handles[i] = new ManualResetEvent(false)
                    {
                        SafeWaitHandle = new SafeWaitHandle(processHandle, false)
                    };
                    m_log.Fatal("Started pid={0}.",p.Id);
                }

                m_log.Info("Created the processes, now wait.");
                WaitHandle.WaitAll(handles);
                sw.Stop();
                Thread.Sleep(100);
                m_log.Info("Done, took {0}.",sw.ElapsedMilliseconds);
            }
            else
            {
                m_log.Info("Running for {0} * {1}",1000,m_log.ToString());
                for (int i = 0; i < 1000; i++)
                {
                    m_log.Error("Hello nlog {0}", i);
                }              
            }           
        }
        #region Native API

        [Flags]
        enum ProcessAccessFlags
        {
            All = 0x001F0FFF,
            Terminate = 0x00000001,
            CreateThread = 0x00000002,
            VMOperation = 0x00000008,
            VMRead = 0x00000010,
            VMWrite = 0x00000020,
            DupHandle = 0x00000040,
            SetInformation = 0x00000200,
            QueryInformation = 0x00000400,
            Synchronize = 0x00100000
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);
        #endregion
    }

运行良好,只要我不需要存档... 像这样运行我的测试器:“LoggerTester.exe 10”我得到了

  1. 少于 10,000 条错误消息(每条应写入 1000 条“hello nlog in ERROR”)
  2. 似乎有时 2 个记录器滚动文件,因此中间有一些小于 100kb 的日志。

也许我要求太多,我必须使用某种内存记录器模式(使用 1 个记录器来实际管理文件),但我真的不希望这样做。

在任何地方都找不到对此的任何参考,所以我想我在这里试试运气...谢谢。

【问题讨论】:

    标签: c# logging nlog


    【解决方案1】:

    NLog 现在对并发归档逻辑进行了多项改进:

    这允许在执行归档操作时更好地协调并发进程。当然,协调/同步仍然有性能成本。

    建议不要让多个并发进程尝试重命名/移动相同的静态文件名,而是建议在 NLog 4.5(及更高版本)中在文件名中包含 ${shortdate}。这减少了在白天卷入战斗的机会:

         <target name="regFile" xsi:type="File"
               fileName="${basedir}/logs/test.${shortdate}.log" 
               archiveAboveSize="102400"
               maxArchiveFiles="14"
               concurrentWrites="true"
               keepFileOpen="true" />
    

    【讨论】:

      【解决方案2】:

      我认为您将无法从多个进程记录到单个文件。我建议尝试一些其他目标:

      1. 数据库目标
      2. Lo​​gReceiverwebServiceTarget(NLog 也有一个 LogReceiverService 实现与目标一起使用)。
      3. WebServiceTarget

      我过去曾成功使用 DatabaseTarget。

      我没有使用其他目标的经验,但我确实实现过一次 LoggingService。它与 LogReceiverWebServiceTarget 非常相似。这是一个实现日志接口的 WCF 服务。我们有一个对应的目标,可以配置为与日志服务端点进行通信。我们还实现了一些缓存和刷新,以便我们发送消息块,而不是为每条消息进行服务调用。

      祝你好运!

      【讨论】:

      • 感谢回复,看来我的场景对于nlog来说有点过头了。我们最终使用了自己的实现。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多