MoveNext вместо фактического имени метода / задачи

Использование log4net объявлено как:

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

В асинхронном методе или задаче, например:

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

бревна MoveNext вместо CheckSomething, Есть идеи, как сделать так, чтобы в журнал входило реальное имя метода?

7 ответов

Решение

Все 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<AsyncStateMachineAttribute>() 
        where attr != null && attr.StateMachineType == generatedType 
        select methodInfo;

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

Long (Отказ от ответственности)

Не используйте это в производстве. Это зависит от поведения компилятора, которое может измениться в будущей версии без предварительного уведомления. Следующие предположения о компиляторе сделаны:

  1. Фактически запущенный асинхронный метод генерируется внутри сгенерированного типа.
  2. Сгенерированный тип - это вложенный тип исходного типа, содержащий оригинальный рукописный метод.
  3. Исходный метод получает сгенерированный компилятором атрибут AsyncStateMachine с предоставленным в нем сгенерированным типом.

Это работает в моем коде, где я использую его для анализа кода во время выполнения только во время отладки / тестирования. Опять же, пожалуйста, не используйте его в рабочем коде.

Благодаря ответу Яцека Горгоня, вот утилита, которую я придумала. Он имеет несколько улучшений, но ему еще предстоит пройти долгий путь, чтобы хорошо работать с анонимными или лямбда-методами.

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

Я написал простую обертку вокруг 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....
    }

С помощью метода расширения, который просто возвращает имя вызывающего члена для MethodBase.

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

Вы можете сделать что-то подобное, просто используя конфигурацию, но это не совсем то, что вы упомянули. Если вы можете прочитать его и не возражаете против странного синтаксиса, вы можете использовать %typeшаблон, чтобы распечатать полное имя метода, а затем %methodшаблон для печати метода. Вы даже можете напечатать только часть квалифицированного имени для борьбы с длинными пространствами имен.

Из документов:

Используется для вывода полного имени типа вызывающего объекта, выдающего запрос на регистрацию. За этим спецификатором преобразования может дополнительно следовать спецификатор точности, то есть десятичная константа в скобках.

Если задан спецификатор точности, то будет напечатано только соответствующее количество самых правых компонентов имени класса. По умолчанию имя класса выводится в полной форме.

Например, для имени класса "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>.

Другие вопросы по тегам