Tap Global Exception Handler

TAP global exception handler

This is actually a good question, if I understood it correctly. I initially voted to close it, but now retracted my vote.

It is important to understand how an exception thrown inside an async Task method gets propagated outside it. The most important thing is that such exception needs to be observed by the code which handles the completion of the task.

For example, here is a simple WPF app, I'm on NET 4.5.1:

using System;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication_22369179
{
public partial class MainWindow : Window
{
Task _task;

public MainWindow()
{
InitializeComponent();

AppDomain.CurrentDomain.UnhandledException +=
CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException +=
TaskScheduler_UnobservedTaskException;

_task = DoAsync();
}

async Task DoAsync()
{
await Task.Delay(1000);

MessageBox.Show("Before throwing...");

GCAsync(); // fire-and-forget the GC

throw new ApplicationException("Surprise");
}

async void GCAsync()
{
await Task.Delay(1000);

MessageBox.Show("Before GC...");

// garbage-collect the task without observing its exception
_task = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
}

void TaskScheduler_UnobservedTaskException(object sender,
UnobservedTaskExceptionEventArgs e)
{
MessageBox.Show("TaskScheduler_UnobservedTaskException:" +
e.Exception.Message);
}

void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
MessageBox.Show("CurrentDomain_UnhandledException:" +
((Exception)e.ExceptionObject).Message);
}
}
}

Once ApplicationException has been thrown, it goes unobserved. Neither TaskScheduler_UnobservedTaskException nor CurrentDomain_UnhandledException gets invoked. The exception remains dormant until the _task object gets waited or awaited. In the above example it never gets observed, so TaskScheduler_UnobservedTaskException will be invoked only when the task gets garbage-collected. Then this exception will be swallowed.

The old .NET 4.0 behavior, where the AppDomain.CurrentDomain.UnhandledException event gets fired and the app crashes, can be enabled by configuring ThrowUnobservedTaskExceptions in app.config:

<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>

When enabled this way, AppDomain.CurrentDomain.UnhandledException will still be fired after TaskScheduler.UnobservedTaskException when the exception gets garbage-collected, rather than on the spot where it thrown.

This behavior is described by Stephen Toub in his "Task Exception Handling in .NET 4.5" blog post. The part about task garbage-collection is described in the comments to the post.

That's the case with async Task methods. The story is quite different for async void methods, which are typically used for event handlers. Let's change the code this way:

public MainWindow()
{
InitializeComponent();

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

this.Loaded += MainWindow_Loaded;
}

async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(1000);

MessageBox.Show("Before throwing...");

throw new ApplicationException("Surprise");
}

Because it's async void there's no Task reference to hold on to (so there's nothing to be possibly observed or garbage-collected later). In this case, the exception is thrown immediately on the current synchronization context. For a WPF app, Dispatcher.UnhandledException will be fired first, then Application.Current.DispatcherUnhandledException, then AppDomain.CurrentDomain.UnhandledException. Finally, if none of these events are handled (EventArgs.Handled is not set to true), the app will crash, regardless of the ThrowUnobservedTaskExceptions setting. TaskScheduler.UnobservedTaskException is not getting fired in this case, for the same reason: there is no Task.

Global Exception Handling for winforms control

You need to handle the System.Windows.Forms.Application.ThreadException event for Windows Forms. This article really helped me: http://bytes.com/forum/thread236199.html.

Is there a global exception handler for NServiceBus?

As mentioned in the original post the recommended solution is to use ServicePulse for monitoring errors. The client I currently work for is using a custom made centralized logger, and we want NServiceBus to log to this log store when messages are forwarded to the error queue.

We could have achieved this by just editing the log4net config if the exception from NServiceBus had included the original exception, currently NServiceBus just logs a generic error message with no details about what caused the failure.

NServiceBus has a class named NServiceBus.Faults.ErrorsNotifications which contains the following observables:

  • MessageSentToErrorQueue
  • MessageHasFailedAFirstLevelRetryAttempt
  • MessageHasBeenSentToSecondLevelRetries

You can subscribe to these observables when the endpoint starts, like in the following example which logs an error when messages are sent to the error queue:

public class GlobalErrorHandler : IWantToRunWhenBusStartsAndStops
{
private readonly ILogger _logger;
private readonly BusNotifications _busNotifications;
readonly List<IDisposable> _notificationSubscriptions = new List<IDisposable>();

public GlobalErrorHandler(ILogger logger, BusNotifications busNotifications)
{
_logger = logger;
_busNotifications = busNotifications;
}

public void Start()
{
_notificationSubscriptions.Add(_busNotifications.Errors.MessageSentToErrorQueue.Subscribe(LogWhenMessageSentToErrorQueue));
}

public void Stop()
{
foreach (var subscription in _notificationSubscriptions)
{
subscription.Dispose();
}
}

private void LogWhenMessageSentToErrorQueue(FailedMessage message)
{
var properties = new
{
MessageType = message.Headers["NServiceBus.EnclosedMessageTypes"],
MessageId = message.Headers["NServiceBus.MessageId"],
OriginatingMachine = message.Headers["NServiceBus.OriginatingMachine"],
OriginatingEndpoint = message.Headers["NServiceBus.OriginatingEndpoint"],
ExceptionType = message.Headers["NServiceBus.ExceptionInfo.ExceptionType"],
ExceptionMessage = message.Headers["NServiceBus.ExceptionInfo.Message"],
ExceptionSource = message.Headers["NServiceBus.ExceptionInfo.Source"],
TimeSent = message.Headers["NServiceBus.TimeSent"]
};

_logger.Error("Message sent to error queue. " + properties, message.Exception);
}
}

The observable is implemented by using Reactive Extensions, so you will have to install the NuGet package Rx-Core for this to work.

Catching unhandled exceptions with asp.net webapi and TPL

Exceptions that are thrown from inside a running task aren't unhandled. They are captured on that task turning it faulted.

The correct way to handle these exceptions would be to await each task, or register continuations using Task.ContinueWith. However, if you want a global handler you can use the TaskScheduler.UnobservedTaskException event.

When there's an exception inside a task it's handled and stored on the Task object for you to observe. If the task gets garbage collected and the exception wasn't yet observed by any code .Net knows that it will never be observed (as you don't have a reference to the task) and raises the TaskScheduler.UnobservedTaskException event (in .Net 4.0 it will also crash the app and that behavior can be replicated in newer versions as well).

