Oleg Vazhnev
Oleg Vazhnev

Reputation: 24067

is it possible to catch when any Task terminates due exception and log?

Is it possible to catch when any Task terminates due exception and log? I've added CurrentDomain_UnhandledException handling but this doesn't help.

I create tasks using Task.Factory.StartNew() as usual. When somewhere inside such task exception occurs it crashes silently (but it supposed to work forever, i'm also using LongRunning option). So I want to be notified about such behavior.

Ideallly I want to set some option somewhere to be notified when any Task crashes due exception.

If it is not possible then likely I should add something to each Task I create? Of course I can just add big try{} finally{} block inside each Task, but probably there are better solutions?

Upvotes: 9

Views: 1829

Answers (5)

Furqan Safdar
Furqan Safdar

Reputation: 16698

Assuming you have a Test as Task to run:

static int Test()
{
    throw new Exception();
}
  1. First Approach - Process exception in the caller's thread:

    Task<int> task = new Task<int>(Test);
    task.Start();
    
    try
    {
        task.Wait();
    }
    catch (AggregateException ex)
    {
        Console.WriteLine(ex);
    }
    

    Note: The exception will be of type AggregateException. All actual exceptions are available through ex.InnerExceptions property.

  2. Second Approach - Process exception in some task's thread:

    Define the ExceptionHandler this way:

    static void ExceptionHandler(Task<int> task)
    {
        var ex = task.Exception;
        Console.WriteLine(ex);
    }
    

    Usage:

    Task<int> task = new Task<int>(Test);
    task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
    task.Start();
    

Reference: How to: Handle Exceptions Thrown by Tasks

Upvotes: 6

Dave New
Dave New

Reputation: 40002

You can create a OnlyOnFaulted continuation on your Task which observes the exception and logs/reports the problem.

t.ContinueWith(task =>
{
    // Report and log error
}, System.Threading.CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());

The above code will run the task on the UI thread because of TaskScheduler.FromCurrentSynchronizationContext(). This may be necessary if you are using winforms and need to notify the user.

Upvotes: 0

margabit
margabit

Reputation: 2954

You can use an extension method that performs an operation when an exception has ocurred. This happens when the Task gets Faulted. So if it has another tasks to continue with, the next one can check if the previous task was faulted and Log the exception.

I usually use this methods:

//If you want to chain more tasks..
public static Task<T> Continue<T>(this Task<T> task, Action<T> action)
{
    if (!task.IsFaulted)
    {
        task.ContinueWith((t) => action(t.Result), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    return task;
}

public static Task OnException(this Task task, Action<Exception> onFaulted)
{
    task.ContinueWith(c =>
    {
        var excetion = c.Exception;
            onFaulted(excetion);
    },
        TaskContinuationOptions.OnlyOnFaulted |
        TaskContinuationOptions.ExecuteSynchronously);
    return task;
}

So you can use:

Task.Factory.StartNew(...).OnException(ex => Log(ex));

Hope it helps.

Upvotes: 2

Rohit Vats
Rohit Vats

Reputation: 81243

Wrap your task.Wait() in a try/catch block and catch AggregateException. Something like this -

Task<string[]> task1 = Task<string[]>.Factory.StartNew(() => GetAllFiles(path));

// Use this line to throw an exception that is not handled.
try
{
    task1.Wait();
}
catch (AggregateException ae)
{

    ae.Handle((x) =>
    {
        if (x is UnauthorizedAccessException) // This we know how to handle.
        {
            Console.WriteLine("You do not have permission to access all folders
                                in this path.");
            Console.WriteLine("See your network administrator or try
                                another path.");
            return true;
        }
        return false; // Let anything else stop the application.
    }); 
}

Details can be found here - Handle exceptions thrown by Task.

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500365

For tasks that you create yourself, it's reasonably simple: create your own methods which call Task.Factory.StartNew(), but then also call Task.ContinueWith(loggingDelegate, TaskContinuationOptions.OnlyOnFaulted before returning the task.

The problem is that that won't add a fault handler for tasks created by other bits of infrastructure - including by async methods in C# 5. It still might be useful to you though.

You can also use TaskScheduler.UnobservedTaskException, but as per the name that will only be called for exceptions which aren't already observed by something else. (Again, that may be fine for you...)

Upvotes: 2

Related Questions