Quartz.Plugins provides some useful ready-made plugins for your convenience.
Installation
You need to add NuGet package reference to your project which uses Quartz.
Install-Package Quartz.Plugins
Configuration
Plugins are configured by using either DI configuration extensions or adding required configuration keys.
Configuration key in in format quartz.plugin.{name-to-refer-with}.{property}.
See configuration reference on how to configure each plugin
Features
LoggingJobHistoryPlugin
Logs a history of all job executions (and execution vetoes) and writes the entries to configured logging infrastructure.
StructuredLoggingJobHistoryPlugin
Structured logging alternative to LoggingJobHistoryPlugin. Uses named message template parameters (e.g. {JobName}, {TriggerGroup}) instead of index-based placeholders, making log output compatible with structured logging sinks like Serilog and NLog. This avoids template cache memory leaks that can occur with the original plugin.
Message templates can be customized via properties. When customizing, the parameter names in templates are positionally mapped, so they must appear in the same order as the defaults.
Available template properties:
| Property | Parameters (in order) |
|---|---|
JobToBeFiredMessage | {JobGroup}, {JobName}, {TriggerGroup}, {TriggerName}, {FireTime}, {ScheduledFireTime}, {NextFireTime}, {RefireCount} |
JobSuccessMessage | {JobGroup}, {JobName}, {FireTime}, {TriggerGroup}, {TriggerName}, {Result} |
JobFailedMessage | {JobGroup}, {JobName}, {FireTime}, {TriggerGroup}, {TriggerName}, {ExceptionMessage} |
JobWasVetoedMessage | {JobGroup}, {JobName}, {TriggerGroup}, {TriggerName}, {FireTime} |
DI configuration:
services.AddQuartz(q =>
{
q.UseStructuredJobLogging();
});
Tips
Recommended over LoggingJobHistoryPlugin when using structured logging providers (Serilog, NLog, etc.).
StructuredLoggingTriggerHistoryPlugin
Structured logging alternative to LoggingTriggerHistoryPlugin. Logs trigger firings, misfires, and completions using named message template parameters for structured logging compatibility.
Message templates can be customized via properties. When customizing, the parameter names in templates are positionally mapped, so they must appear in the same order as the defaults.
Available template properties:
| Property | Parameters (in order) |
|---|---|
TriggerFiredMessage | {TriggerGroup}, {TriggerName}, {JobGroup}, {JobName}, {FireTime}, {ScheduledFireTime}, {NextFireTime}, {RefireCount} |
TriggerMisfiredMessage | {TriggerGroup}, {TriggerName}, {JobGroup}, {JobName}, {FireTime}, {ScheduledFireTime}, {NextFireTime} |
TriggerCompleteMessage | {TriggerGroup}, {TriggerName}, {JobGroup}, {JobName}, {CompletedTime}, {ScheduledFireTime}, {NextFireTime}, {TriggerInstructionCode} |
DI configuration:
services.AddQuartz(q =>
{
q.UseStructuredTriggerLogging();
});
Tips
Recommended over LoggingTriggerHistoryPlugin when using structured logging providers (Serilog, NLog, etc.).
ShutdownHookPlugin
This plugin catches the event of the VM terminating (such as upon a CRTL-C) and tells the scheduler to Shutdown.
XMLSchedulingDataProcessorPlugin
This plugin loads XML file(s) to add jobs and schedule them with triggers as the scheduler is initialized, and can optionally periodically scan the file for changes.
Warning
The periodically scanning of files for changes is not currently supported in a clustered environment.
JsonSchedulingDataProcessorPlugin
This plugin loads JSON file(s) to add jobs and schedule them with triggers as the scheduler is initialized, and can optionally periodically scan the file for changes. It is the JSON analog of XMLSchedulingDataProcessorPlugin.
Warning
The periodically scanning of files for changes is not currently supported in a clustered environment.
DI configuration:
services.AddQuartz(q =>
{
q.UseJsonSchedulingConfiguration(x =>
{
x.Files = ["quartz_jobs.json"];
x.ScanInterval = TimeSpan.FromMinutes(1);
x.FailOnFileNotFound = true;
x.FailOnSchedulingError = true;
});
});
See JSON Configuration for the full JSON file format and trigger type reference.
JobInterruptMonitorPlugin
This plugin catches the event of job running for a long time (more than the configured max time) and tells the scheduler to "try" interrupting it if enabled.
Tips
Quartz 3.3 or later required.
Each job configuration needs to have JobInterruptMonitorPlugin.JobDataMapKeyAutoInterruptable key's value set to true in order for plugin to monitor the execution timeout. Jobs can also define custom timeout value instead of global default by using key JobInterruptMonitorPlugin.JobDataMapKeyMaxRunTime.
var job = JobBuilder.Create<SlowJob>()
.WithIdentity("slowJob")
.UsingJobData(JobInterruptMonitorPlugin.JobDataMapKeyAutoInterruptable, true)
// allow only five seconds for this job, overriding default configuration
.UsingJobData(JobInterruptMonitorPlugin.JobDataMapKeyMaxRunTime, TimeSpan.FromSeconds(5).TotalMilliseconds.ToString())
.Build();
Authoring plugin configuration extensions
When you write your own ISchedulerPlugin, you can offer the same strongly typed configuration experience as the built-in plugins by creating an extension method that targets IPropertyConfigurationRoot. The UsePlugin helper takes care of the whole registration: it sets the quartz.plugin.{name}.type property and, when the configuration is backed by Microsoft DI (AddQuartz), registers the plugin type into the container so it gets constructed with constructor injection.
public static class MyPluginConfigurationExtensions
{
public static T UseMyPlugin<T>(this T configurer, Action<MyPluginOptions>? configure = null)
where T : IPropertyConfigurationRoot
{
configurer.UsePlugin<MyPlugin>("myPlugin");
// optional: register companion services your plugin needs injected;
// returns false when there is no container (plain SchedulerBuilder usage)
configurer.TryRegisterSingleton<IMyPluginDependency, MyPluginDependency>();
configure?.Invoke(new MyPluginOptions(configurer));
return configurer;
}
}
Strongly typed options use the PropertiesSetter base class with the plugin's property prefix; each property setter maps to a quartz.plugin.{name}.{property} configuration key that gets applied to the plugin's public setters:
public sealed class MyPluginOptions : PropertiesSetter
{
internal MyPluginOptions(IPropertySetter parent) : base(parent, "quartz.plugin.myPlugin")
{
}
// maps to quartz.plugin.myPlugin.someSetting and MyPlugin.SomeSetting setter
public string SomeSetting
{
set => SetProperty("someSetting", value);
}
}
The same extension method then works with all configuration styles:
// Microsoft DI - plugin is constructed by the container, constructor injection available
services.AddQuartz(q =>
{
q.UseMyPlugin(options =>
{
options.SomeSetting = "value";
});
});
// plain SchedulerBuilder - plugin is created via reflection and needs
// a public parameterless constructor
var scheduler = await SchedulerBuilder.Create()
.UseMyPlugin()
.BuildScheduler();
