Skip to content

Commit 65ef04c

Browse files
authored
Add Sentry::Cron::MonitorCheckIns module for automatic monitoring of jobs (#2130)
Standard job frameworks such as `ActiveJob` and `Sidekiq` can now use this module to automatically capture check ins. ```rb class ExampleJob < ApplicationJob include Sentry::Cron::MonitorCheckIns sentry_monitor_check_ins def perform(*args) # do stuff end end ``` ```rb class SidekiqJob include Sidekiq::Job include Sentry::Cron::MonitorCheckIns sentry_monitor_check_ins def perform(*args) # do stuff end end ``` You can pass in optional attributes to `sentry_monitor_check_ins` as follows. ```rb sentry_monitor_check_ins slug: 'custom_slug' sentry_monitor_check_ins monitor_config: Sentry::Cron::MonitorConfig.from_interval(1, :minute) sentry_monitor_check_ins monitor_config: Sentry::Cron::MonitorConfig.from_crontab('5 * * * *') ```
1 parent 3d0ed07 commit 65ef04c

File tree

7 files changed

+484
-8
lines changed

7 files changed

+484
-8
lines changed

CHANGELOG.md

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,56 @@
44

55
- Record client reports for profiles [#2107](https://github.com/getsentry/sentry-ruby/pull/2107)
66
- Adopt Rails 7.1's new BroadcastLogger [#2120](https://github.com/getsentry/sentry-ruby/pull/2120)
7-
- Add `Sentry.capture_check_in` API for Cron Monitoring [#2117](https://github.com/getsentry/sentry-ruby/pull/2117)
7+
- Add [Cron Monitoring](https://docs.sentry.io/product/crons/) support
8+
- Add `Sentry.capture_check_in` API for Cron Monitoring [#2117](https://github.com/getsentry/sentry-ruby/pull/2117)
89

9-
You can now track progress of long running scheduled jobs.
10+
You can now track progress of long running scheduled jobs.
1011

11-
```rb
12-
check_in_id = Sentry.capture_check_in('job_name', :in_progress)
13-
# do job stuff
14-
Sentry.capture_check_in('job_name', :ok, check_in_id: check_in_id)
15-
```
12+
```rb
13+
check_in_id = Sentry.capture_check_in('job_name', :in_progress)
14+
# do job stuff
15+
Sentry.capture_check_in('job_name', :ok, check_in_id: check_in_id)
16+
```
17+
- Add `Sentry::Cron::MonitorCheckIns` module for automatic monitoring of jobs [#2130](https://github.com/getsentry/sentry-ruby/pull/2130)
18+
19+
Standard job frameworks such as `ActiveJob` and `Sidekiq` can now use this module to automatically capture check ins.
20+
21+
```rb
22+
class ExampleJob < ApplicationJob
23+
include Sentry::Cron::MonitorCheckIns
24+
25+
sentry_monitor_check_ins
26+
27+
def perform(*args)
28+
# do stuff
29+
end
30+
end
31+
```
32+
33+
```rb
34+
class SidekiqJob
35+
include Sidekiq::Job
36+
include Sentry::Cron::MonitorCheckIns
37+
38+
sentry_monitor_check_ins
39+
40+
def perform(*args)
41+
# do stuff
42+
end
43+
end
44+
```
45+
46+
You can pass in optional attributes to `sentry_monitor_check_ins` as follows.
47+
```rb
48+
# slug defaults to the job class name
49+
sentry_monitor_check_ins slug: 'custom_slug'
50+
51+
# define the monitor config with an interval
52+
sentry_monitor_check_ins monitor_config: Sentry::Cron::MonitorConfig.from_interval(1, :minute)
53+
54+
# define the monitor config with a crontab
55+
sentry_monitor_check_ins monitor_config: Sentry::Cron::MonitorConfig.from_crontab('5 * * * *')
56+
```
1657

1758
### Bug Fixes
1859

sentry-rails/spec/sentry/rails/activejob_spec.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ def rescue_callback(error)
5555
end
5656
end
5757

58+
class NormalJobWithCron < NormalJob
59+
include Sentry::Cron::MonitorCheckIns
60+
sentry_monitor_check_ins
61+
end
62+
63+
class FailedJobWithCron < FailedJob
64+
include Sentry::Cron::MonitorCheckIns
65+
sentry_monitor_check_ins slug: "failed_job", monitor_config: Sentry::Cron::MonitorConfig.from_crontab("5 * * * *")
66+
end
67+
68+
5869
RSpec.describe "without Sentry initialized" do
5970
it "runs job" do
6071
expect { FailedJob.perform_now }.to raise_error(FailedJob::TestError)
@@ -311,4 +322,68 @@ def perform(event, hint)
311322
end
312323
end
313324
end
325+
326+
context "with cron monitoring mixin" do
327+
context "normal job" do
328+
it "returns #perform method's return value" do
329+
expect(NormalJobWithCron.perform_now).to eq("foo")
330+
end
331+
332+
it "captures two check ins" do
333+
NormalJobWithCron.perform_now
334+
335+
expect(transport.events.size).to eq(2)
336+
337+
first = transport.events[0]
338+
check_in_id = first.check_in_id
339+
expect(first).to be_a(Sentry::CheckInEvent)
340+
expect(first.to_hash).to include(
341+
type: 'check_in',
342+
check_in_id: check_in_id,
343+
monitor_slug: "NormalJobWithCron",
344+
status: :in_progress
345+
)
346+
347+
second = transport.events[1]
348+
expect(second).to be_a(Sentry::CheckInEvent)
349+
expect(second.to_hash).to include(
350+
:duration,
351+
type: 'check_in',
352+
check_in_id: check_in_id,
353+
monitor_slug: "NormalJobWithCron",
354+
status: :ok
355+
)
356+
end
357+
end
358+
359+
context "failed job" do
360+
it "captures two check ins" do
361+
expect { FailedJobWithCron.perform_now }.to raise_error(FailedJob::TestError)
362+
363+
expect(transport.events.size).to eq(3)
364+
365+
first = transport.events[0]
366+
check_in_id = first.check_in_id
367+
expect(first).to be_a(Sentry::CheckInEvent)
368+
expect(first.to_hash).to include(
369+
type: 'check_in',
370+
check_in_id: check_in_id,
371+
monitor_slug: "failed_job",
372+
status: :in_progress,
373+
monitor_config: { schedule: { type: :crontab, value: "5 * * * *" } }
374+
)
375+
376+
second = transport.events[1]
377+
expect(second).to be_a(Sentry::CheckInEvent)
378+
expect(second.to_hash).to include(
379+
:duration,
380+
type: 'check_in',
381+
check_in_id: check_in_id,
382+
monitor_slug: "failed_job",
383+
status: :error,
384+
monitor_config: { schedule: { type: :crontab, value: "5 * * * *" } }
385+
)
386+
end
387+
end
388+
end
314389
end

sentry-ruby/lib/sentry-ruby.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
require "sentry/hub"
2222
require "sentry/background_worker"
2323
require "sentry/session_flusher"
24+
require "sentry/cron/monitor_check_ins"
2425

2526
[
2627
"sentry/rake",
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module Sentry
2+
module Cron
3+
module MonitorCheckIns
4+
module Patch
5+
def perform(*args)
6+
slug = self.class.sentry_monitor_slug || self.class.name
7+
monitor_config = self.class.sentry_monitor_config
8+
9+
check_in_id = Sentry.capture_check_in(slug,
10+
:in_progress,
11+
monitor_config: monitor_config)
12+
13+
start = Sentry.utc_now.to_i
14+
ret = super
15+
duration = Sentry.utc_now.to_i - start
16+
17+
Sentry.capture_check_in(slug,
18+
:ok,
19+
check_in_id: check_in_id,
20+
duration: duration,
21+
monitor_config: monitor_config)
22+
23+
ret
24+
rescue Exception
25+
duration = Sentry.utc_now.to_i - start
26+
27+
Sentry.capture_check_in(slug,
28+
:error,
29+
check_in_id: check_in_id,
30+
duration: duration,
31+
monitor_config: monitor_config)
32+
33+
raise
34+
end
35+
end
36+
37+
module ClassMethods
38+
def sentry_monitor_check_ins(slug: nil, monitor_config: nil)
39+
@sentry_monitor_slug = slug
40+
@sentry_monitor_config = monitor_config
41+
42+
prepend Patch
43+
end
44+
45+
def sentry_monitor_slug
46+
@sentry_monitor_slug
47+
end
48+
49+
def sentry_monitor_config
50+
@sentry_monitor_config
51+
end
52+
end
53+
54+
extend ClassMethods
55+
56+
def self.included(base)
57+
base.extend(ClassMethods)
58+
end
59+
end
60+
end
61+
end

0 commit comments

Comments
 (0)