繁体   English   中英

如何最大程度地减少日志记录影响

[英]How to minimize Logging impact

关于这个问题已经存在一个问题,但是它并没有告诉我我需要知道的内容:假设我有一个Web应用程序,并且每次往返都有很多日志记录。 我不想就为何日志量如此之大或如何减少日志记录操作展开辩论。 我想知道,我在为了使这个记录问题高性能清洁什么的可能性。

到目前为止,我已经实现了声明式(基于属性)和命令式日志,这似乎是一种很酷的方法……现在,假设我可以预期这些日志将花费更多的时间, 我该如何处理性能?超出预期。 是否可以打开线程并保留该工作?

我会考虑的事情:

  • 使用有效的文件格式来最小化要写入的数据量(例如,XML和文本格式易于阅读,但通常效率极低-相同的信息可以二进制格式存储在较小的空间中)。 但是,不要花费大量CPU时间尝试“最佳”打包数据。 只需选择一种紧凑但可以快速编写的简单格式。

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

  • 仅记录有用的信息。 无论您认为一切有多么重要,都有可能找到一些切入点。

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

  • 测试将记录的数据缓存在RAM中是否有助于提高性能(例如,写入一千个20字节的日志记录将意味着1,000个函数调用,并且可能会导致大量磁盘搜索和其他写入开销,而在一个突发中写入单个20,000字节的块仅意味着一个函数调用,可以显着提高性能,并最大程度地提高您进入磁盘的突发速率。 通常,写大小为(4k,16k,32、64k)的数据块效果很好,因为它倾向于适合磁盘和I / O架构(但请检查您的特定架构,以获取有关哪种尺寸可能会提高效率的线索)。 RAM缓冲区的缺点是,如果断电,您将丢失更多数据。 因此,您可能必须在性能与健壮性之间取得平衡。

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

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

  • 是否有硬件解决方案可以为您带来巨大收益? 例如,您是否使用过SSD或RAID磁盘? 将数据转储到其他服务器会有所帮助还是受阻? 如果您可以花费500美元来简单地升级磁盘,花10,000美元的开发人员时间来使某些性能更好可能并不总是很有意义。

我使用下面的代码登录。 它是一个单例,它接受日志记录并将每条消息放入并发队列。 每隔两秒钟,它将写入其中的所有内容写入磁盘。 现在,您的应用仅会延迟将每条消息放入列表所需的时间。 这是我自己的代码,请随时使用。

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;
            }
        }
    }
}                                                

在调用logger.debug之前,请检查记录器是否已启用调试功能,这意味着在关闭调试时,您的代码不必评估消息字符串。

if (_logger.IsDebugEnabled) _logger.Debug($"slow old string {this.foo} {this.bar}");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM