使用nLog缺少日志和不一致的归档从多个进程记录

时间:2014-04-16 12:54:35

标签: c# logging nlog

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

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

    所以我想我会试试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条ERROR消息(每一条消息应写入1000“hello nlog in ERROR)
    2. 似乎有时2个记录器会滚动文件,因此有些日志会比中间的100kb小。
    3. 也许我要求的很多,我必须使用某种内存记录器模式(使用1个记录器来实际管理文件),但我真的希望能够这样做。

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

2 个答案:

答案 0 :(得分:0)

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

  1. DatabaseTarget
  2. LogReceiverwebServiceTarget(NLog还有一个与目标一起使用的LogReceiverService实现)。
  3. WebServiceTarget
  4. 我过去使用DatabaseTarget取得了成功。

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

    祝你好运!

答案 1 :(得分:0)

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" />