目的
・非同期に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の方が遅いかもしれないが、他の処理への遅延を与えない為には、非同期で動作させた方が効率が良いと思われる。