私がこれを行った方法は、各ログメソッドをラップするNLogのラッパークラスを作成し、methodNameを廃止し、StackTraceオブジェクトを使用してメソッド名を取得することです。そうすれば、毎回書く必要はありません。Loggingラッパーメソッドを呼び出すメソッドのメソッド名が自動的に挿入されます。
{0}とmethodNameがどこにもないので、見た目がすっきりします。
さらに一歩進んで、ログ文字列とアクションを取得し、アクションを実行し、StackTraceオブジェクトを使用してログオブジェクトを一度に呼び出すロギングラッパークラスを作成できます。
私はこれをタイムアクションの実行とログ記録に使用しました。すべてを1回の呼び出しで実行すると便利で、繰り返しのコードを節約できます。私のメソッドExecuteTimedAction(string logString、Action actionToExecute)は、ストップウォッチを使用し、開始文字列をログに記録し、ストップウォッチを開始し、メソッド(アクションデリゲート)を実行し、ストップウォッチを停止し、両方のログにタイムスタンプとアセンブリの名前を付けて再度ログに記録します、および呼び出しが開始されたメソッドの名前。
メソッドを取得するためのコードは単純で、StackTraceオブジェクトを使用して、前の呼び出しのStackFrameを取得します。
var stackTrace = new StackTrace();
var callingMethodName = stackTrace.GetFrame(2).GetMethod().Name;
上記に2つハードコードされていることに注意してください。これは、追加のラッパー呼び出しが原因です。直接呼び出す場合は、代わりにGetFrame(1)が必要になる場合があります。最良の方法は、即時ウィンドウを使用してさまざまなフレームを試すか、StackTraceオブジェクトのGetFrames()メソッドを使用して、フレームをループして何が得られるかを確認することです。
文字列形式のパラメータを保持し、ログラッパーの最初のパラメータを追加することを検討しています。これは次のように実行できます。
public static class LogWrapper
{
private static Logger _logger // where Logger assumes that is the actual NLog logger, not sure if it is the right name but this is for example
public static void Info(string logString, object[] params)
{
// Just prepend the method name and then pass the string and the params to the NLog object
_logger.Info(
string.Concat(
GetMethodName(),
": ",
logString
),
params
);
}
public static void Warn(string logString, object[] params)
{
// _logger.Warn(
// You get the point ;)
// )
}
private static string GetMethodName()
{
var stackTrace = new StackTrace(); // Make sure to add using System.Diagnostics at the top of the file
var callingMethodName = stackTrace.GetFrame(2).GetMethod().Name; // Possibly a different frame may have the correct method, might not be 2, might be 1, etc.
}
}
次に、呼び出しコードで、_loggerメンバーはLoggerではなくLoggerWrapperになり、まったく同じ方法で呼び出しますが、コードから{0}を削除します。nullをチェックする必要があります。他にパラメータがない場合は、パラメータなしで呼び出すだけのメソッドオーバーロードが必要です。NLogがそれをサポートしているかどうかはわかりませんので、これを確認する必要があります。
... 編集:
興味深い点として、このタイプのコードは、一連のアセンブリによって参照される可能性のある一般的なライブラリタイプのアセンブリで使用しているため、ハードコーディングしたり心配したりすることなく、アセンブリの呼び出しやメソッド名などの情報を取得できます。私のロギングコードでそれ。また、コードを使用している他の人がそれについて心配する必要がないことも確認します。Log()やWarn()などを呼び出すだけで、アセンブリが自動的にログに保存されます。
ここに例があります(あなたがあなたのためにやり過ぎだと言ったのは知っていますが、あなたがこのようなものを必要とするかもしれないなら、将来のための思考のための食べ物です)。この例では、メソッド名ではなく、アセンブリのみをログに記録していましたが、簡単に組み合わせることができます。
#region : Execute Timed Action :
public static T ExecuteTimedAction<T>(string actionText, Func<T> executeFunc)
{
return ExecuteTimedAction<T>(actionText, executeFunc, null);
}
/// <summary>
/// Generic method for performing an operation and tracking the time it takes to complete (returns a value)
/// </summary>
/// <typeparam name="T">Generic parameter which can be any Type</typeparam>
/// <param name="actionText">Title for the log entry</param>
/// <param name="func">The action (delegate method) to execute</param>
/// <returns>The generic Type returned from the operation's execution</returns>
public static T ExecuteTimedAction<T>(string actionText, Func<T> executeFunc, Action<string> logAction)
{
string beginText = string.Format("Begin Execute Timed Action: {0}", actionText);
if (null != logAction)
{
logAction(beginText);
}
else
{
LogUtil.Log(beginText);
}
Stopwatch stopWatch = Stopwatch.StartNew();
T t = executeFunc(); // Execute the action
stopWatch.Stop();
string endText = string.Format("End Execute Timed Action: {0}", actionText);
string durationText = string.Format("Total Execution Time (for {0}): {1}", actionText, stopWatch.Elapsed);
if (null != logAction)
{
logAction(endText);
logAction(durationText);
}
else
{
LogUtil.Log(endText);
LogUtil.Log(durationText);
}
return t;
}
public static void ExecuteTimedAction(string actionText, Action executeAction)
{
bool executed = ExecuteTimedAction<bool>(actionText, () => { executeAction(); return true; }, null);
}
/// <summary>
/// Method for performing an operation and tracking the time it takes to complete (does not return a value)
/// </summary>
/// <param name="actionText">Title for the log entry</param>
/// <param name="action">The action (delegate void) to execute</param>
public static void ExecuteTimedAction(string actionText, Action executeAction, Action<string> logAction)
{
bool executed = ExecuteTimedAction<bool>(actionText, () => { executeAction(); return true; }, logAction);
}
#endregion
次に、ログ関数は次のようになります。ログ関数がExecuteTimedActionにハードコードされていないことがわかるので、ログアクションを渡すことができます。
ログクラスでは、エントリアセンブリ名を静的変数に一度保存し、それをすべてのログに使用します...
private static readonly string _entryAssemblyName = Assembly.GetEntryAssembly().GetName().Name;
これにより、リファクタリングについて考えるのに十分な食べ物が得られることを願っています!