sentry-good_job 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fd17c2d28618ace1fe96fd34f6f39035c7a72db021555dcbd7239a727603f340
4
+ data.tar.gz: 784f07f7a32ec531bb35327f55b17ebe303b25095af640f8016373547810719f
5
+ SHA512:
6
+ metadata.gz: 2ef631f7d3df7cc2de495e64ecb325980ae95b3809f208fcabcbf276529b3c3c1e0fe04660d6f7aae08b898df6d7fc73f40176e8b381ccf690cfa5868a61d289
7
+ data.tar.gz: c349ca2697b3435f30469d0ee21c730fec186931017eb94400bff9fa2795331a2845ed66eae82142fa7e7953b5920fc4b116d732fd531f0f23bc740038dcaf93
data/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## 6.2.0
4
+
5
+ ### Features
6
+
7
+ - Initial release of sentry-good_job integration
8
+ - Automatic error capture for ActiveJob workers using Good Job
9
+ - Performance monitoring for job execution
10
+ - Automatic cron monitoring setup for scheduled jobs
11
+ - Context preservation and trace propagation
12
+ - Configurable error reporting options
13
+ - Rails integration with automatic setup
14
+
15
+ ### Configuration Options
16
+
17
+ #### Good Job Specific Options
18
+ - `enable_cron_monitors`: Enable cron monitoring for scheduled jobs
19
+
20
+ #### ActiveJob Options (handled by sentry-rails)
21
+ - `config.rails.active_job_report_on_retry_error`: Only report errors after all retry attempts are exhausted
22
+ - `config.send_default_pii`: Include job arguments in error context
23
+
24
+ **Note**: The Good Job integration now leverages sentry-rails for core ActiveJob functionality, including trace propagation, user context preservation, and error reporting.
25
+
26
+ ### Integration Features
27
+
28
+ - Seamless integration with Rails applications
29
+ - Automatic setup when Good Job integration is enabled
30
+ - Support for both manual and automatic cron monitoring
31
+ - Respects ActiveJob retry configuration
32
+ - Comprehensive error context and performance metrics
@@ -0,0 +1,37 @@
1
+ # Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We pledge to make participation in our community a harassment-free experience for everyone. We operate on principles of mutual respect, privacy, and authentic engagement. We value substantive contributions and clarity on intentions.
6
+
7
+ ## Our Standards
8
+
9
+ Examples of behavior that contributes to a positive environment include:
10
+
11
+ * **Authenticity:** Engaging with genuine curiosity and admitting uncertainty rather than feigning knowledge.
12
+
13
+ * **Responsible Innovation:** Taking full responsibility for any content or code contributed, whether manually written or generated by automation tools.
14
+
15
+ * **Gentle Correction:** Responding politely to errors. We view mistakes as opportunities for learning, provided they are addressed with humility.
16
+
17
+ * **Inclusive Language:** Using language that welcomes diverse perspectives and respects the privacy and identity of all participants.
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * **Harassment:** Public or private harassment, trolling, or insulting comments.
22
+
23
+ * **Weaponized Complexity:** Using jargon or overwhelming volume (including automated spam) to silence others.
24
+
25
+ * **Publishing Private Information:** Sharing others' data or personal context without explicit permission.
26
+
27
+ ## Artificial Intelligence & Automation
28
+
29
+ In accordance with our commitment to **Collective Awareness**:
30
+
31
+ * Contributors are responsible for the accuracy and security of any AI-generated artifacts they submit.
32
+
33
+ * "The AI wrote it" is not a valid excuse for introducing bugs, security vulnerabilities, or bias.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@kiskolabs.com. All complaints will be reviewed and investigated promptly and fairly.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,35 @@
1
+ # Contributing Guidelines
2
+
3
+ Thank you for your interest in contributing. We value **learning over perfection** but require **rigor and responsibility**.
4
+
5
+ ## The Golden Rule of Automation
6
+
7
+ We welcome the use of AI and automation tools to reduce toil, but you must strictly adhere to the following:
8
+
9
+ 1. **You are the Author:** You act as the responsible agent for any code you submit. You must review, debug, and understand every line.
10
+
11
+ 2. **Manage Cognitive Load:** Do not submit massive, unreviewed automated dumps. Respect the reviewers' time by annotating complex logic.
12
+
13
+ 3. **Security:** Never feed project secrets or private context into public AI models.
14
+
15
+ ## How to Contribute
16
+
17
+ ### 1. Reporting Issues
18
+
19
+ * **Verify Accuracy:** Before posting, verify your information. Avoid generalizations.
20
+
21
+ * **Use Structured Inputs:** Use our Issue Templates to provide clear goals, constraints, and reproduction steps. This helps us understand the context immediately.
22
+
23
+ ### 2. Pull Request Process
24
+
25
+ * **Scope:** Keep PRs focused on a single goal.
26
+
27
+ * **Context:** Explain *why* the change is necessary. Transparency builds trust.
28
+
29
+ * **Testing:** Run all smoke tests and regression checks locally. We prioritize "Safety First."
30
+
31
+ ### 3. Review Process
32
+
33
+ * We encourage **productive friction**. Expect questions about your approach.
34
+
35
+ * If a reviewer suggests a change, view it as **mutual aid**, not criticism.
data/GOVERNANCE.md ADDED
@@ -0,0 +1,31 @@
1
+ # Project Governance
2
+
3
+ ## Overview
4
+
5
+ This project balances **individual autonomy** with **collective coordination**. We aim for a workflow that supports rapid iteration while maintaining strict guardrails for safety and architectural integrity.
6
+
7
+ ## Roles
8
+
9
+ ### Contributors
10
+
11
+ Anyone who submits code, documentation, or participates in discussions. Contributors are expected to exercise **Direct Action**—taking ownership of problems they identify—while adhering to our quality standards.
12
+
13
+ ### Maintainers (Human Oversight)
14
+
15
+ Maintainers are responsible for:
16
+
17
+ 1. **Strategic Judgment:** Defining scope and architectural direction.
18
+
19
+ 2. **Review:** verifying that contributions (human or automated) meet security and logic standards.
20
+
21
+ 3. **Consensus Building:** Facilitating decisions when the community is divided.
22
+
23
+ ## Decision Making process
24
+
25
+ ### Lazy Consensus
26
+
27
+ For most routine changes, we operate on "lazy consensus." If a proposal is made and no objections are raised within two weeks, it is considered approved.
28
+
29
+ ### Strategic Alignment
30
+
31
+ Major architectural changes, high-risk automation integrations, or changes that affect business logic require explicit approval from the Maintainers. We prioritize **substance over performance**—a change must solve a real problem, not just appear polished.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Andrei Makarov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,203 @@
1
+ <p align="center">
2
+ <a href="https://sentry.io" target="_blank" align="center">
3
+ <img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
4
+ </a>
5
+ <br>
6
+ </p>
7
+
8
+ # sentry-good_job, the Good Job integration for Sentry's Ruby client
9
+
10
+ ---
11
+
12
+ [![Gem Version](https://img.shields.io/gem/v/sentry-good_job.svg)](https://rubygems.org/gems/sentry-good_job)
13
+ ![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_good_job_test.yml/badge.svg)
14
+ [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master)
15
+ [![Gem](https://img.shields.io/gem/dt/sentry-good_job.svg)](https://rubygems.org/gems/sentry-good_job/)
16
+ [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=sentry-good_job&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=sentry-good_job&package-manager=bundler&version-scheme=semver)
17
+
18
+ [Documentation](https://docs.sentry.io/platforms/ruby/guides/good_job/) | [Bug Tracker](https://github.com/getsentry/sentry-ruby/issues) | [Forum](https://forum.sentry.io/) | IRC: irc.freenode.net, #sentry
19
+
20
+ The official Ruby-language client and integration layer for the [Sentry](https://github.com/getsentry/sentry) error reporting API.
21
+
22
+ ## Getting Started
23
+
24
+ ### Install
25
+
26
+ ```ruby
27
+ gem "sentry-ruby"
28
+ gem "sentry-rails"
29
+ gem "good_job"
30
+ gem "sentry-good_job"
31
+ ```
32
+
33
+ Then you're all set! `sentry-good_job` will automatically capture exceptions from your ActiveJob workers when using Good Job as the backend!
34
+
35
+ ## Features
36
+
37
+ - **Automatic Error Capture**: Captures exceptions from ActiveJob workers using Good Job
38
+ - **Performance Monitoring**: Tracks job execution times and performance metrics
39
+ - **Cron Monitoring**: Automatic setup for scheduled jobs with cron monitoring
40
+ - **Context Preservation**: Maintains user context and trace propagation across job executions
41
+ - **Configurable Reporting**: Control when errors are reported (after retries, only dead jobs, etc.)
42
+ - **Rails Integration**: Seamless integration with Rails applications
43
+
44
+ ## Configuration
45
+
46
+ The integration can be configured through Sentry's configuration:
47
+
48
+ ```ruby
49
+ Sentry.init do |config|
50
+ config.dsn = 'your-dsn-here'
51
+
52
+ # Good Job specific configuration
53
+ config.good_job.enable_cron_monitors = true
54
+
55
+ # ActiveJob configuration (handled by sentry-rails)
56
+ config.rails.active_job_report_on_retry_error = false
57
+ config.send_default_pii = false
58
+
59
+ # Optional: Configure logging for debugging
60
+ config.sdk_logger = Rails.logger
61
+ end
62
+ ```
63
+
64
+ ### Configuration Options
65
+
66
+ #### Good Job Specific Options
67
+
68
+ - `enable_cron_monitors` (default: `true`): Enable cron monitoring for scheduled jobs
69
+
70
+ #### ActiveJob Options (handled by sentry-rails)
71
+
72
+ - `config.rails.active_job_report_on_retry_error` (default: `false`): Only report errors after all retry attempts are exhausted
73
+ - `config.send_default_pii` (default: `false`): Include job arguments in error context (be careful with sensitive data)
74
+ - `config.sdk_logger` (default: `nil`): Configure the SDK logger for custom logging needs (general Sentry configuration)
75
+
76
+ **Note**: The Good Job integration now leverages sentry-rails for core ActiveJob functionality, including trace propagation, user context preservation, and error reporting. This provides better integration and reduces duplication.
77
+
78
+ ## Usage
79
+
80
+ ### Automatic Setup
81
+
82
+ The integration works automatically once installed. It will:
83
+
84
+ 1. **Capture exceptions** from ActiveJob workers using sentry-rails
85
+ 2. **Set up performance monitoring** for job execution with enhanced GoodJob-specific metrics
86
+ 3. **Automatically configure cron monitoring** for scheduled jobs
87
+ 4. **Preserve user context and trace propagation** across job executions
88
+ 5. **Add GoodJob-specific context** including queue name, executions, priority, and latency
89
+
90
+ ### Cron Monitoring
91
+
92
+ For scheduled jobs, cron monitoring is automatically set up based on your Good Job configuration:
93
+
94
+ ```ruby
95
+ # config/application.rb
96
+ config.good_job.cron = {
97
+ 'my_scheduled_job' => {
98
+ class: 'MyScheduledJob',
99
+ cron: '0 * * * *' # Every hour
100
+ }
101
+ }
102
+ ```
103
+
104
+ You can also manually set up cron monitoring:
105
+
106
+ ```ruby
107
+ class MyScheduledJob < ApplicationJob
108
+ include Sentry::Cron::MonitorCheckIns
109
+
110
+ sentry_monitor_check_ins(
111
+ slug: "my_scheduled_job",
112
+ monitor_config: Sentry::Cron::MonitorConfig.from_crontab("0 * * * *", timezone: "UTC")
113
+ )
114
+ end
115
+ ```
116
+
117
+ ### Custom Error Handling
118
+
119
+ The integration respects ActiveJob's retry configuration and will only report errors based on your settings:
120
+
121
+ ```ruby
122
+ class MyJob < ApplicationJob
123
+ retry_on StandardError, wait: :exponentially_longer, attempts: 3
124
+
125
+ def perform
126
+ # This will only be reported to Sentry after 3 attempts if active_job_report_on_retry_error is true
127
+ raise "Something went wrong"
128
+ end
129
+ end
130
+ ```
131
+
132
+ ### Debugging and Detailed Logging
133
+
134
+ The integration uses the standard Sentry SDK logger (`Sentry.configuration.sdk_logger`) for all logging needs. You can configure this logger to get detailed information about what the integration is doing:
135
+
136
+ ```ruby
137
+ Sentry.init do |config|
138
+ config.dsn = 'your-dsn-here'
139
+
140
+ # Configure the SDK logger for debugging
141
+ config.sdk_logger = Logger.new($stdout)
142
+ config.sdk_logger.level = Logger::DEBUG
143
+
144
+ # Or use Rails logger with debug level
145
+ # config.sdk_logger = Rails.logger
146
+ # config.sdk_logger.level = Logger::DEBUG
147
+ end
148
+ ```
149
+
150
+ #### Log Levels
151
+
152
+ The integration logs at different levels:
153
+ - **INFO**: Integration setup, cron monitoring configuration, job monitoring setup
154
+ - **WARN**: Configuration issues, missing job classes, cron parsing errors
155
+ - **DEBUG**: Detailed execution flow (when debug level is enabled)
156
+
157
+ #### What Gets Logged
158
+
159
+ When logging is enabled, you'll see information about:
160
+ - Job execution start and completion
161
+ - Error capture and reporting decisions
162
+ - Cron monitoring setup and configuration
163
+ - Performance metrics collection
164
+ - GoodJob-specific context enhancement
165
+ - Integration initialization and setup
166
+
167
+ ## Performance Monitoring
168
+
169
+ When performance monitoring is enabled, the integration will track:
170
+
171
+ - Job execution time
172
+ - Queue latency (GoodJob-specific)
173
+ - Retry counts
174
+ - Job context and metadata
175
+ - GoodJob-specific metrics (queue name, executions, priority)
176
+
177
+ ## Error Context
178
+
179
+ The integration automatically adds relevant context to error reports:
180
+
181
+ - Job class name
182
+ - Job ID
183
+ - Queue name (GoodJob-specific)
184
+ - Execution count (GoodJob-specific)
185
+ - Priority (GoodJob-specific)
186
+ - Enqueued and scheduled timestamps
187
+ - Job arguments (if enabled via send_default_pii)
188
+ - Latency metrics (GoodJob-specific)
189
+
190
+ ## Compatibility
191
+
192
+ - Ruby 2.4+
193
+ - Rails 5.2+
194
+ - Good Job 3.0+
195
+ - Sentry Ruby SDK 5.28.0+
196
+
197
+ ## Contributing
198
+
199
+ We welcome contributions! Please see our [contributing guidelines](https://github.com/getsentry/sentry-ruby/blob/master/CONTRIBUTING.md) for details.
200
+
201
+ ## License
202
+
203
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE.txt) file for details.
data/SECURITY.md ADDED
@@ -0,0 +1,28 @@
1
+ # SECURITY
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ **Do NOT** open a public GitHub issue for security vulnerabilities.
6
+
7
+ Email security details to: **security@kiskolabs.com**
8
+
9
+ Include: description, steps to reproduce, potential impact, and suggested fix (if available).
10
+
11
+ ### Response Timeline
12
+
13
+ - We will acknowledge receipt of your report
14
+ - We will provide an initial assessment
15
+ - We will keep you informed of our progress and resolution timeline
16
+
17
+ ### Disclosure Policy
18
+
19
+ - We will work with you to understand and resolve the issue
20
+ - We will credit you for the discovery (unless you prefer to remain anonymous)
21
+ - We will publish a security advisory after the vulnerability is patched
22
+ - We will coordinate public disclosure with you
23
+
24
+ ## Automation Security
25
+
26
+ * **Context Isolation:** It is strictly forbidden to include production credentials, API keys, or Personally Identifiable Information (PII) in prompts sent to third-party LLMs or automation services.
27
+
28
+ * **Supply Chain:** All automated dependencies must be verified.
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "sentry-good_job"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "fileutils"
5
+
6
+ # path to your application root.
7
+ APP_ROOT = File.expand_path("..", __dir__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ FileUtils.chdir APP_ROOT do
14
+ # This script is a way to set up or update your development environment automatically.
15
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
16
+ # Add necessary setup steps to this file.
17
+
18
+ puts "== Installing dependencies =="
19
+ system! "gem install bundler --conservative"
20
+ system("bundle check") || system!("bundle install")
21
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ # GoodJob-specific extensions to sentry-rails ActiveJob integration
4
+ # This module enhances sentry-rails ActiveJob with GoodJob-specific functionality:
5
+ # - GoodJob-specific context and tags
6
+ # - GoodJob-specific span data enhancements
7
+ module Sentry
8
+ module GoodJob
9
+ module ActiveJobExtensions
10
+ # Enhance sentry-rails ActiveJob context with GoodJob-specific data
11
+ def self.enhance_sentry_context(job, base_context)
12
+ return base_context unless job.respond_to?(:queue_name) && job.respond_to?(:executions)
13
+
14
+ # Add GoodJob-specific context to the existing sentry-rails context
15
+ good_job_context = {
16
+ queue_name: job.queue_name,
17
+ executions: job.executions,
18
+ enqueued_at: job.enqueued_at,
19
+ priority: job.respond_to?(:priority) ? job.priority : nil
20
+ }
21
+
22
+ # Merge with base context, preserving existing structure
23
+ base_context.merge(good_job: good_job_context)
24
+ end
25
+
26
+ # Enhance sentry-rails ActiveJob tags with GoodJob-specific data
27
+ def self.enhance_sentry_tags(job, base_tags)
28
+ return base_tags unless job.respond_to?(:queue_name) && job.respond_to?(:executions)
29
+
30
+ good_job_tags = {
31
+ queue_name: job.queue_name,
32
+ executions: job.executions
33
+ }
34
+
35
+ # Add priority if available
36
+ if job.respond_to?(:priority)
37
+ good_job_tags[:priority] = job.priority
38
+ end
39
+
40
+ base_tags.merge(good_job_tags)
41
+ end
42
+
43
+ # Set up GoodJob-specific ActiveJob extensions
44
+ def self.setup
45
+ return unless defined?(::Rails) && ::Sentry.initialized?
46
+
47
+ # Hook into sentry-rails ActiveJob integration
48
+ if defined?(::Sentry::Rails::ActiveJobExtensions::SentryReporter)
49
+ enhance_sentry_reporter
50
+ end
51
+
52
+ # Set up GoodJob-specific ActiveJob extensions
53
+ setup_good_job_extensions
54
+ end
55
+
56
+ def self.enhance_sentry_reporter
57
+ return if defined?(@reporter_enhanced) && @reporter_enhanced
58
+
59
+ # Enhance the sentry_context method in SentryReporter
60
+ ::Sentry::Rails::ActiveJobExtensions::SentryReporter.class_eval do
61
+ class << self
62
+ alias_method :original_sentry_context, :sentry_context
63
+
64
+ def sentry_context(job)
65
+ base_context = original_sentry_context(job)
66
+ Sentry::GoodJob::ActiveJobExtensions.enhance_sentry_context(job, base_context)
67
+ end
68
+ end
69
+ end
70
+
71
+ @reporter_enhanced = true
72
+ end
73
+
74
+ def self.setup_good_job_extensions
75
+ # Extend ActiveJob::Base with GoodJob-specific functionality
76
+ ActiveSupport.on_load(:active_job) do
77
+ # Add GoodJob-specific attributes and methods
78
+ include GoodJobExtensions
79
+
80
+ # Ensure the sentry-rails integration is properly set up
81
+ # by checking if the ActiveJobExtensions module is already included
82
+ if defined?(::Sentry::Rails::ActiveJobExtensions) && !ancestors.include?(::Sentry::Rails::ActiveJobExtensions)
83
+ require "sentry/rails/active_job"
84
+ prepend ::Sentry::Rails::ActiveJobExtensions
85
+ end
86
+ end
87
+ end
88
+
89
+ private_class_method :enhance_sentry_reporter, :setup_good_job_extensions
90
+
91
+ # GoodJob-specific extensions for ActiveJob
92
+ module GoodJobExtensions
93
+ extend ActiveSupport::Concern
94
+
95
+ included do
96
+ # Set up around_enqueue hook for GoodJob-specific enqueue span
97
+ around_enqueue do |job, block|
98
+ next block.call unless ::Sentry.initialized?
99
+
100
+ # Create enqueue span with GoodJob-specific data
101
+ ::Sentry.with_child_span(op: "queue.publish", description: job.class.name) do |span|
102
+ _sentry_set_span_data(span, job)
103
+ block.call
104
+ end
105
+ end
106
+
107
+ # GoodJob-specific context is now handled through the enhanced sentry_context method
108
+ # The sentry-rails integration handles error capturing through SentryReporter.record
109
+ end
110
+
111
+ private
112
+
113
+ # Override _sentry_set_span_data to add GoodJob-specific functionality
114
+ def _sentry_set_span_data(span, job, retry_count: nil)
115
+ return unless span
116
+
117
+ # Call the base implementation if it exists (from sentry-rails)
118
+ if respond_to?(:_sentry_set_span_data, true) && method(:_sentry_set_span_data).super_method
119
+ super
120
+ else
121
+ # Fallback: implement base functionality directly
122
+ span.set_data("messaging.message.id", job.job_id)
123
+ span.set_data("messaging.destination.name", job.queue_name) if job.respond_to?(:queue_name)
124
+ span.set_data("messaging.message.retry.count", retry_count) if retry_count
125
+ end
126
+
127
+ # Add GoodJob-specific span data (latency)
128
+ latency = calculate_job_latency(job)
129
+ span.set_data("messaging.message.receive.latency", latency) if latency
130
+ end
131
+
132
+ # Calculate job latency in milliseconds (GoodJob-specific)
133
+ def calculate_job_latency(job)
134
+ return nil unless job.enqueued_at
135
+
136
+ ((Time.now.utc - job.enqueued_at) * 1000).to_i
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class Configuration
5
+ attr_reader :good_job
6
+
7
+ add_post_initialization_callback do
8
+ @good_job = Sentry::GoodJob::Configuration.new
9
+ @excluded_exceptions = @excluded_exceptions.concat(Sentry::GoodJob::IGNORE_DEFAULT)
10
+ end
11
+ end
12
+
13
+ module GoodJob
14
+ IGNORE_DEFAULT = [
15
+ "ActiveJob::DeserializationError",
16
+ "ActiveJob::SerializationError"
17
+ ]
18
+
19
+ class Configuration
20
+ # Whether to enable cron monitoring for all scheduled jobs
21
+ # This is GoodJob-specific functionality for monitoring scheduled tasks
22
+ attr_accessor :enable_cron_monitors
23
+
24
+ def initialize
25
+ @enable_cron_monitors = true
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helper methods for adding GoodJob-specific information to Sentry context
4
+ # This works WITH sentry-rails, not against it
5
+ module Sentry
6
+ module GoodJob
7
+ module ContextHelpers
8
+ # Add GoodJob-specific information to the existing Sentry Rails context
9
+ def self.add_context(job, base_context = {})
10
+ return base_context unless job.respond_to?(:queue_name) && job.respond_to?(:executions)
11
+
12
+ good_job_context = {
13
+ queue_name: job.queue_name,
14
+ executions: job.executions,
15
+ enqueued_at: job.enqueued_at,
16
+ priority: job.respond_to?(:priority) ? job.priority : nil
17
+ }
18
+
19
+ # Note: Job arguments are handled by sentry-rails via send_default_pii configuration
20
+ # This is controlled by Sentry.configuration.send_default_pii, not GoodJob-specific config
21
+
22
+ # Merge with base context
23
+ base_context.merge(good_job: good_job_context)
24
+ end
25
+
26
+ # Add GoodJob-specific information to the existing Sentry Rails tags
27
+ def self.add_tags(job, base_tags = {})
28
+ return base_tags unless job.respond_to?(:queue_name) && job.respond_to?(:executions)
29
+
30
+ good_job_tags = {
31
+ queue_name: job.queue_name,
32
+ executions: job.executions
33
+ }
34
+
35
+ # Add priority if available
36
+ if job.respond_to?(:priority)
37
+ good_job_tags[:priority] = job.priority
38
+ end
39
+
40
+ base_tags.merge(good_job_tags)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Sentry Cron Monitoring for Active Job
4
+ # This module provides comprehensive cron monitoring for Active Job scheduled tasks
5
+ # It works with any Active Job adapter, including GoodJob
6
+ # Following Active Job's extension patterns and Sentry's integration guidelines
7
+ module Sentry
8
+ module GoodJob
9
+ module CronHelpers
10
+ # Utility methods for cron parsing and configuration
11
+ # These methods handle the conversion between Good Job cron expressions and Sentry monitor configs
12
+ module Helpers
13
+ # Parse cron expression and create Sentry monitor config
14
+ def self.monitor_config_from_cron(cron_expression, timezone: nil)
15
+ return nil unless cron_expression && !cron_expression.strip.empty?
16
+
17
+ # Parse cron expression using fugit (same as Good Job)
18
+ parsed_cron = Fugit.parse_cron(cron_expression)
19
+ return nil unless parsed_cron
20
+
21
+ # Convert to Sentry monitor config
22
+ if timezone && !timezone.strip.empty?
23
+ ::Sentry::Cron::MonitorConfig.from_crontab(cron_expression, timezone: timezone)
24
+ else
25
+ ::Sentry::Cron::MonitorConfig.from_crontab(cron_expression)
26
+ end
27
+ rescue => e
28
+ Sentry.configuration.sdk_logger.warn "Failed to parse cron expression '#{cron_expression}': #{e.message}"
29
+ nil
30
+ end
31
+
32
+ # Generate monitor slug from job name
33
+ def self.monitor_slug(job_name)
34
+ job_name.to_s.underscore.gsub(/_job$/, "")
35
+ end
36
+
37
+ # Parse cron expression and extract timezone
38
+ def self.parse_cron_with_timezone(cron_expression)
39
+ return [cron_expression, nil] unless cron_expression && !cron_expression.strip.empty?
40
+
41
+ parts = cron_expression.strip.split(" ")
42
+ return [cron_expression, nil] unless parts.length > 5
43
+
44
+ # Last part might be timezone
45
+ timezone = parts.last
46
+ # Comprehensive timezone validation that handles:
47
+ # - Standard timezone names (UTC, GMT)
48
+ # - IANA timezone identifiers (America/New_York, Europe/Stockholm)
49
+ # - Multi-level IANA timezones (America/Argentina/Buenos_Aires)
50
+ # - UTC offsets (UTC+2, UTC-5, GMT+1, GMT-8)
51
+ # - Numeric timezones (GMT-5, UTC+2)
52
+ if timezone.match?(/^[A-Za-z_]+$/) || # Simple timezone names (UTC, GMT, EST, etc.)
53
+ timezone.match?(/^[A-Za-z_]+\/[A-Za-z_]+$/) || # Single slash timezones (Europe/Stockholm)
54
+ timezone.match?(/^[A-Za-z_]+\/[A-Za-z_]+\/[A-Za-z_]+$/) || # Multi-slash timezones (America/Argentina/Buenos_Aires)
55
+ timezone.match?(/^[A-Za-z_]+[+-]\d+$/) || # UTC/GMT offsets (UTC+2, GMT-5)
56
+ timezone.match?(/^[A-Za-z_]+\/[A-Za-z_]+[+-]\d+$/) # IANA with offset (Europe/Stockholm+1)
57
+ cron_without_timezone = cron_expression.gsub(/\s+#{Regexp.escape(timezone)}$/, "")
58
+ [cron_without_timezone, timezone]
59
+ else
60
+ [cron_expression, nil]
61
+ end
62
+ end
63
+ end
64
+
65
+ # Main integration class that handles all cron monitoring setup
66
+ # This class follows Good Job's integration patterns and Sentry's extension guidelines
67
+ class Integration
68
+ # Track whether setup has already been performed to prevent duplicates
69
+ @setup_completed = false
70
+ @reload_hooked = false
71
+
72
+ # Set up monitoring for all scheduled jobs from Good Job configuration
73
+ def self.setup_monitoring_for_scheduled_jobs
74
+ return unless ::Sentry.initialized?
75
+ return unless ::Sentry.configuration.good_job.enable_cron_monitors
76
+ attach_reload_hook_if_available
77
+ return if @setup_completed
78
+
79
+ return unless defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application
80
+ cron_config = ::Rails.application.config.good_job.cron
81
+ return if cron_config.blank?
82
+
83
+ added_jobs = []
84
+ cron_config.each do |cron_key, job_config|
85
+ job_name = setup_monitoring_for_job(cron_key, job_config)
86
+ added_jobs << job_name if job_name
87
+ end
88
+
89
+ @setup_completed = true
90
+ if added_jobs.any?
91
+ job_list = added_jobs.join(", ")
92
+ Sentry.configuration.sdk_logger.info "Sentry cron monitoring setup for #{added_jobs.size} scheduled jobs: #{job_list}"
93
+ else
94
+ Sentry.configuration.sdk_logger.info "Sentry cron monitoring setup for #{cron_config.keys.size} scheduled jobs"
95
+ end
96
+ end
97
+
98
+ # Reset setup state (primarily for testing)
99
+ def self.reset_setup_state!
100
+ @setup_completed = false
101
+ end
102
+
103
+ # Set up monitoring for a specific job
104
+ def self.setup_monitoring_for_job(cron_key, job_config)
105
+ job_class_name = job_config[:class]
106
+ cron_expression = job_config[:cron]
107
+
108
+ return unless job_class_name && cron_expression
109
+
110
+ # Defer job class constantization to avoid boot-time issues
111
+ # The job class will be constantized when the job is actually executed
112
+ # This prevents issues during development boot and circular dependencies
113
+
114
+ # Store the monitoring configuration for later use
115
+ # We'll set up the monitoring when the job class is first loaded
116
+ deferred_setup = lambda do
117
+ job_class = begin
118
+ job_class_name.constantize
119
+ rescue NameError => e
120
+ Sentry.configuration.sdk_logger.warn "Could not find job class '#{job_class_name}' for Sentry cron monitoring: #{e.message}"
121
+ return
122
+ end
123
+
124
+ # Include Sentry::Cron::MonitorCheckIns module for cron monitoring
125
+ # only patch if not explicitly included in job by user
126
+ unless job_class.ancestors.include?(Sentry::Cron::MonitorCheckIns)
127
+ job_class.include(Sentry::Cron::MonitorCheckIns)
128
+ end
129
+
130
+ # Parse cron expression and create monitor config
131
+ cron_without_tz, timezone = Sentry::GoodJob::CronHelpers::Helpers.parse_cron_with_timezone(cron_expression)
132
+ monitor_config = Sentry::GoodJob::CronHelpers::Helpers.monitor_config_from_cron(cron_without_tz, timezone: timezone)
133
+
134
+ if monitor_config
135
+ # Configure Sentry cron monitoring - use cron_key as slug for consistency
136
+ monitor_slug = Sentry::GoodJob::CronHelpers::Helpers.monitor_slug(cron_key)
137
+
138
+ job_class.sentry_monitor_check_ins(
139
+ slug: monitor_slug,
140
+ monitor_config: monitor_config
141
+ )
142
+
143
+ job_class_name
144
+ else
145
+ Sentry.configuration.sdk_logger.warn "Could not create monitor config for #{job_class_name} with cron '#{cron_expression}'"
146
+ nil
147
+ end
148
+ end
149
+
150
+ # Set up monitoring when the job class is first loaded
151
+ # This defers constantization until the job is actually needed
152
+ if defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application
153
+ ::Rails.application.config.after_initialize do
154
+ deferred_setup.call
155
+ end
156
+ else
157
+ # Fallback for non-Rails environments
158
+ deferred_setup.call
159
+ end
160
+
161
+ # Return the job name for logging purposes
162
+ job_class_name
163
+ end
164
+
165
+ # Manually add cron monitoring to a specific job
166
+ def self.add_monitoring_to_job(job_class, slug: nil, cron_expression: nil, timezone: nil)
167
+ return unless ::Sentry.initialized?
168
+
169
+ # Include Sentry::Cron::MonitorCheckIns module for cron monitoring
170
+ # only patch if not explicitly included in job by user
171
+ unless job_class.ancestors.include?(Sentry::Cron::MonitorCheckIns)
172
+ job_class.include(Sentry::Cron::MonitorCheckIns)
173
+ end
174
+
175
+ # Create monitor config
176
+ monitor_config = if cron_expression
177
+ Sentry::GoodJob::CronHelpers::Helpers.monitor_config_from_cron(cron_expression, timezone: timezone)
178
+ else
179
+ # Default to hourly monitoring if no cron expression provided
180
+ ::Sentry::Cron::MonitorConfig.from_crontab("0 * * * *")
181
+ end
182
+
183
+ if monitor_config
184
+ monitor_slug = slug || Sentry::GoodJob::CronHelpers::Helpers.monitor_slug(job_class.name)
185
+
186
+ job_class.sentry_monitor_check_ins(
187
+ slug: monitor_slug,
188
+ monitor_config: monitor_config
189
+ )
190
+
191
+ Sentry.configuration.sdk_logger.info "Added Sentry cron monitoring for #{job_class.name} (#{monitor_slug})"
192
+ end
193
+ end
194
+
195
+ def self.attach_reload_hook_if_available
196
+ return if @reload_hooked
197
+ return unless defined?(::ActiveSupport::Reloader)
198
+
199
+ ::ActiveSupport::Reloader.to_prepare do
200
+ @setup_completed = false
201
+ end
202
+
203
+ @reload_hooked = true
204
+ rescue NameError
205
+ # ActiveSupport::Reloader not available in this environment
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module GoodJob
5
+ VERSION = "6.2.0"
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is kept for backward compatibility and simply requires the main entry point
4
+ require "sentry-good_job"
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "good_job"
4
+ require "sentry-ruby"
5
+ require "sentry/integrable"
6
+ require "sentry/good_job/version"
7
+ require "sentry/good_job/configuration"
8
+ require "sentry/good_job/context_helpers"
9
+ require "sentry/good_job/active_job_extensions"
10
+ require "sentry/good_job/cron_helpers"
11
+
12
+ module Sentry
13
+ module GoodJob
14
+ extend Sentry::Integrable
15
+
16
+ register_integration name: "good_job", version: Sentry::GoodJob::VERSION
17
+
18
+ if defined?(::Rails::Railtie)
19
+ class Railtie < ::Rails::Railtie
20
+ config.after_initialize do
21
+ next unless Sentry.initialized? && defined?(::Sentry::Rails)
22
+
23
+ # Automatic setup for Good Job when the integration is enabled
24
+ if Sentry.configuration.enabled_patches.include?(:good_job)
25
+ Sentry::GoodJob.setup_good_job_integration
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.setup_good_job_integration
32
+ # Enhance sentry-rails ActiveJob integration with GoodJob-specific context
33
+ Sentry::GoodJob::ActiveJobExtensions.setup
34
+
35
+ # Set up cron monitoring for all scheduled jobs (automatically configured from Good Job config)
36
+ if Sentry.configuration.good_job.enable_cron_monitors
37
+ Sentry::GoodJob::CronHelpers::Integration.setup_monitoring_for_scheduled_jobs
38
+ end
39
+
40
+ Sentry.configuration.sdk_logger.info "Sentry Good Job integration initialized automatically"
41
+ end
42
+
43
+ # Delegate capture_exception so internal components can be tested in isolation
44
+ def self.capture_exception(exception, **options)
45
+ ::Sentry.capture_exception(exception, **options)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/sentry/good_job/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sentry-good_job"
7
+ spec.version = Sentry::GoodJob::VERSION
8
+ spec.authors = ["Sentry Team", "Andrei Makarov"]
9
+ spec.summary = "GoodJob integration for the Sentry error logger"
10
+ spec.description = "Adds Sentry instrumentation, context helpers, and cron monitoring support to GoodJob-backed ActiveJob workloads."
11
+ spec.email = "contact@kiskolabs.com"
12
+ spec.license = "MIT"
13
+
14
+ spec.platform = Gem::Platform::RUBY
15
+ spec.required_ruby_version = ">= 2.4"
16
+ spec.extra_rdoc_files = ["README.md", "LICENSE.txt"]
17
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
18
+ Dir[
19
+ "lib/**/*",
20
+ "bin/*",
21
+ "CHANGELOG.md",
22
+ "README.md",
23
+ "LICENSE.txt",
24
+ "CODE_OF_CONDUCT.md",
25
+ "CONTRIBUTING.md",
26
+ "GOVERNANCE.md",
27
+ "SECURITY.md",
28
+ "sentry-good_job.gemspec"
29
+ ].select { |path| File.file?(path) }
30
+ end
31
+
32
+ github_root_uri = "https://github.com/amkisko/sentry-good_job"
33
+ spec.homepage = github_root_uri
34
+
35
+ spec.metadata = {
36
+ "homepage_uri" => spec.homepage,
37
+ "source_code_uri" => "#{github_root_uri}/tree/main",
38
+ "changelog_uri" => "#{github_root_uri}/blob/main/CHANGELOG.md",
39
+ "bug_tracker_uri" => "#{github_root_uri}/issues",
40
+ "documentation_uri" => "http://www.rubydoc.info/gems/#{spec.name}/#{spec.version}"
41
+ }
42
+
43
+ spec.bindir = "exe"
44
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
45
+ spec.require_paths = ["lib"]
46
+
47
+ spec.add_dependency "sentry-ruby", "~> 6"
48
+ spec.add_dependency "good_job", "~> 4"
49
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sentry-good_job
3
+ version: !ruby/object:Gem::Version
4
+ version: 6.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Sentry Team
8
+ - Andrei Makarov
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sentry-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: good_job
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4'
41
+ description: Adds Sentry instrumentation, context helpers, and cron monitoring support
42
+ to GoodJob-backed ActiveJob workloads.
43
+ email: contact@kiskolabs.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files:
47
+ - LICENSE.txt
48
+ - README.md
49
+ files:
50
+ - CHANGELOG.md
51
+ - CODE_OF_CONDUCT.md
52
+ - CONTRIBUTING.md
53
+ - GOVERNANCE.md
54
+ - LICENSE.txt
55
+ - README.md
56
+ - SECURITY.md
57
+ - bin/console
58
+ - bin/setup
59
+ - lib/sentry-good_job.rb
60
+ - lib/sentry/good_job.rb
61
+ - lib/sentry/good_job/active_job_extensions.rb
62
+ - lib/sentry/good_job/configuration.rb
63
+ - lib/sentry/good_job/context_helpers.rb
64
+ - lib/sentry/good_job/cron_helpers.rb
65
+ - lib/sentry/good_job/version.rb
66
+ - sentry-good_job.gemspec
67
+ homepage: https://github.com/amkisko/sentry-good_job
68
+ licenses:
69
+ - MIT
70
+ metadata:
71
+ homepage_uri: https://github.com/amkisko/sentry-good_job
72
+ source_code_uri: https://github.com/amkisko/sentry-good_job/tree/main
73
+ changelog_uri: https://github.com/amkisko/sentry-good_job/blob/main/CHANGELOG.md
74
+ bug_tracker_uri: https://github.com/amkisko/sentry-good_job/issues
75
+ documentation_uri: http://www.rubydoc.info/gems/sentry-good_job/6.2.0
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '2.4'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubygems_version: 3.6.9
91
+ specification_version: 4
92
+ summary: GoodJob integration for the Sentry error logger
93
+ test_files: []