0

この問題についてはすでに質問がありますが、私が知る必要があることを教えてくれません。私がWebアプリケーションを持っていて、すべてのラウンドトリップで多くのログが記録されていると仮定しましょう。なぜこれほど多くのロギングがあるのか​​、またはどうすればloggin操作を減らすことができるのかについて議論を始めたくありません。このロギングの問題を実行可能でクリーンにするために、どのような可能性があるのか​​知りたいです。

これまで、宣言型(属性ベース)および命令型のロギングを実装しました。これは、クールでクリーンな方法のようです...では、これらのログにもっと時間がかかると想定して、パフォーマンスについて何ができるでしょうか。予想より。スレッドを開いてその作業を任せても大丈夫ですか?

4

3 に答える 3

3

私が考えること:

  • 効率的なファイル形式を使用して、書き込むデータの量を最小限に抑えます(たとえば、XMLおよびテキスト形式は読みやすいですが、通常は非常に非効率的です。同じ情報をバイナリ形式ではるかに小さなスペースに保存できます)。ただし、データを「最適に」パックするために多くのCPU時間を費やさないでください。コンパクトでありながら書き込みが速いシンプルなフォーマットを選ぶだけです。

  • ログでの圧縮の使用をテストします。これは高速SSDの場合には当てはまらないかもしれませんが、ほとんどのI / O状況では、データ圧縮のオーバーヘッドはI / Oオーバーヘッドよりも小さいため、圧縮によって正味のゲインが得られます(ただし、妥協点ですが、CPU使用率を下げてIを下げる) / O使用法)。

  • 有用な情報のみをログに記録します。すべてがどれほど重要だと思っていても、切り取るものを見つけることができるでしょう。

  • 繰り返されるデータを排除します。たとえば、クライアントのIPアドレスまたはドメイン名を繰り返しログに記録していますか?これらはセッションで1回報告され、その後繰り返されないようにすることはできますか?または、それらをマップファイルに保存し、参照する必要があるときはいつでもコンパクトなインデックス値を使用できますか?等

  • ログに記録されたデータをRAMにバッファリングすることで、パフォーマンスが向上するかどうかをテストします(たとえば、20バイトのログレコードを1,000回書き込むと、1,000回の関数呼び出しが発生し、ディスクシークやその他の書き込みオーバーヘッドが発生する可能性がありますが、1回のバーストで20,000バイトのブロックを1回書き込むと、 1回の関数呼び出しで、パフォーマンスが大幅に向上し、ディスクに出力されるバーストレートが最大化される可能性があります)。多くの場合、データの(4k、16k、32、64k)のようなサイズでブロックを書き込むと、ディスクとI / Oアーキテクチャに適合する傾向があるため、うまく機能します(ただし、どのサイズが効率を向上させるかについての手がかりについては、特定のアーキテクチャを確認してください)。RAMバッファの欠点は、停電が発生した場合、より多くのデータが失われることです。したがって、パフォーマンスと堅牢性のバランスをとる必要がある場合があります。

  • (特にバッファリングしている場合は...)情報をメモリ内のデータ構造にダンプし、別のスレッドに渡してディスクにストリーミングします。これは、プライマリスレッドがログI/Oによって保持されるのを防ぐのに役立ちます。ただし、スレッドには注意してください。たとえば、短いバーストでログに記録できるよりも速くデータを作成しているときに、どのように処理するかを検討する必要がある場合があります。キューなどを実装する必要がありますか?

  • 複数のストリームをログに記録していますか?これらを単一のログに多重化して、ディスクのシークと開いているファイルの数を減らすことはできますか?

  • あなたのお金に大きな価値を与えるハードウェアソリューションはありますか?例:SSDまたはRAIDディスクを使用しましたか?データを別のサーバーにダンプすることは助けになりますか、それとも妨げになりますか?単にディスクをアップグレードするために500ドルを費やすことができれば、開発者の時間を10,000ドル費やして、パフォーマンスを向上させることは必ずしも意味がないかもしれません。

于 2013-03-25T20:27:07.267 に答える
2

以下のコードを使用してログに記録します。これは、ロギングを受け入れ、すべてのメッセージをコンカレントキューに入れるシングルトンです。2秒ごとに、ディスクに入ってきたものすべてを書き込みます。アプリは、すべてのメッセージをリストに追加するのにかかる時間だけ遅延するようになりました。それは私自身のコードです、それを自由に使ってください。

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;
            }
        }
    }
}                                                
于 2013-03-29T10:10:09.497 に答える
0

logger.debugを呼び出す前に、ロガーがデバッグ対応になっているかどうかを確認してください。これは、デバッグがオフになっているときにコードがメッセージ文字列を評価する必要がないことを意味します。

if (_logger.IsDebugEnabled) _logger.Debug($"slow old string {this.foo} {this.bar}");
于 2018-05-17T12:01:15.940 に答える