1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NLogの非同期出力とConfigファイルを使用しない方法

Posted at

目的

・非同期にLogを出力する
ログ出力がボトルネックになって処理が遅延する場合がある。非同期にLog出力ができるNLogを使用した方法を検討する

・configファイルに依存しない作りにする。
複数のLogを出力したい場合が多い為、プログラムで動的に設定を変更できるように作成したい

NLogをTLogでラッピング

TLogクラスを作成してDLLを作成した。より細かい設定は、引数を増やして設定すればよい。
Close処理も入れたが、プログラム終了時にNLogは自動でシャットダウンされる。

TLog.cs
   public static class  TLog
    {
        private static Dictionary<string,string> LoggerNameMap = new Dictionary<string,string>();

        private static NLog.Config.LoggingConfiguration config = new NLog.Config.LoggingConfiguration();

        // NLogレイアウト
        // https://nlog-project.org/config/?tab=layout-renderers


        /// <summary>
        /// ファイルフォーマットサンプル1
        /// 実行中のアプリケーションの基底ディレクトリ/logs/yyyy-MM-dd.log
        /// </summary>
        public static readonly string FileSampleFormat1 = "${basedir}/logs/${shortdate}.log";

        /// <summary>
        /// レイアウトフォーマットサンプル1
        /// 
        /// 例1 通常
        /// 2023-12-22 09:11:15.3232 TRACE [11] Logger.Logger - Trace ログです。
        /// 
        /// 例2 Exception
        /// 2023-12-22 09:09:36.7485 ERROR [10] Logger.Logger - エラーが発生しました。 System.DivideByZeroException: Attempted to divide by zero.
        /// at WinFormsApp1.Form1.thTest() in D:\study\work\DotNet_Sample\ConsoleApp1\WinFormsApp1\Form1.cs:line 51
        /// 
        /// 
        /// </summary>
        public static readonly string LayoutSampleFormat1 = "${longdate} ${level:uppercase=true:padding=-5} [${threadid}] ${logger} - ${message} ${exception:format=tostring}";

        /*
            name - rules の出力先に設定するために任意の名前を指定します。
            xsi:type - ファイルへ出力するためのタイプ ”File” を指定します。
            encoding - ファイル出力のエンコーディングを指定します。
            writeBom - BOM(バイトオーダーマーク)の有り無しを指定します。 BOM有りだと文字コード判定が容易なので、エディタの文字化けが防げます。
            lineEnding - OSに合わせた改行コードが指定できます。Default(OSに合わせる), CR, CRLF, LF, None から選択します。
            layout - ログのフォーマットを指定します。必要に応じて編集してください。
            fileName - ログファイルの出力先を指定します。{processname} 変数でプロセス名をファイルに名にしています。
            archiveFileName - バックアップ先のファイル名を指定します。{###} 変数は数字の連番になります。
            archiveEvery - 指定の時間でログファイルを切り替えます。Day, Hour, Minute, Month, None などから選択します。
            archiveNumbering - 連番の番号の付け方を Rolling, Sequence から選択します。
            archiveAboveSize - 最大ファイルサイズ
            maxArchiveFiles - 保存するアーカイブファイルの最大数を指定します。デフォルトは 0 です。        
         
         */



        /// <summary>
        /// Logインスタンスを取得する
        /// </summary>
        /// <param name="LoggerName">Logger名</param>
        /// <param name="logger">オブジェクト</param>
        /// <returns>結果</returns>
        public static bool GetInstance(string LoggerName, ref NLog.Logger logger)
        {
            if(LoggerNameMap.ContainsKey(LoggerName))
            {
                logger = NLog.LogManager.GetLogger(LoggerNameMap[LoggerName]);
                return true;
            }
            else
            {
                return false;
            }

        }


        /// <summary>
        /// 非同期のLogを作成する
        /// パフォーマンスを優先する場合は、非同期での出力を行う
        /// </summary>
        /// <param name="LoggerName">Logger名称</param>
        /// <param name="FileName">ファイルのフルパス(相対or絶対)</param>
        /// <param name="levMin">出力レベル Min</param>
        /// <param name="levMax">出力レベル Max</param>
        /// <param name="layout">レイアウト</param>
        public static void MakeLogAsync(string LoggerName, string FileName, LogLevel levMin,LogLevel levMax,string layout = "${longdate} ${level:uppercase=true:padding=-5} [${threadid}] ${logger} - ${message} ${exception:format=tostring}")
        {

            if (LoggerNameMap.ContainsKey(LoggerName))
            {
                return;
            }

            var logfile = new NLog.Targets.FileTarget(LoggerName);

            // ファイル名の設定
            logfile.FileName = FileName;
            // ファイルサイズ
            logfile. ArchiveAboveSize  = 1000000;
            // バックアップファイル数
            logfile.MaxArchiveFiles = 10;
            // バックアアップファイル名設定
            logfile.ArchiveNumbering = ArchiveNumberingMode.Sequence;
            // レイアウト
            logfile.Layout = layout;

            AsyncTargetWrapper wrapper = new AsyncTargetWrapper();

            wrapper.WrappedTarget = logfile;
            wrapper.QueueLimit = 5000;
            wrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard;


            config.AddRule(levMin, levMax, wrapper, LoggerName + "_Logger");

            NLog.LogManager.Configuration = config;

            LoggerNameMap.Add(LoggerName, LoggerName + "_Logger");

        }


        /// <summary>
        /// Logを作成する
        /// </summary>
        /// <param name="LoggerName">Logger名称</param>
        /// <param name="FileName">ファイルのフルパス(相対or絶対)</param>
        /// <param name="levMin">出力レベル Min</param>
        /// <param name="levMax">出力レベル Max</param>
        /// <param name="layout">レイアウト</param>
        public static void MakeLog(string LoggerName, string FileName, LogLevel levMin, LogLevel levMax, string layout = "${longdate} ${level:uppercase=true:padding=-5} [${threadid}] ${logger} - ${message} ${exception:format=tostring}")
        {

            if (LoggerNameMap.ContainsKey(LoggerName))
            {
                return;
            }

            var logfile = new NLog.Targets.FileTarget(LoggerName);

            // ファイル名の設定
            logfile.FileName = FileName;
            // ファイルサイズ
            logfile.ArchiveAboveSize = 1000000;
            // バックアップファイル数
            logfile.MaxArchiveFiles = 10;
            // バックアアップファイル名設定
            logfile.ArchiveNumbering = ArchiveNumberingMode.Sequence;
            // レイアウト
            logfile.Layout = layout;


            config.AddRule(levMin, levMax, logfile, LoggerName + "_Logger");

            NLog.LogManager.Configuration = config;

            LoggerNameMap.Add(LoggerName, LoggerName + "_Logger");

        }


        /// <summary>
        /// 終了処理
        /// アプリケーション終了時に実行する
        /// </summary>
        public static void Close()
        {

            NLog.LogManager.Shutdown();

            LoggerNameMap.Clear();

        }

    }