So, an event handler for TaskScheduler.UnobservedTaskException will globally handle all exceptions raised from tasks, but only after they have been GCed (which isn't immediately):

TaskScheduler.UnobservedTaskException += (sender, args) => HandleException(args.Exception);

How can I preserve exception context in an async console application using AsyncPump?

You would probably see the desired behavior if you used async void signature for MainAsync, rather than async Task. This doesn't mean you should change your code (async void is almost never a good idea), it just means that the existing behavior is perfectly normal.

An exception thrown from async Task methods is not re-thrown immediately. Rather, it is stored inside the Task object (with the captured stack context) and will be re-thrown when the task's result gets observed via task.Result, task.Wait(), await task or task.GetAwaiter().GetResult().

I posted a bit more detailed explanation of this: TAP global exception handler.

On a side note, I use a slightly modified version of AsyncPump, which makes sure the initial task starts executing asynchronously (i.e., after the core loop has started pumping), with TaskScheduler.Current being TaskScheduler.FromCurrentSynchronizationContext():

/// <summary>
/// PumpingSyncContext, based on AsyncPump
/// http://blogs.msdn.com/b/pfxteam/archive/2012/02/02/await-synchronizationcontext-and-console-apps-part-3.aspx
/// </summary>
class PumpingSyncContext : SynchronizationContext
{
BlockingCollection<Action> _actions;
int _pendingOps = 0;

public TResult Run<TResult>(Func<Task<TResult>> taskFunc, CancellationToken token = default(CancellationToken))
{
_actions = new BlockingCollection<Action>();
SynchronizationContext.SetSynchronizationContext(this);
try
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

var task = Task.Factory.StartNew(
async () =>
{
OperationStarted();
try
{
return await taskFunc();
}
finally
{
OperationCompleted();
}
},
token, TaskCreationOptions.None, scheduler).Unwrap();

// pumping loop
foreach (var action in _actions.GetConsumingEnumerable())
action();

return task.GetAwaiter().GetResult();
}
finally
{
SynchronizationContext.SetSynchronizationContext(null);
}
}

void Complete()
{
_actions.CompleteAdding();
}

// SynchronizationContext methods
public override SynchronizationContext CreateCopy()
{
return this;
}

public override void OperationStarted()
{
// called when async void method is invoked
Interlocked.Increment(ref _pendingOps);
}

public override void OperationCompleted()
{
// called when async void method completes
if (Interlocked.Decrement(ref _pendingOps) == 0)
Complete();
}

public override void Post(SendOrPostCallback d, object state)
{
_actions.Add(() => d(state));
}

public override void Send(SendOrPostCallback d, object state)
{
throw new NotImplementedException("Send");
}
}

It's also possible to change this part:

return task.GetAwaiter().GetResult();

To this:

return task.Result;

In this case, the exception will be propagated to the caller as AggregateException, with AggregateException.InnerException pointing to the original exception from inside the async method.

common spring integration exception handler

I'd say that you go right way dividing logic to achieve loosely-coupled pronciple. That's correct that different modules (or integration flows) can have their own error handling logic and that's good that you use error-channel to catch those downstream exceptions.

I see one good solution for your global tweeting error handling. We can't use here built-in error-channel, because any custom one has precedence. But we can add an aspect for them and it is global <wire-tap> with pattern for all those target error-channels. In this case all your developers just should follow with naming convention for all their error-channels to allow that global channel-interceptor to handle messages for them.

However pay attention here, please, that WireTap processes messages in preSend. So global error-handling will be before any custom logic. In this case you may be need to write simple WireTap extension to do that in the postSend

How to add a global error handler for all redux-observable epics?

redux-observable leans on idiomatic paradigms of RxJS for nearly everything you would do. When you combine Epics together, you're creating a new Epic that has the normal function (action$, store) signature. So this new Epic represents everything output from those individual Epics, including errors.

You can leverage that fact by defining your own rootEpic and composing the result of your combineEpics() call. Since Epics are factories, you'll need to remember to pass along (action$, store)!

Here's one way:

export const rootEpic = (action$, store) =>
combineEpics(epic1, epic2, epic3)(action$, store) // <-- call epic w/ args
.do({ error: err => console.error(err) });

You probably shouldn't use the .catch() operator for this, because .catch() is for returning some other Observable to use instead, when there is an error. If you just want to log the error out (and not try to recover), .do() is the ideal operator--its purpose is to allow you to do external side effects (like logging) that have no effect on the stream itself.

Composing Epics is very powerful, not just in this case, but many others that we're planning to describe soon.

Related to the .catch() operator, you may also be interested in the RFC for better default error handling.

How to handle unhandled exceptions in Task awaiter

The Task<bool> returned from your Test method will become faulted. If you await that task (or call Wait() on it) then the exception will be rethrown at that point. If you choose to ignore the faulted state of the task, then yes, you'll be ignoring the exception.



Related Topics



Leave a reply



Submit