2013-03-25 86 views
0

已经有一个关于这个issue的问题,但它并不告诉我我需要知道什么: 假设我有一个web应用程序,并且每次往返都有很多日志记录。我不想打开关于的辩论为什么有这么多的日志记录,或者我该如何做更少的登录操作。我想知道为了使日志记录问题有什么可能性高性能干净如何最大限度地减少对日志的影响

到目前为止,我已经实现了声明(基于属性)和必要的记录,这似乎是做...现在的凉爽和清洁的方式,我能做些什么有关性能,假设我可以期待那些日志需要比预期更多的时间。 可以打开一个线程并将其保留下来吗?

+0

不是说这是一个足够明确的答案,但我知道如果你做日志记录,你应该通过网络注销到你的Web应用程序不在的驱动器上。因为那么性能不会受到日志被写入webapp运行的同一个驱动器的阻碍。 – leeand00 2013-03-25 20:09:36

+0

@ leeand00这是无稽之谈。 – 2013-03-25 20:17:12

+1

你可以使用像Gurock Smartinspect之类的东西,它允许跨管道记录,tcp以及文件记录。它也允许异步记录并且易于使用。 http://www.gurock.com/smartinspect/ – 2013-03-25 20:17:21

回答

1

我使用下面的代码来记录。它是一个接受Logging并将每条消息放入并发队列的单例。每隔两秒它会写入所有进入磁盘的内容。现在,您的应用只会延迟将每条消息放入列表所需的时间。这是我自己的代码,可以随意使用它。

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading; 
using System.Windows.Forms; 

namespace FastLibrary 
{ 
    public enum Severity : byte 
    { 
     Info = 0, 
     Error = 1, 
     Debug = 2 
    } 

    public class Log 
    { 
     private struct LogMsg 
     { 
      public DateTime ReportedOn; 
      public string Message; 
      public Severity Seriousness; 
     } 

     // Nice and Threadsafe Singleton Instance 
     private static Log _instance; 

     public static Log File 
     { 
      get { return _instance; } 
     } 

     static Log() 
     { 
      _instance = new Log(); 
      _instance.Message("Started"); 
      _instance.Start(""); 
     } 

     ~Log() 
     { 
      Exit(); 
     } 

     public static void Exit() 
     { 
      if (_instance != null) 
      { 
       _instance.Message("Stopped"); 
       _instance.Stop(); 
       _instance = null; 
      } 
     } 

     private ConcurrentQueue<LogMsg> _queue = new ConcurrentQueue<LogMsg>(); 
     private Thread _thread; 
     private string _logFileName; 
     private volatile bool _isRunning; 

     public void Message(string msg) 
     { 
      _queue.Enqueue(new LogMsg { ReportedOn = DateTime.Now, Message = msg, Seriousness = Severity.Info }); 
     } 

     public void Message(DateTime time, string msg) 
     { 
      _queue.Enqueue(new LogMsg { ReportedOn = time, Message = msg, Seriousness = Severity.Info }); 
     } 

     public void Message(Severity seriousness, string msg) 
     { 
      _queue.Enqueue(new LogMsg { ReportedOn = DateTime.Now, Message = msg, Seriousness = seriousness }); 
     } 

     public void Message(DateTime time, Severity seriousness, string msg) 
     { 
      _queue.Enqueue(new LogMsg { ReportedOn = time, Message = msg, Seriousness = seriousness }); 
     } 

     private void Start(string fileName = "", bool oneLogPerProcess = false) 
     { 
      _isRunning = true; 
      // Unique FileName with date in it. And ProcessId so the same process running twice will log to different files 
      string lp = oneLogPerProcess ? "_" + System.Diagnostics.Process.GetCurrentProcess().Id : ""; 
      _logFileName = fileName == "" 
           ? DateTime.Now.Year.ToString("0000") + DateTime.Now.Month.ToString("00") + 
           DateTime.Now.Day.ToString("00") + lp + "_" + 
           System.IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath) + ".log" 
           : fileName; 
      _thread = new Thread(LogProcessor); 
      _thread.IsBackground = true; 
      _thread.Start(); 
     } 

     public void Flush() 
     { 
      EmptyQueue(); 
     } 