サンプルアプリ

Sample1.logに6000回出力
Sample2.logに6000回出力
Sample3.logに実行時間を出力

WinFormsApp1.cs
// ボタン処理以外は省略

        private void button1_Click(object sender, EventArgs e)
        {
            Logger.TLog.MakeLogAsync("Sample1", "${basedir}/logs/Sample1_Async_${shortdate}.log", LogLevel.Trace, LogLevel.Fatal, Logger.TLog.LayoutSampleFormat1);
            Logger.TLog.MakeLog("Sample2", "${basedir}/logs/Sample2_${shortdate}.log", LogLevel.Trace, LogLevel.Fatal, Logger.TLog.LayoutSampleFormat1);
            Logger.TLog.MakeLog("Sample3", "${basedir}/logs/Sample3_${shortdate}.log", LogLevel.Trace, LogLevel.Fatal, Logger.TLog.LayoutSampleFormat1);


            NLog.Logger log1 = NLog.LogManager.GetCurrentClassLogger();
            NLog.Logger log2 = NLog.LogManager.GetCurrentClassLogger();
            NLog.Logger log3 = NLog.LogManager.GetCurrentClassLogger();

            Logger.TLog.GetInstance("Sample1", ref log1);
            Logger.TLog.GetInstance("Sample2", ref log2);
            Logger.TLog.GetInstance("Sample3", ref log3);

            System.Threading.Thread.Sleep(200);

            Stopwatch sw = new Stopwatch();

            sw.Start();

            for(int i = 0; i < 6000; i++)
            {
                log1.Trace("{0} Trace ログです。",i);
            }

            sw.Stop();

            log3.Trace("log1 経過時間:{0}", sw.ElapsedMilliseconds);

            sw.Reset();

            sw.Start();

            for (int i = 0; i < 6000; i++)
            {
                log2.Trace("{0} Trace ログです。", i);
            }

            sw.Stop();

            log3.Trace("log2 経過時間:{0}", sw.ElapsedMilliseconds);

            sw.Reset();

        }

