Skip to content

feat: add SentrySdk.WithMonitor to run a job with cron check-ins and create the monitor from code - #5664

Open
wedamija wants to merge 5 commits into
mainfrom
danf/crons-with-monitor
Open

wedamija wants to merge 5 commits into
mainfrom
danf/crons-with-monitor

Conversation

@wedamija

@wedamija wedamija commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Description

Adds SentrySdk.WithMonitor, which runs a job and sends its check-ins in one call, like Sentry.withMonitor in JavaScript and @monitor in Python:

await SentrySdk.WithMonitor("nightly-cleanup", () => CleanupAsync(), options =>
{
    options.Interval("0 3 * * *");
});

It sends an in-progress check-in with the optional monitor config, runs the job, then sends ok or error with the duration. Exceptions are rethrown and return values pass through. There are overloads for Action, Func<T>, Func<Task> and Func<Task<T>>, added as extension methods on IHub so existing IHub implementations don't break. Each run gets its own scope and, unless a span is active, its own trace.

A ValueTask<T> job binds to Func<T> and isn't awaited, so the SDK logs a warning; wrap it as async () => await job().

Motivation

Check-ins for a monitor that doesn't exist yet are dropped, so today users create each monitor by hand and pair the in-progress and final check-ins themselves. With the config passed here, Sentry creates the monitor, or updates its schedule, from code.

Testing

HubExtensionsMonitorTests and SentrySdkTests; API approval files updated.

Docs: getsentry/sentry-docs#19779

SentrySdk.WithMonitor and IHub.WithMonitor send an in-progress check-in
(with optional monitor config), run the job, then send an ok or error
check-in with the duration. Exceptions are rethrown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.18310% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.20%. Comparing base (ea7fa3a) to head (a2cf249).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
src/Sentry/SentrySdk.cs 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5664      +/-   ##
==========================================
+ Coverage   74.93%   75.20%   +0.27%     
==========================================
  Files         515      515              
  Lines       18975    19060      +85     
  Branches     3695     3703       +8     
==========================================
+ Hits        14219    14335     +116     
+ Misses       3876     3870       -6     
+ Partials      880      855      -25     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…tured

Hub.CaptureCheckIn returns SentryId.Empty when capturing fails, and
SentryCheckIn only generates an id for null, so the final check-in was
sent with an all-zero id. Also puts one parameter per line in the new
signatures and covers the throwing Func<T> and Func<Task<T>> overloads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wedamija wedamija changed the title feat: add WithMonitor to run a job with cron check-ins feat: add SentrySdk.WithMonitor to run a job with cron check-ins and create the monitor from code Oct 2, 2026
@wedamija
wedamija marked this pull request as ready for review October 2, 2026 19:29
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Oct 2, 2026
@wedamija
wedamija requested a review from bruno-garcia October 2, 2026 23:26
@ric-oliv

ric-oliv commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Hey @wedamija, thanks for the work here! I had a quick look and it's looking pretty good overall.

Before anything else: is there any urgency to bringing this feature in?
We're focused on the version7 release right now, and from a quick review there might be some edge cases (e.g. when a job returns a ValueTask and crashes) that we need to validate and test more closely before bringing this in...

That said, I'd be fine with merging if @bruno-garcia approves it, since he definitely knows best here :)

Either way, I could not find any issue to link this to, except for #3262.
Given its size and spread (hangfire, quartz, docs, ...) we should probably create an issue and bring this into the roadmap.

@bruno-garcia bruno-garcia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick pass

}
catch
{
hub.CaptureCheckIn(monitorSlug, CheckInStatus.Error, checkInId, stopwatch.Elapsed);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to tie the exception we're bubbling here with the check-in?

We do something similar with spans, where we keep a WeakRef of the exception and in the product we can link the two entities.

Otherwise, is this check-in error linked to the actual exception that had it fail?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check-in carries the current trace ID, which should be shared with the given exception, afaik

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I was wrong here, I've pushed an update to fix this:

Each WithMonitor run now gets its own scope, and a new trace when no span or transaction is active. Check-ins and any exception captured during the run share that trace ID. When there's an active transaction, its trace is used.

}
catch
{
hub.CaptureCheckIn(monitorSlug, CheckInStatus.Error, checkInId, stopwatch.Elapsed);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here regarding the exception reference and linking to failed check-in

Action<SentryMonitorOptions>? configureMonitorOptions = null)
=> hub.RunWithMonitorAsync<object?>(monitorSlug, async () =>
{
await job().ConfigureAwait(false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's up to the caller of WithMonitor to handle errors and capture the exception with Sentry?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception is typically caught by the apps unhanded exception setup

Makes sense for the app to handle since it's app error.. but we need to make sure the SDK is linking things correctly

@bruno-garcia

Copy link
Copy Markdown
Member

That said, I'd be fine with merging if @bruno-garcia approves it, since he definitely knows best here :)

@ric-oliv I haven't been too involved with .NET for a while, so it's really your call

@wedamija

wedamija commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Hey @wedamija, thanks for the work here! I had a quick look and it's looking pretty good overall.

Before anything else: is there any urgency to bringing this feature in? We're focused on the version7 release right now, and from a quick review there might be some edge cases (e.g. when a job returns a ValueTask and crashes) that we need to validate and test more closely before bringing this in...

That said, I'd be fine with merging if @bruno-garcia approves it, since he definitely knows best here :)

Either way, I could not find any issue to link this to, except for #3262. Given its size and spread (hangfire, quartz, docs, ...) we should probably create a issue and bring this into the roadmap.

Hey @ric-oliv, my main motivation for this is that we've seen a pretty big bump in crons usage recently in sdks that make it easy to set up crons, and I'm hoping to get this kind of support into most SDKs.

Not sure if you saw, but I also have Hangfire support here: #5665, but I wanted to get this initial work merged first before I make it ready for review

I'm happy to work towards getting this into a good state. I wouldn't call it extremely urgent, but I think it's worthwhile to do and get out sooner rather than later, and I'm happy to put in the work to finish it up if you don't mind reviewing.

wedamija and others added 2 commits October 5, 2026 12:00
A job returning ValueTask binds to WithMonitor<T>(Func<T>), so ok was sent
as soon as the job returned. That path now awaits a plain ValueTask once
and sends ok or error when it completes. A ValueTask<T>, or a Task that
reaches Func<T> through an explicit type argument, still sends ok on
return and logs a warning pointing at 'async () => await job()'.

Tests pin which overload each job shape binds to.

Co-Authored-By: Claude <noreply@anthropic.com>
Like sentry-java's CheckInUtils, each run pushes a scope and, unless a
span is active, starts a new trace, so a run's check-ins and the errors
captured during it share a trace ID. The scope stays current across
awaits, including for ValueTask jobs. Also reject a null job or empty
slug before sending any check-in.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added risk: high PR risk score: high and removed risk: medium PR risk score: medium labels Oct 5, 2026
The final check-in reused the ID returned for the in-progress one, which
is empty when that one isn't captured. Create the ID up front and send
it with both check-ins.

Co-Authored-By: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants