我这样做的方法是为 NLog 创建一个包装类,它将包装每个日志方法,并混淆 methodName 并使用 StackTrace 对象来获取方法名称。那你就不用每次都写了;调用 Logging 包装器方法的方法的方法名称是自动注入的。
它会看起来更干净,因为您不会到处都有 {0} 和 methodName。
您甚至可以更进一步,创建一个日志包装类,该类接受日志字符串和一个动作,执行该动作,并使用 StackTrace 对象一次性调用日志对象。
我已经使用它来执行时间操作并记录它们,一次调用即可完成所有操作并节省重复代码。我的方法 ExecuteTimedAction(string logString, Action actionToExecute) 使用秒表,记录开始字符串,启动秒表,执行方法(Action 委托),停止秒表,并再次记录两个日志,其中包含时间戳、程序集名称,以及发起调用的方法的名称。
获取方法的代码很简单,使用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 成员变为 LoggerWrapper,而不是 Logger,并且您以完全相同的方式调用它,但您从代码中删除了 {0}。您需要检查空值,也许如果没有其他参数,则有一个方法重载,只在没有参数的情况下调用;我不确定 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;
希望这能让您在重构方面有足够的思考空间!