Quartz.Net Job Listeners, Part 3 of Quartz.net Listeners in detail

This is the third post in the Quartz.Net Listener Tutorial series. It’s also the third part of the introduction to listeners overview series. You can find Part 1 here and Part 2 here. Today we’ll be looking at job listeners and how to implement one.

As we mentioned in Part 1, job listeners get notified of job level events. To implement a job listener, you need to implement the IJobListener interface. Here’s what that interface looks like:

public interface IJobListener
{
    string Name { get; }
    void JobToBeExecuted(IJobExecutionContext context);
    void JobExecutionVetoed(IJobExecutionContext context);
    void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException);
}

 

You’ll notice that job listeners get notified when the job is about to be executed, after it was executed, and if it’s execution was vetoed by a trigger.  Let’s build a job listener that measures how long it took for job to execute.  Download the sample code from the Quartz.Net ebook if you want to follow along.

Here’s the code for the job duration job listener we’ll be going over:

using Quartz;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Examples { public class JobDurationListenerExample : IJobListener { public void JobExecutionVetoed(IJobExecutionContext context) { // Do nothing }

    public void JobToBeExecuted(IJobExecutionContext context)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        Console.WriteLine("Job {0} in group {1} is about to be executed", context.JobDetail.Key.Name, context.JobDetail.Key.Group);
        _Stopwatches.Add(context.FireInstanceId, stopwatch);
    }

    public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
    {
        var elapsed = _Stopwatches[context.FireInstanceId].ElapsedMilliseconds;
        Console.WriteLine("Job {0} in group {1} was executed in {2}ms", context.JobDetail.Key.Name, context.JobDetail.Key.Group, elapsed);
    }

    public string Name
    {
        get { return "JobDurationListener"; }
    }
    private Dictionary<string, Stopwatch> _Stopwatches = new Dictionary<string, Stopwatch>();
}

}

 

First, let’s look at the JobExecutionVetoed method (line 12). We’re not going to do anything there, so I just added a comment. If you wanted to be notified if a trigger ever vetoed a job execution, then add your code here. If you need additional information about which job is running or which trigger fired, it’s all available in the context object. You’ll see how it’s used in a minute.

Second, let’s look at the JobToBeExecuted method (line 17). In this method we’re creating a stopwatch, starting it and adding it to a dictionary so that we can keep track of which job got fired. This job listener listens to all jobs, so we have to keep track of one stopwatch for each job fired. You may want to just listen to events from a given job and you should probably cleanup the stopwatches. Alternatively, you could just store the start time back in the context and then pull it out to compute the duration. Obviously this isn’t production quality code, so use it wisely.

Third, let’s look at the JobWasExecuted method (line 25). Here we pull out the stopwatch and then get the time it took to execute the job.

One last thing… you’ll have to give your listener a name that will be returned by the Name property (line 31).

Now, lets run the samples solution (make sure EmbeddedScheduler is set up as your startup project). You’ll see some output similar to this:

The scheduler called SchedulerStarting
The scheduler called JobAdded
The scheduler called JobScheduled
The scheduler called JobAdded
The scheduler called JobScheduled
The scheduler called JobUnscheduled
The scheduler called JobScheduled
The scheduler called JobUnscheduled
The scheduler called JobScheduled
The scheduler called SchedulerStarted
IsStarted=True
SchedulerInstanceId=NON_CLUSTERED
SchedulerName=ServerScheduler
The scheduler is running. Press any key to stop
The scheduler called JobToBeExecuted
The scheduler called JobToBeExecuted
Job directoryScanJobExample in group directoryScanJobExample is about to be executed
Job addDirectoryScanListener in group directoryScanJobExample is about to be executed
The scheduler called JobWasExecuted
Job addDirectoryScanListener in group directoryScanJobExample was executed in 1ms
The scheduler called JobWasExecuted
Job directoryScanJobExample in group directoryScanJobExample was executed in 1ms
The scheduler called TriggerFinalized
The scheduler called JobDeleted
The scheduler called JobToBeExecuted
Job directoryScanJobExample in group directoryScanJobExample is about to be executed
The scheduler called JobWasExecuted
Job directoryScanJobExample in group directoryScanJobExample was executed in 0ms
The scheduler called JobToBeExecuted
Job directoryScanJobExample in group directoryScanJobExample is about to be executed
The scheduler called JobWasExecuted
Job directoryScanJobExample in group directoryScanJobExample was executed in 0ms

The output shows that we are now logging how long it takes to execute each job (look at lines 20, 22, 28 and 32). The jobs that exist in the scheduler don’t take very long to run,  but you can try it out with a longer running job.

That’s it for job listeners. The next post will cover trigger listeners.