出力結果

Sample1_Async_2023-12-22.0.log
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 0 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 1 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 2 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 3 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 4 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 5 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 6 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 7 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 8 Trace ログです。 
2023-12-22 15:29:47.4872 TRACE [5] Sample1_Logger - 9 Trace ログです。 
.......
Sample1_Async_2023-12-22.0.log
2023-12-22 15:29:47.5204 TRACE [5] Sample2_Logger - 0 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 1 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 2 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 3 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 4 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 5 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 6 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 7 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 8 Trace ログです。 
2023-12-22 15:29:47.5384 TRACE [5] Sample2_Logger - 9 Trace ログです。 
.......
Sample3_2023-12-22.log
2023-12-22 15:29:47.5019 TRACE [5] Sample3_Logger - log1 経過時間:23 
2023-12-22 15:29:47.5958 TRACE [5] Sample3_Logger - log2 経過時間:71 
2023-12-22 15:29:49.9777 TRACE [12] Sample3_Logger - log1 経過時間:8 
2023-12-22 15:29:50.0334 TRACE [12] Sample3_Logger - log2 経過時間:59 
2023-12-22 15:29:52.4479 TRACE [13] Sample3_Logger - log1 経過時間:6 
2023-12-22 15:29:52.5017 TRACE [13] Sample3_Logger - log2 経過時間:61 
2023-12-22 15:29:55.3623 TRACE [5] Sample3_Logger - log1 経過時間:5 
2023-12-22 15:29:55.4081 TRACE [5] Sample3_Logger - log2 経過時間:47 
2023-12-22 15:29:58.0483 TRACE [14] Sample3_Logger - log1 経過時間:6 
2023-12-22 15:29:58.0952 TRACE [14] Sample3_Logger - log2 経過時間:53 
2023-12-22 15:30:00.1110 TRACE [13] Sample3_Logger - log1 経過時間:5 
2023-12-22 15:30:00.1579 TRACE [13] Sample3_Logger - log2 経過時間:48 
2023-12-22 15:30:03.0522 TRACE [12] Sample3_Logger - log1 経過時間:4 
2023-12-22 15:30:03.0952 TRACE [12] Sample3_Logger - log2 経過時間:53 
2023-12-22 15:30:04.7734 TRACE [5] Sample3_Logger - log1 経過時間:4 
2023-12-22 15:30:04.8142 TRACE [5] Sample3_Logger - log2 経過時間:49 
2023-12-22 15:30:06.6026 TRACE [14] Sample3_Logger - log1 経過時間:6 
2023-12-22 15:30:06.6583 TRACE [14] Sample3_Logger - log2 経過時間:50 
2023-12-22 15:30:08.3455 TRACE [12] Sample3_Logger - log1 経過時間:4 
2023-12-22 15:30:08.3924 TRACE [12] Sample3_Logger - log2 経過時間:47 
2023-12-22 15:30:10.0843 TRACE [5] Sample3_Logger - log1 経過時間:6 
2023-12-22 15:30:10.1421 TRACE [5] Sample3_Logger - log2 経過時間:50 
2023-12-22 15:30:11.7989 TRACE [13] Sample3_Logger - log1 経過時間:5 
2023-12-22 15:30:11.8457 TRACE [13] Sample3_Logger - log2 経過時間:50 

結果

非同期で作成したSamlple1の方が終了が速いことが、分かった。
ファイルへの出力は、Samlple1の方が遅いかもしれないが、他の処理への遅延を与えない為には、非同期で動作させた方が効率が良いと思われる。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?