MoveNext而不是实际的方法/任务名称

时间:2014-03-23 23:03:13

标签: c# log4net async-await methodbase

使用声明为:

的log4net
private readonly ILog log = 
       LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType());

在异步方法或任务中,如下所示:

public async void CheckSomething()
{
    log.Info(null);
    //....
}

记录MoveNext而不是CheckSomething。 知道如何让它记录实际的方法名称吗?

7 个答案:

答案 0 :(得分:22)

所有async方法都会重写到状态机中,以满足方法中潜在的await值。代码所在的最终方法是MoveNext方法,这是log4net报告的方法。

在运行时没有好办法从MoveNext转换到最初编写代码的实际方法。它们在元数据级别上有些断开连接。您可能只需要直接记录名称

答案 1 :(得分:8)

简短:根据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<AsyncStateMachineAttribute>() 
        where attr != null && attr.StateMachineType == generatedType 
        select methodInfo;

    // If this throws, the async method scanning failed.
    var foundMethod = matchingMethods.Single();
    return foundMethod;
}

长(免责声明)

不要在生产中使用它。它依赖于编译器行为,这可能会在将来的版本中发生变化,恕不另行通知。关于编译器的以下假设是:

  1. 实际运行的异步方法是在生成的类型中生成的。
  2. 生成的类型是原始类型的嵌套类型,包含原始的手写方法。
  3. 原始方法获取编译器生成的属性AsyncStateMachine,其中包含生成的类型。
  4. 它适用于我的代码,我只在调试/测试期间将其用于运行时代码分析。请再次不要在生产代码中使用它

答案 2 :(得分:2)

使用它,效果很好...

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....
    }

答案 3 :(得分:1)

我在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]

答案 4 :(得分:1)

感谢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<AsyncStateMachineAttribute>()?.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;
    }
}

答案 5 :(得分:0)

使用扩展方法,该方法仅返回MethodBase的调用方成员名称。

public static class MemberBaseExtension
{
    public static string GetDeclaringName(this MethodBase methodBase, [CallerMemberName] string memberName = "")
    {
        return memberName;
    }
}

答案 6 :(得分:0)

您可以仅使用配置来执行类似的操作,但这与您提到的不完全相同。如果您能够阅读它并且不介意奇怪的语法,您可以使用 %type 模式打印出方法的限定名称,然后使用 %method 模式打印出方法。你甚至可以只打印一些限定名称来对抗长命名空间。

来自docs

<块引用>

用于输出发出日志请求的调用者的完全限定类型名称。此转换说明符可以选择后跟精度说明符,即括号中的十进制常量。

<块引用>

如果给出了精度说明符,则只会打印类名最右边的组件的相应数量。默认情况下,类名以完全限定的形式输出。

<块引用>

例如,对于类名“log4net.Layout.PatternLayout”,模式 %type{1} 将输出“PatternLayout”。

例如

<layout type="log4net.Layout.PatternLayout">
  <conversionPattern value="%date %5level %logger %type{2}.%method [%line] - %message%newline %exception" />
</layout>

MoveNext 打印而言,它看起来像:

2021-03-10 11:45:29,203  INFO StackOverflowLogger SubNamespace.ClassName+<MethodNameAsync>d__15.MoveNext [123] - Logging is starting...

只要异步方法存在 d__15.MoveNext,我们就不关心 SubNamespace.ClassName+<MethodNameAsync>