Unhandled Exception in Quartz.NET job

Logging undhandled exceptions is usually done by adding a delegate to System.AppDomain.CurrentDomain.UnhandledException:

System.AppDomain.CurrentDomain.UnhandledException += (sender,
                                                      eventArgs) =>
  var exception = eventArgs.ExceptionObject as System.Exception;

  // TODO add logging

This did also apply to the execution of Quartz.IJob implementations - but at some point Quartz.NET wrapped the execution of jobs with a try/catch, which breaks the bubbling of the exception to the AppDomain’s level.

Nevertheless, you don’t have to give up hope, there are some solutions available, based on adding a Quartz.IJobListener to the scheduler.


The most convenient approach is to add a Quartz.Plugin.History.LoggingJobHistoryPlugin instance to the scheduler, which forwards any events of the trigger to Common.Logging:

<?xml version="1.0" encoding="utf-8" ?>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <!-- TODO configure logging here -->
    <!-- TODO configure other quartz settings here -->
    <add key="quartz.plugin.triggerHistory.type" value="Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz" />

Custom Quartz.IJobListener implementation

Another approach is to create a custom job listener:

public class ExceptionOccuredJobListener : Quartz.IJobListener
  public virtual void JobToBeExecuted(Quartz.IJobExecutionContext context)
    var message = $"Job {context.JobDetail.JobType} to be executed";

    // TODO add logging

  public virtual void JobExecutionVetoed(Quartz.IJobExecutionContext context)
    var message = $"Job {context.JobDetail.JobType} vetoed";

    // TODO add logging

  public virtual void JobWasExecuted(Quartz.IJobExecutionContext context,
                                     Quartz.JobExecutionException jobException)
    var exception = jobException?.GetBaseException();
    if (exception != null)
      var message = $"Job {context.JobDetail.JobType} threw an exception";

      // TODO add logging

  public virtual string Name => "ExceptionOccuredJobListener";

This listener can easily be added like:

var exceptionOccuredJobListener = new ExceptionOccuredJobListener();