MoveNext вместо фактического имени метода/задачи
Использование log4net, объявленного как:
private readonly ILog log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType());
В асинхронном методе или задаче, подобном этой:
public async void CheckSomething()
{
log.Info(null);
//....
}
logs MoveNext
вместо CheckSomething
.
Любая идея, как заставить его регистрировать имя фактического метода?
Ответы
Ответ 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;
}
}