簡體   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