Подтвердить что ты не робот

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

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

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

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

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

logs MoveNext вместо CheckSomething. Любая идея, как заставить его регистрировать имя фактического метода?

4b9b3361

Ответ 1

Все методы async переписываются в машину состояний для удовлетворения потенциальных значений await внутри метода. Конечным методом, в котором работает код, является метод MoveNext, который представляет собой отчет log4net.

В режиме исполнения нет хорошего способа перехода от MoveNext к фактическому методу, в котором был изначально написан код. Они несколько отключены на уровне метаданных. Вам просто нужно прибегнуть к регистрации имени напрямую

Ответ 2

Короткая: с учетом метода 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;
}

Длинный (отказ от ответственности)

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

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

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

Ответ 3

Я написал простую оболочку вокруг 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 и т.д., вы можете просто добавить его в класс оболочки.

Примечание
это использует С# 5.0 [CallerMemberName]

Ответ 4

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

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

Используйте это, прекрасно работает...

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

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

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