diff --git a/docs/platforms/dotnet/common/crons/index.mdx b/docs/platforms/dotnet/common/crons/index.mdx index da4e734feb6357..938a28eb809c85 100644 --- a/docs/platforms/dotnet/common/crons/index.mdx +++ b/docs/platforms/dotnet/common/crons/index.mdx @@ -10,7 +10,7 @@ Once implemented, it'll allow you to get alerts and metrics to help you solve er -If you are using [Hangfire](https://www.hangfire.io/), please see how to setup monitoring for Hangfire jobs. +If you are using [Hangfire](https://www.hangfire.io/), please see how to setup monitoring for Hangfire jobs. If you are using [Quartz.NET](https://www.quartz-scheduler.net/), see how to set up monitoring for Quartz.NET jobs. diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx new file mode 100644 index 00000000000000..8b531637fe49de --- /dev/null +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -0,0 +1,139 @@ +--- +title: Quartz.NET +description: "Learn how to monitor your Quartz.NET jobs." +sidebar_order: 5002 +--- + +The `Sentry.Quartz` and `Sentry.Quartz3` packages monitor your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). They're available from the next SDK release. Use `Sentry.Quartz` with Quartz.NET 4.x and `Sentry.Quartz3` with Quartz.NET 3.x (3.6 and later). + +## Install + +```shell {tabTitle:.NET Core CLI} +dotnet add package Sentry.Quartz +``` + +```powershell {tabTitle:Package Manager} +Install-Package Sentry.Quartz +``` + +For Quartz.NET 3.x, install `Sentry.Quartz3` instead. With Quartz.NET 4.x, `Sentry.Quartz3` sends no check-ins and logs an error in debug mode. + +## Configure + +Initialize Sentry as usual, then add Sentry to your scheduler. + +### Quartz.NET 4.x + +Call `AddSentry` when you configure Quartz.NET: + +```csharp +using Quartz; +using Sentry.Quartz; + +builder.Services.AddQuartz(q => +{ + q.AddSentry(); +}); +``` + +Each job then runs in its own Sentry scope and trace, tagged with `quartz.job`, and the SDK captures the exceptions jobs throw in that scope. + +### Quartz.NET 3.x + +Add `SentryJobListener` to your scheduler. If you register Quartz.NET with dependency injection, `AddQuartz` comes from the `Quartz.Extensions.DependencyInjection` package, or from `Quartz.Extensions.Hosting` if you also run the scheduler as a hosted service: + +```csharp +using Quartz; +using Sentry.Quartz; + +builder.Services.AddQuartz(q => +{ + q.AddJobListener(); +}); +``` + +Or add the listener to a scheduler directly: + +```csharp +scheduler.ListenerManager.AddJobListener(new SentryJobListener()); +``` + +## Choose Which Jobs to Monitor + +Add the `SentryMonitorSlug` attribute to each job class you want to monitor. Jobs without it aren't monitored. + +```csharp +[SentryMonitorSlug] +public class PricingUpdateJob : IJob +{ + // ... +} +``` + +The SDK captures a check-in with `CheckInStatus.InProgress` when a job starts. When the job finishes, it updates the check-in to `CheckInStatus.Ok`, or to `CheckInStatus.Error` if the job threw an exception or was cancelled, along with how long the job ran. + +With Quartz.NET 4.x, the SDK captures each exception a job throws once, in the job's scope, so it's tagged with `quartz.job` and shares a trace with the check-in. With Quartz.NET 3.x it doesn't: Quartz.NET logs the exceptions as errors, and Sentry's logging integration captures that log entry, for example when you use `Sentry.AspNetCore` or `Sentry.Extensions.Logging`. + +## Monitor Slugs + +To choose the slug, pass it to the attribute: + +```csharp +[SentryMonitorSlug("update-pricing")] +public class PricingUpdateJob : IJob +``` + +Without one, the slug is the job's group and name joined by `-`, or only the name for jobs in the default group, normalized the same way Sentry normalizes monitor slugs: lowercased, accents removed, spaces and dashes replaced by a single `-`, other characters dropped, and cut to 50 characters. For example, the job `billing.PricingUpdate` reports to `billing-pricingupdate`, `reports.Daily_Email (v2)` reports to `reports-daily_email-v2`, and the job `Cleanup` in the default group reports to `cleanup`. A job whose key leaves nothing is not monitored unless you set a slug. + +A job built without `WithIdentity` gets a new random name each time it's created, so the SDK uses the name of the job's class instead, such as `pricingupdatejob`. Give the job an identity or a slug to keep its monitor the same. If triggers that share a slug have different schedules, the SDK doesn't send a schedule for them. + +To use a different slug for one job or trigger, set `SentryMonitorSlugAttribute.JobDataKey` in its data map: + +```csharp +var job = JobBuilder.Create() + .WithIdentity("PricingUpdate", "billing") + .UsingJobData(SentryMonitorSlugAttribute.JobDataKey, "update-pricing") + .Build(); +``` + +## Monitor Schedule + +Sentry only creates a monitor from a check-in that includes a schedule. For monitored jobs, the SDK sends the schedule of the trigger that started the job: + +- Cron triggers, with the trigger's time zone. Quartz.NET cron triggers use the server's local time zone unless you set one with `InTimeZone`, so set it if your servers don't all use the same time zone. The cron expression must use a fixed number of seconds and no year, `L`, `W`, or `#`. Quartz.NET 3.x needs `?` in exactly one of the two day fields. Quartz.NET 4.x also accepts `*` or values in both. When both have values, the job runs on days that match either field, and Sentry reads the schedule the same way. +- Simple triggers that repeat forever every whole number of minutes, hours, or days. + +Other triggers, triggers with a calendar, and time zones without an IANA ID (Windows time zone IDs on .NET Framework) are sent without a schedule. Triggers that share a slug are also sent without one if their schedules differ, for example two triggers of the same job. To give each trigger its own monitor, set a different slug in each trigger's data map. + +Sentry sets the monitor's schedule and time zone from every check-in that includes them, so changes you make to those two settings in Sentry are overwritten. Other monitor settings are kept. To stop sending the schedule for one job, set `SendMonitorConfig` on its attribute: + +```csharp +[SentryMonitorSlug(SendMonitorConfig = false)] +public class PricingUpdateJob : IJob +``` + +To stop sending it for all jobs, set `SendMonitorConfig` in the options: + +```csharp {tabTitle:Quartz.NET 4.x} +q.AddSentry(options => options.SendMonitorConfig = false); +``` + +```csharp {tabTitle:Quartz.NET 3.x} +q.AddJobListener(_ => new SentryJobListener(new SentryQuartzOptions { SendMonitorConfig = false })); +``` + +If you delete a monitor while its job still has `[SentryMonitorSlug]`, the next run creates it again. To stop monitoring a job, disable the monitor in Sentry, or remove the attribute. + +## Monitor Settings + +To set other monitor settings, such as the thresholds or the maximum runtime, use `ConfigureMonitorOptions`. It runs before each in-progress check-in with the job's `IJobExecutionContext`, unless `SendMonitorConfig` is off. A schedule or time zone you set here is used instead of the trigger's. Sentry only receives these settings along with a schedule, from the trigger or set here, so you can also use it to set the schedule of a trigger the SDK can't send: + +```csharp +q.AddSentry(options => options.ConfigureMonitorOptions = (context, monitorOptions) => +{ + monitorOptions.MaxRuntime = TimeSpan.FromMinutes(30); + monitorOptions.FailureIssueThreshold = 3; +}); +``` + +With Quartz.NET 3.x, set it on the `SentryQuartzOptions` you pass to `SentryJobListener`.