From d86397f99c118585e128fa33a8605cc9428bf7f7 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Fri, 2 Oct 2026 17:25:15 -0700 Subject: [PATCH 01/14] docs(dotnet): Add Quartz.NET crons page Document the new Sentry.Quartz package: registering SentryJobListener, monitor slugs, choosing jobs with matchers, and which trigger schedules are sent as monitor config. Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/index.mdx | 2 +- .../dotnet/common/crons/quartz/index.mdx | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 docs/platforms/dotnet/common/crons/quartz/index.mdx 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..14cddd5c3b6984 --- /dev/null +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -0,0 +1,77 @@ +--- +title: Quartz.NET +description: "Learn how to monitor your Quartz.NET jobs." +sidebar_order: 5002 +--- + +The `Sentry.Quartz` package monitors your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). It's available from the next SDK release and supports Quartz.NET 3.6 and later 3.x versions. + +## Install + +```shell {tabTitle:.NET Core CLI} +dotnet add package Sentry.Quartz +``` + +```powershell {tabTitle:Package Manager} +Install-Package Sentry.Quartz +``` + +## Configure + +Initialize Sentry as usual, then add `SentryJobListener` to your scheduler. If you register Quartz.NET with dependency injection: + +```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()); +``` + +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, along with how long the job ran. + +## Monitor Slugs + +The monitor slug is the job's key in lowercase, with any other characters replaced by a hyphen. Jobs in the default group use only the job name. For example, the job `billing.PricingUpdate` reports to `billing-pricingupdate`, and the job `Cleanup` in the default group reports to `cleanup`. + +To use a different slug, set `SentryJobListener.MonitorSlugKey` in the job's data map: + +```csharp +var job = JobBuilder.Create() + .WithIdentity("PricingUpdate", "billing") + .UsingJobData(SentryJobListener.MonitorSlugKey, "update-pricing") + .Build(); +``` + +## Choose Which Jobs to Monitor + +The listener monitors every job. To monitor only some jobs, pass Quartz.NET matchers when you add it: + +```csharp +using Quartz.Impl.Matchers; + +q.AddJobListener(GroupMatcher.GroupEquals("billing")); +``` + +## Monitor Schedule + +Sentry only creates a monitor from a check-in that includes a schedule. The SDK sends the schedule of the trigger that started the job: + +- Cron triggers, with the trigger's time zone. The cron expression must use a fixed number of seconds and no year, `L`, `W`, or `#`. +- 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. Jobs with more than one trigger are also sent without one, unless each trigger sets its own `SentryJobListener.MonitorSlugKey` in its 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: + +```csharp +q.AddJobListener(_ => new SentryJobListener(new SentryQuartzOptions { SendMonitorConfig = false })); +``` From 019b92e64d640482476d6ed2960919a8508fb3ef Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Mon, 5 Oct 2026 12:18:06 -0700 Subject: [PATCH 02/14] docs(dotnet): Clarify Quartz.NET version, setup, time zones and slugs Quartz.NET 4.x isn't supported. AddQuartz needs the Quartz.NET DI or hosting package, cron triggers default to the server's local time zone, and spell out how the monitor slug is built. Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 14cddd5c3b6984..a44032673e82f1 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -4,7 +4,7 @@ description: "Learn how to monitor your Quartz.NET jobs." sidebar_order: 5002 --- -The `Sentry.Quartz` package monitors your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). It's available from the next SDK release and supports Quartz.NET 3.6 and later 3.x versions. +The `Sentry.Quartz` package monitors your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). It's available from the next SDK release and supports Quartz.NET 3.6 and later 3.x versions. Quartz.NET 4.x isn't supported. ## Install @@ -18,7 +18,7 @@ Install-Package Sentry.Quartz ## Configure -Initialize Sentry as usual, then add `SentryJobListener` to your scheduler. If you register Quartz.NET with dependency injection: +Initialize Sentry as usual, then 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; @@ -40,7 +40,7 @@ The SDK captures a check-in with `CheckInStatus.InProgress` when a job starts. W ## Monitor Slugs -The monitor slug is the job's key in lowercase, with any other characters replaced by a hyphen. Jobs in the default group use only the job name. For example, the job `billing.PricingUpdate` reports to `billing-pricingupdate`, and the job `Cleanup` in the default group reports to `cleanup`. +The monitor slug is the job's key, `group.name`, or only the name for jobs in the default group. The SDK lowercases it and keeps ASCII letters, digits, and underscores. Each run of other characters becomes a single hyphen, leading and trailing hyphens and underscores are removed, and the slug is 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. To use a different slug, set `SentryJobListener.MonitorSlugKey` in the job's data map: @@ -65,7 +65,7 @@ q.AddJobListener(GroupMatcher.GroupEquals("billing")) Sentry only creates a monitor from a check-in that includes a schedule. The SDK sends the schedule of the trigger that started the job: -- Cron triggers, with the trigger's time zone. The cron expression must use a fixed number of seconds and no year, `L`, `W`, or `#`. +- 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 `#`. - 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. Jobs with more than one trigger are also sent without one, unless each trigger sets its own `SentryJobListener.MonitorSlugKey` in its data map. From 6a8e1a77ee4b5772acf4112c9cddba106aec965e Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Mon, 5 Oct 2026 12:58:17 -0700 Subject: [PATCH 03/14] docs(dotnet): Cover Quartz.NET 4.x with Sentry.Quartz4 Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index a44032673e82f1..617074fbcecf0c 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -4,7 +4,7 @@ description: "Learn how to monitor your Quartz.NET jobs." sidebar_order: 5002 --- -The `Sentry.Quartz` package monitors your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). It's available from the next SDK release and supports Quartz.NET 3.6 and later 3.x versions. Quartz.NET 4.x isn't supported. +The `Sentry.Quartz` package monitors your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). It's available from the next SDK release. Use `Sentry.Quartz` with Quartz.NET 3.x (3.6 and later) and `Sentry.Quartz4` with Quartz.NET 4.x. ## Install @@ -16,6 +16,8 @@ dotnet add package Sentry.Quartz Install-Package Sentry.Quartz ``` +For Quartz.NET 4.x, install `Sentry.Quartz4` instead. Everything else on this page is the same for both packages. + ## Configure Initialize Sentry as usual, then 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: From 5b748be206b4d15249d81880a13499c541a16867 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Mon, 5 Oct 2026 12:58:48 -0700 Subject: [PATCH 04/14] docs(dotnet): Note where GroupMatcher lives in Quartz.NET 4.x Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 617074fbcecf0c..8aa1b5b8a9c063 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -16,7 +16,7 @@ dotnet add package Sentry.Quartz Install-Package Sentry.Quartz ``` -For Quartz.NET 4.x, install `Sentry.Quartz4` instead. Everything else on this page is the same for both packages. +For Quartz.NET 4.x, install `Sentry.Quartz4` instead. The setup is the same. ## Configure @@ -58,7 +58,7 @@ var job = JobBuilder.Create() The listener monitors every job. To monitor only some jobs, pass Quartz.NET matchers when you add it: ```csharp -using Quartz.Impl.Matchers; +using Quartz.Impl.Matchers; // Quartz.NET 3.x only; 4.x has GroupMatcher in Quartz q.AddJobListener(GroupMatcher.GroupEquals("billing")); ``` From 2bfc69c7574bd18da8dcb80820a3325d6424ee6a Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Mon, 5 Oct 2026 13:13:21 -0700 Subject: [PATCH 05/14] docs(dotnet): Note that Sentry.Quartz logs an error on Quartz.NET 4.x Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 8aa1b5b8a9c063..1c9ba313a1b580 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -16,7 +16,7 @@ dotnet add package Sentry.Quartz Install-Package Sentry.Quartz ``` -For Quartz.NET 4.x, install `Sentry.Quartz4` instead. The setup is the same. +For Quartz.NET 4.x, install `Sentry.Quartz4` instead. The setup is the same. With Quartz.NET 4.x, `Sentry.Quartz` sends no check-ins and logs an error in debug mode. ## Configure From f9c415d5de4b1d00974e60873997d92a044779e9 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Mon, 5 Oct 2026 19:06:06 -0700 Subject: [PATCH 06/14] docs(dotnet): Cover Sentry.Quartz4 and Sentry.Quartz3 Co-Authored-By: Claude --- .../dotnet/common/crons/quartz/index.mdx | 94 +++++++++++++++---- 1 file changed, 74 insertions(+), 20 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 1c9ba313a1b580..578b7ea57fa16f 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -4,23 +4,43 @@ description: "Learn how to monitor your Quartz.NET jobs." sidebar_order: 5002 --- -The `Sentry.Quartz` package monitors your [Quartz.NET](https://www.quartz-scheduler.net/) jobs by [creating check-ins for them](/product/monitors-and-alerts/monitors/crons/job-monitoring/). It's available from the next SDK release. Use `Sentry.Quartz` with Quartz.NET 3.x (3.6 and later) and `Sentry.Quartz4` with Quartz.NET 4.x. +The `Sentry.Quartz4` 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.Quartz4` 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 +dotnet add package Sentry.Quartz4 ``` ```powershell {tabTitle:Package Manager} -Install-Package Sentry.Quartz +Install-Package Sentry.Quartz4 ``` -For Quartz.NET 4.x, install `Sentry.Quartz4` instead. The setup is the same. With Quartz.NET 4.x, `Sentry.Quartz` sends no check-ins and logs an error in debug mode. +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 `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: +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 exceptions it throws are captured 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; @@ -38,42 +58,76 @@ Or add the listener to a scheduler directly: scheduler.ListenerManager.AddJobListener(new SentryJobListener()); ``` +## Choose Which Jobs to Monitor + +Add the `SentryCronMonitorSlug` attribute to each job class you want to monitor. Jobs without it aren't monitored. + +```csharp +[SentryCronMonitorSlug] +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, along with how long the job ran. ## Monitor Slugs -The monitor slug is the job's key, `group.name`, or only the name for jobs in the default group. The SDK lowercases it and keeps ASCII letters, digits, and underscores. Each run of other characters becomes a single hyphen, leading and trailing hyphens and underscores are removed, and the slug is 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. - -To use a different slug, set `SentryJobListener.MonitorSlugKey` in the job's data map: +To choose the slug, pass it to the attribute: ```csharp -var job = JobBuilder.Create() - .WithIdentity("PricingUpdate", "billing") - .UsingJobData(SentryJobListener.MonitorSlugKey, "update-pricing") - .Build(); +[SentryCronMonitorSlug("update-pricing")] +public class PricingUpdateJob : IJob ``` -## Choose Which Jobs to Monitor +Without one, the slug is the job's key, `group.name`, or only the name for jobs in the default group. The SDK lowercases it and keeps ASCII letters, digits, and underscores. Each run of other characters becomes a single hyphen, leading and trailing hyphens and underscores are removed, and the slug is 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. -The listener monitors every job. To monitor only some jobs, pass Quartz.NET matchers when you add it: +To use a different slug for one job or trigger, set `SentryCronMonitorSlugAttribute.JobDataKey` in its data map: ```csharp -using Quartz.Impl.Matchers; // Quartz.NET 3.x only; 4.x has GroupMatcher in Quartz - -q.AddJobListener(GroupMatcher.GroupEquals("billing")); +var job = JobBuilder.Create() + .WithIdentity("PricingUpdate", "billing") + .UsingJobData(SentryCronMonitorSlugAttribute.JobDataKey, "update-pricing") + .Build(); ``` ## Monitor Schedule -Sentry only creates a monitor from a check-in that includes a schedule. The SDK sends the schedule of the trigger that started the job: +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 `#`. - 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. Jobs with more than one trigger are also sent without one, unless each trigger sets its own `SentryJobListener.MonitorSlugKey` in its data map. +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. Jobs with more than one trigger are also sent without one, unless each trigger sets its own slug in its 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: +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 +[SentryCronMonitorSlug(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 })); ``` + +## Monitor Settings + +To set other monitor settings, such as the thresholds or the maximum runtime, use `ConfigureMonitorOptions`. It runs before the trigger's schedule is applied, so a schedule or time zone you set here is used instead: + +```csharp +q.AddSentry(options => options.ConfigureMonitorOptions = (jobDetail, monitorOptions) => +{ + monitorOptions.MaxRuntime = TimeSpan.FromMinutes(30); + monitorOptions.FailureIssueThreshold = 3; +}); +``` + +With Quartz.NET 3.x, set it on the `SentryQuartzOptions` you pass to `SentryJobListener`. From 3ade61e6607f5c221d8f98495a642527815a1aa2 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Tue, 6 Oct 2026 10:03:58 -0700 Subject: [PATCH 07/14] docs(dotnet): Rename Sentry.Quartz4 to Sentry.Quartz Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 578b7ea57fa16f..b5efe48c90a1fc 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -4,16 +4,16 @@ description: "Learn how to monitor your Quartz.NET jobs." sidebar_order: 5002 --- -The `Sentry.Quartz4` 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.Quartz4` with Quartz.NET 4.x and `Sentry.Quartz3` with Quartz.NET 3.x (3.6 and later). +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.Quartz4 +dotnet add package Sentry.Quartz ``` ```powershell {tabTitle:Package Manager} -Install-Package Sentry.Quartz4 +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. From cd4e826aadc44e20bb964746a88fc37f4dd41548 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Thu, 8 Oct 2026 13:58:55 -0700 Subject: [PATCH 08/14] docs(dotnet): Update Quartz.NET crons for the SDK review changes Fix the check-in status names and note that cancelled jobs report an error, that the integration no longer captures job exceptions, that jobs without an identity fall back to their class name with a warning, the new ConfigureMonitorOptions signature, and Quartz.NET 4.x day fields. Co-Authored-By: Claude --- .../platforms/dotnet/common/crons/quartz/index.mdx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index b5efe48c90a1fc..4a5367f165500e 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -36,7 +36,7 @@ builder.Services.AddQuartz(q => }); ``` -Each job then runs in its own Sentry scope and trace, tagged with `quartz.job`, and exceptions it throws are captured in that scope. +Each job then runs in its own Sentry scope and trace, tagged with `quartz.job`. ### Quartz.NET 3.x @@ -70,7 +70,9 @@ 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, along with how long the job ran. +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. + +The SDK doesn't capture the exceptions jobs throw. Quartz.NET logs them as errors, and Sentry's logging integration captures that log entry, for example when you use `Sentry.AspNetCore` or `Sentry.Extensions.Logging`. ## Monitor Slugs @@ -83,6 +85,8 @@ public class PricingUpdateJob : IJob Without one, the slug is the job's key, `group.name`, or only the name for jobs in the default group. The SDK lowercases it and keeps ASCII letters, digits, and underscores. Each run of other characters becomes a single hyphen, leading and trailing hyphens and underscores are removed, and the slug is 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`, and logs a warning. Give the job an identity or a slug to keep its monitor the same. The SDK also logs a warning when two jobs use the same slug. + To use a different slug for one job or trigger, set `SentryCronMonitorSlugAttribute.JobDataKey` in its data map: ```csharp @@ -96,7 +100,7 @@ var job = JobBuilder.Create() 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 `#`. +- 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. Jobs with more than one trigger are also sent without one, unless each trigger sets its own slug in its data map. @@ -120,10 +124,10 @@ q.AddJobListener(_ => new SentryJobListener(new SentryQuartzOptions { SendMonito ## Monitor Settings -To set other monitor settings, such as the thresholds or the maximum runtime, use `ConfigureMonitorOptions`. It runs before the trigger's schedule is applied, so a schedule or time zone you set here is used instead: +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 = (jobDetail, monitorOptions) => +q.AddSentry(options => options.ConfigureMonitorOptions = (context, monitorOptions) => { monitorOptions.MaxRuntime = TimeSpan.FromMinutes(30); monitorOptions.FailureIssueThreshold = 3; From 78ac3b723cd5999590715188e04ff5bd6951c274 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Thu, 8 Oct 2026 15:26:59 -0700 Subject: [PATCH 09/14] docs(dotnet): Note that deleted Quartz monitors are re-created Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 4a5367f165500e..cae2a2352782ea 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -122,6 +122,8 @@ q.AddSentry(options => options.SendMonitorConfig = false); q.AddJobListener(_ => new SentryJobListener(new SentryQuartzOptions { SendMonitorConfig = false })); ``` +If you delete a monitor while its job still has `[SentryCronMonitorSlug]`, 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: From f3b04bd4576a70d3f80f2ad933b571378873cbd0 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Fri, 16 Oct 2026 19:05:36 -0700 Subject: [PATCH 10/14] docs(dotnet): Rename SentryCronMonitorSlug to SentryMonitorSlug Co-Authored-By: Claude --- .../platforms/dotnet/common/crons/quartz/index.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index cae2a2352782ea..6e0b7b63a2d116 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -60,10 +60,10 @@ scheduler.ListenerManager.AddJobListener(new SentryJobListener()); ## Choose Which Jobs to Monitor -Add the `SentryCronMonitorSlug` attribute to each job class you want to monitor. Jobs without it aren't monitored. +Add the `SentryMonitorSlug` attribute to each job class you want to monitor. Jobs without it aren't monitored. ```csharp -[SentryCronMonitorSlug] +[SentryMonitorSlug] public class PricingUpdateJob : IJob { // ... @@ -79,7 +79,7 @@ The SDK doesn't capture the exceptions jobs throw. Quartz.NET logs them as error To choose the slug, pass it to the attribute: ```csharp -[SentryCronMonitorSlug("update-pricing")] +[SentryMonitorSlug("update-pricing")] public class PricingUpdateJob : IJob ``` @@ -87,12 +87,12 @@ Without one, the slug is the job's key, `group.name`, or only the name for jobs 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`, and logs a warning. Give the job an identity or a slug to keep its monitor the same. The SDK also logs a warning when two jobs use the same slug. -To use a different slug for one job or trigger, set `SentryCronMonitorSlugAttribute.JobDataKey` in its data map: +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(SentryCronMonitorSlugAttribute.JobDataKey, "update-pricing") + .UsingJobData(SentryMonitorSlugAttribute.JobDataKey, "update-pricing") .Build(); ``` @@ -108,7 +108,7 @@ Other triggers, triggers with a calendar, and time zones without an IANA ID (Win 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 -[SentryCronMonitorSlug(SendMonitorConfig = false)] +[SentryMonitorSlug(SendMonitorConfig = false)] public class PricingUpdateJob : IJob ``` @@ -122,7 +122,7 @@ q.AddSentry(options => options.SendMonitorConfig = false); q.AddJobListener(_ => new SentryJobListener(new SentryQuartzOptions { SendMonitorConfig = false })); ``` -If you delete a monitor while its job still has `[SentryCronMonitorSlug]`, the next run creates it again. To stop monitoring a job, disable the monitor in Sentry, or remove the attribute. +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 From bbf0ed815af0f4e6c98fe63751e15188c0137884 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Tue, 20 Oct 2026 16:30:41 -0700 Subject: [PATCH 11/14] docs(dotnet): Update Quartz.NET exception capture and slug logging Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 6e0b7b63a2d116..f77a55a5a9a6bb 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -36,7 +36,7 @@ builder.Services.AddQuartz(q => }); ``` -Each job then runs in its own Sentry scope and trace, tagged with `quartz.job`. +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 @@ -72,7 +72,7 @@ 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. -The SDK doesn't capture the exceptions jobs throw. Quartz.NET logs them as errors, and Sentry's logging integration captures that log entry, for example when you use `Sentry.AspNetCore` or `Sentry.Extensions.Logging`. +With Quartz.NET 4.x, the SDK captures the exceptions jobs throw in the job's scope, so they're tagged with `quartz.job` and share a trace with the check-in. With Quartz.NET 3.x it doesn't: Quartz.NET logs them as errors, and Sentry's logging integration captures that log entry, for example when you use `Sentry.AspNetCore` or `Sentry.Extensions.Logging`. ## Monitor Slugs @@ -85,7 +85,7 @@ public class PricingUpdateJob : IJob Without one, the slug is the job's key, `group.name`, or only the name for jobs in the default group. The SDK lowercases it and keeps ASCII letters, digits, and underscores. Each run of other characters becomes a single hyphen, leading and trailing hyphens and underscores are removed, and the slug is 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`, and logs a warning. Give the job an identity or a slug to keep its monitor the same. The SDK also logs a warning when two jobs use the same 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: From 9df04b75f2d992ab363507f0f025970140ef26b5 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 21 Oct 2026 12:41:40 +1300 Subject: [PATCH 12/14] docs(dotnet): Say shared-slug triggers lose the schedule only when it differs Matches getsentry/sentry-dotnet@787ab1e, which replaced the several-triggers check. Co-Authored-By: Claude Opus 5.5 --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index f77a55a5a9a6bb..77e123978ce52b 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -103,7 +103,7 @@ Sentry only creates a monitor from a check-in that includes a schedule. For moni - 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. Jobs with more than one trigger are also sent without one, unless each trigger sets its own slug in its data map. +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: From ce044fc8a8e00dacd05d2a34171d7cd476d7921a Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Wed, 21 Oct 2026 09:37:18 -0700 Subject: [PATCH 13/14] docs(dotnet): Say Sentry.Quartz captures each job exception once Co-Authored-By: Claude --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 77e123978ce52b..14c6b7a3ca6197 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -72,7 +72,7 @@ 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 the exceptions jobs throw in the job's scope, so they're tagged with `quartz.job` and share a trace with the check-in. With Quartz.NET 3.x it doesn't: Quartz.NET logs them as errors, and Sentry's logging integration captures that log entry, for example when you use `Sentry.AspNetCore` or `Sentry.Extensions.Logging`. +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 From e2cf93493fe56f9ce8115b33f324968e2ebc96d2 Mon Sep 17 00:00:00 2001 From: Dan Fuller Date: Thu, 22 Oct 2026 09:55:16 -0700 Subject: [PATCH 14/14] docs(dotnet): Match Quartz slug rule to shared normalization --- docs/platforms/dotnet/common/crons/quartz/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/platforms/dotnet/common/crons/quartz/index.mdx b/docs/platforms/dotnet/common/crons/quartz/index.mdx index 14c6b7a3ca6197..8b531637fe49de 100644 --- a/docs/platforms/dotnet/common/crons/quartz/index.mdx +++ b/docs/platforms/dotnet/common/crons/quartz/index.mdx @@ -83,7 +83,7 @@ To choose the slug, pass it to the attribute: public class PricingUpdateJob : IJob ``` -Without one, the slug is the job's key, `group.name`, or only the name for jobs in the default group. The SDK lowercases it and keeps ASCII letters, digits, and underscores. Each run of other characters becomes a single hyphen, leading and trailing hyphens and underscores are removed, and the slug is 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. +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.