     private void EmptyQueue() 
     { 
      while (_queue.Any()) 
      { 
       var strList = new List<string>(); 
       // 
       try 
       { 
        // Block concurrent writing to file due to flush commands from other context 
        lock (_queue) 
        { 
         LogMsg l; 
         while (_queue.TryDequeue(out l)) strList.Add(l.ReportedOn.ToLongTimeString() + "|" + l.Seriousness + "|" + l.Message); 
         if (strList.Count > 0) 
         { 
          System.IO.File.AppendAllLines(_logFileName, strList); 
          strList.Clear(); 
         } 
        } 
       } 
       catch 
       { 
        //ignore errors on errorlogging ;-) 
       } 
      } 
     } 

     public void LogProcessor() 
     { 
      while (_isRunning) 
      { 
       EmptyQueue(); 
       // Sleep while running so we write in efficient blocks 
       if (_isRunning) Thread.Sleep(2000); 
       else break; 
      } 
     } 

     private void Stop() 
     { 
      // This is never called in the singleton. 
      // But we made it a background thread so all will be killed anyway 
      _isRunning = false; 
      if (_thread != null) 
      { 
       _thread.Join(5000); 
       _thread.Abort(); 
       _thread = null; 
      } 
     } 
    } 
}             
+0

我将使用扩展方法来“覆盖”日志记录并使用该并发队列技术,如果它获得更好的时间,我会让你知道,但这正是我所寻找的,感谢代码示例。 – 2013-03-31 23:13:19

2

事情我会考虑:

  • 使用一个有效的文件格式,以尽量减少数据量写入(如XML和文本格式很容易阅读,但通常非常低效的 - 同样的信息以更小的空间以二进制格式存储)。但是,不要花费大量的CPU时间试图“优化”打包数据。只需要一个简洁的格式,但它很紧凑,但写起来很快。

  • 在日志上测试压缩的使用情况。这可能不是快速SSD的情况,但在大多数I/O情况下,压缩数据的开销小于I/O开销,因此压缩会带来净增益(尽管这是一种折衷方案 - 提高CPU使用率以降低I/O用法)。

  • 只记录有用信息。不管你认为一切都很重要,你很可能会找到一些切断的东西。

  • 消除重复的数据。例如您是否重复记录客户的IP地址或域名?这些可以报告一次会议,然后不再重复?或者,您是否可以将它们存储在地图文件中并在需要引用时使用紧凑索引值? etc

  • 测试是否缓存RAM中记录的数据有助于提高性能(例如,编写一千个20字节的日志记录将意味着1,000个函数调用,并且可能导致大量磁盘搜寻和其他写入开销,同时写入一个20,000字节在一次突发中阻塞意味着只有一个函数调用,并且可以显着提高性能并最大化您获取到磁盘的突发速率)。通常将数据块写入像(4k,16k,32,64k)数据这样的大小可以很好地适用于磁盘和I/O体系结构(但请检查特定体系结构以了解哪些大小可以提高效率)。 RAM缓冲区的另一面是,如果发生停电,您将丢失更多数据。所以你可能不得不平衡性能与健壮性。

  • (特别是如果你正在缓冲...)将信息转储到内存中的数据结构,并将它传递给另一个线程,以便将其流出到磁盘。这将有助于阻止您的主线程被日志I/O阻止。但请注意线程 - 例如,您可能不得不考虑在创建数据的时候如何处理时间,而不是记录短的突发数据 - 您是否需要实现队列等?

  • 你记录多个流?可以将它们多路复用到一个日志中以减少磁盘搜索和打开文件的数量?

  • 有没有一种硬件解决方案可以为您的降压带来巨大的轰动?例如你使用过SSD还是RAID磁盘?将数据转储到不同的服务器帮助还是阻碍?如果花费500美元来简单地升级磁盘,花费10000美元的开发人员时间使性能更好,但并不总是很有意义。

+0

需要注意的一点是,如果在时间敏感的操作中使用RAM缓冲区,则在某些时候缓冲区必须刷新到磁盘,这可能是一个昂贵的操作。在开始时间敏感操作之前,您可以考虑刷新缓冲区,以避免缓冲区变满并在操作期间强制刷新。 – 2013-03-25 20:40:56

+0

日志记录调用后面有日志文件和Windows事件日志。我对打开线程和排队调用感兴趣。你认为这可能是一个很好的解决方案吗?我基本上想把这个过程从主线程中解放出来。你有关于这个“日志队列”stuf的链接吗? – 2013-03-25 20:41:00

+0

@Denise:是的,最好避免小型和大型缓冲区,因为这两个极端都可能导致性能问题。 4k-64k通常都是很好的尺寸。 – 2013-03-25 23:00:08

相关问题