代码之家  ›  专栏  ›  技术社区  ›  user1307346

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

  •  41
  • user1307346  · 技术社区  · 12 年前

    使用声明为的log4net:

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

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

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

    日志 MoveNext 而不是 CheckSomething . 知道如何让它记录一个实际的方法名吗?

    7 回复  |  直到 12 年前
        1
  •  40
  •   noelicus    9 年前

    全部的 async 方法被重写为状态机以满足潜在的 await 方法中的值。代码所在的最后一个方法是 MoveNext 方法是什么 log4net 正在报告。

    在运行时确实没有很好的方法从 移到下一行 到最初编写代码的实际方法。它们在元数据级别上有些脱节。您可能只需要直接登录名称

        2
  •  13
  •   Jacek Gorgoń    11 年前

    短的 :给定 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,其中提供了生成的类型。

    它在我的代码中工作,我只在调试/测试期间使用它进行运行时代码分析。请再说一遍, 不要在生产代码中使用它 .

        3
  •  7
  •   bboyle1234    8 年前

    感谢Jacek Gorgo的回答,这是我提出的实用程序。它有一些改进,但要很好地使用匿名或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;
        }
    }
    
        4
  •  6
  •   ScubaSteve    11 年前

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

        5
  •  5
  •   Stephen Buck    7 年前

    使用这个,效果很好。。。

    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....
        }
    
        6
  •  2
  •   Wouter    7 年前

    具有仅返回MethodBase的调用方成员名称的扩展方法。

    public static class MemberBaseExtension
    {
        public static string GetDeclaringName(this MethodBase methodBase, [CallerMemberName] string memberName = "")
        {
            return memberName;
        }
    }
    
        7
  •  0
  •   Jimenemex    5 年前

    您可以通过使用配置来完成类似的操作,但这并不是您所提到的。如果你能读懂它,并且不介意奇怪的语法,你可以使用 %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> .