MoveNext而不是实际的方法/任务名称
使用log4net声明为:
private readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType());
在异步方法或任务中,如下所示:
public async void CheckSomething() { log.Info(null); //.... }
记录MoveNext
而不是CheckSomething
。 知道如何让它记录一个实际的方法名称?
所有async
方法都被重写到状态机中,以满足方法中潜在的await
值。 代码所在的最终方法是MoveNext
方法,这是log4net
报告的方法。
在运行时没有很好的方法可以从MoveNext
转换到最初编写代码的实际方法。 它们在元数据级别上有些断开连接。 您可能只需要直接记录名称
简短 :给定MoveNext()
方法,试试这个:
private static MethodBase GetRealMethodFromAsyncMethod(MethodBase asyncMethod) { var generatedType = asyncMethod.DeclaringType; var originalType = generatedType.DeclaringType; var matchingMethods = from methodInfo in originalType.GetMethods() let attr = methodInfo.GetCustomAttribute() where attr != null && attr.StateMachineType == generatedType select methodInfo; // If this throws, the async method scanning failed. var foundMethod = matchingMethods.Single(); return foundMethod; }
长(免责声明)
不要在生产中使用它。 它依赖于编译器行为,这可能会在未来通知的情况下在将来的版本中发生变化。 关于编译器的以下假设是:
- 实际运行的异步方法是在生成的类型中生成的。
- 生成的类型是原始类型的嵌套类型,包含原始的手写方法。
- 原始方法获取编译器生成的属性AsyncStateMachine,其中提供了生成的类型。
它适用于我的代码,我只在调试/测试期间将其用于运行时代码分析。 请再次,请不要在生产代码中使用它 。
感谢JacekGorgoń的回答,这是我提出的实用工具。 它有一些改进,但仍然有很长的路要走很好的匿名或lambda方法。
static string GetMethodContextName() { var name = new StackTrace().GetFrame(1).GetMethod().GetMethodContextName(); } static string GetMethodContextName(this MethodBase method) { if (method.DeclaringType.GetInterfaces().Any(i => i == typeof(IAsyncStateMachine))) { var generatedType = method.DeclaringType; var originalType = generatedType.DeclaringType; var foundMethod = originalType.GetMethods(Instance | Static | Public | NonPublic | DeclaredOnly) .Single(m => m.GetCustomAttribute()?.StateMachineType == generatedType); return foundMethod.DeclaringType.Name + "." + foundMethod.Name; } else { return method.DeclaringType.Name + "." + method.Name; } }
这是一个示例用法:
class Program { static void Main(string[] args) { // outputs Program.Main Console.WriteLine(GetMethodContextName()); Test().Wait(); } static async Task Test() { // outputs Program.Test Console.WriteLine(GetMethodContextName()); await Task.CompletedTask; } }
我在log4net周围写了一个简单的包装器。
public class Logger { private ILog _Log { get; set; } public Logger(Type declaringType) { _Log = LogManager.GetLogger(declaringType); } public void Error(Exception exception, [CallerMemberName] string callerMemberName = "") { _Log.Error(callerMemberName, exception); } }
在执行日志记录的代码中,只需:
private Logger Log = new Logger(MethodBase.GetCurrentMethod().DeclaringType);
当然,如果你想做Info,Debug等事情,你可以将它添加到包装类中。
注意
这利用了c#5.0 [CallerMemberName]
用这个,效果很好……
public void Log(Microsoft.Extensions.Logging.LogLevel level, string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) { //do your logging here.... }