Reputation: 3784
I am looking to get the method/action name from a task in C#. Specifically I am implementing a custom task scheduler, and would like to generate statistics on the duration a task runs, which I will then aggregate by the method running inside of the task. In the visual studio debugger you can access this and see the m_action private variable, as well as the debugger display annotation, displays it as Method={0}. Is there any way to get access to this from the Task itself?
Upvotes: 4
Views: 6610
Reputation: 59
Here is the only thing that is working for me with async task in a background thread :
private static string MethodName(this Task task)
{
const string start = "+<";
const string end = ">d__";
var fullName = task.GetType().FullName;
if (string.IsNullOrEmpty(fullName))
return string.Empty;
var methodName = fullName.Substring(fullName.IndexOf(start) + start.Length);
return methodName.Remove(methodName.IndexOf(end));
}
Hope it might help or give ideas.
Edit:
Actually, I was trying to get the method name of a task that I launch in background, a bit like fire but don't forget. And, what I've inderstood so far is that where I need the name of the task, all the previous answer didn't solve my case since the action.Method or task.Method is always null.
Here is an exemple of my use case :
_anyDBService.CleanStuffAsync(stuff).RunInBackground(_logger);
In that exemple, I just wanted the method name CleanStuffAsync. And I end up with the previous simple MethodeName helper function in my TaskExtensions file, here it is :
public static class TaskExtensions
{
public static void RunInBackground(this Action action, ILogger logger) =>
action.RunWorkInBackground(logger, action.MethodName());
public static void RunInBackground(this Delegate fonc, ILogger logger) =>
fonc.RunWorkInBackground(logger, fonc.MethodName().GetDefaultValue());
public static void RunInBackground(this Task task, ILogger logger) =>
task.RunInBackground(logger, task.MethodName());
private static void RunInBackground(this Task task, ILogger logger, string methodeName) =>
task.RunWorkInBackground(logger, methodeName);
private static string MethodName(this Delegate fonc) =>
fonc.Method.Name;
private static string MethodName(this Action action) =>
action.Method.Name;
private static string MethodName(this Task task)
{
const string start = "+<";
const string end = ">d__";
var fullName = task.GetType().FullName;
if (string.IsNullOrEmpty(fullName))
return string.Empty;
var methodName = fullName[(fullName.IndexOf(start) + start.Length)..];
return methodName.Remove(methodName.IndexOf(end));
}
private static void RunWorkInBackground<T>(this T work, ILogger logger, string methodName)
{
const string errorMsgPrefix = "Unexpected error occured in ";
try
{
ThreadPool.QueueUserWorkItem(async cancellationToken =>
{
try
{
logger.LogInformation(string.Concat("Running task '", methodName, "' in background..."));
await Task.Run(() => work);
}
catch (Exception ex)
{
logger.LogError(string.Concat(errorMsgPrefix, "'", methodName, "' :", Environment.NewLine, ex.Message, Environment.NewLine, ex.StackTrace));
}
});
}
catch (Exception ex)
{
logger.LogError(string.Concat(errorMsgPrefix, "'", nameof(RunWorkInBackground), "' :", Environment.NewLine, ex.Message));
throw;
}
}
}
Upvotes: 0
Reputation: 35881
Well, you could use reflection to get at the private m_action
field, given a Task
variable task
:
var fieldInfo = typeof(Task).GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic);
Delegate action = fieldInfo.GetValue(task) as Delegate;
Then get the Name
of the method and the DeclaringType
:
var name = action.Method.Name;
var type = action.Method.DeclaringType.FullName;
To get the fully qualified method (type + "." + name
)...
But, as soon as the task executes to completion, m_action
is null
. I'm not sure how this would apply with TaskFactory.StartNew...
Upvotes: 2
Reputation: 2841
You could inherit from Task to make this real easy... I'm just going to implement the first constructor here for the example:
public class NamedTask : Task {
public string MethodName { get; set; }
public NamedTask(Action action) : base(action) {
MethodName = action.Method.Name;
}
public NamedTask(Action action, CancellationToken cancellationToken) : base(action, cancellationToken) {}
public NamedTask(Action action, TaskCreationOptions creationOptions) : base(action, creationOptions) {}
public NamedTask(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions) : base(action, cancellationToken, creationOptions) {}
public NamedTask(Action<object> action, object state) : base(action, state) {}
public NamedTask(Action<object> action, object state, CancellationToken cancellationToken) : base(action, state, cancellationToken) {}
public NamedTask(Action<object> action, object state, TaskCreationOptions creationOptions) : base(action, state, creationOptions) {}
public NamedTask(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions) : base(action, state, cancellationToken, creationOptions) {}
}
After that...
NamedTask task = new NamedTask(() => AsyncMethod(arg1, arg2, argN));
string methodName = task.MethodName; // there's the name!
More examples. Inherit from Task<T>
:
public class NamedTask<T> : Task<T> {
public string MethodName { get; set; }
public NamedTask(Func<T> function) : base(function) {
MethodName = function.Method.Name;
}
public NamedTask(Func<T> function, string methodName) : base(function) {
MethodName = methodName;
}
...
}
Handle anonymous methods:
NamedTask<bool> task2 = new NamedTask<bool>(() => {
// some arbitrary code
return true;
});
NamedTask<bool> task3 = new NamedTask<bool>(() => {
// some arbitrary code
return true;
}, "ReturnTrueMethod");
string methodName2 = task2.MethodName; // returns "<LongRunning_Async>b__19"
string methodName3 = task3.MethodName; // returns "ReturnTrueMethod"
Upvotes: 3
Reputation: 9780
Envorinment.StackTrace
from inside the task or directly called methods by it.Upvotes: 0