sentry-sidekiq 5.19.0 → 5.28.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41e6321e26f56fb048d1c360ea5121e32f0a6dc2b015da1b4e1ed2a81b658de0
4
- data.tar.gz: b173d81283a38d46991d796ed927e052068905eafd35ac307cb90f10080cc903
3
+ metadata.gz: dae59f48a8af139f938dfaf4b9662797c9320bbf2920486d3521e5140f719360
4
+ data.tar.gz: 38a0197810f80a084c28740da6d3dfe26977e31e62d534aeead78946b284730c
5
5
  SHA512:
6
- metadata.gz: 1d3c7ca9f379c3e248a762e1f8ade65a4e66ad9e796b17a157ccdc0bca2f426764d0722072e69c119919944f69e01d9535be1b06de40333353e10579bbb0faca
7
- data.tar.gz: 072e73ad9ce4d76c9839b8a60c0fd927f2090914d4575fcf102cf16c3e499865c19a8d47be916931f368498bed67dafa737247483f2b9dcdcb07cd6d92149ef5
6
+ metadata.gz: 67cc29b6a0db050a3e9926a4c83ec81d4b1e19f7669289017d03aa07e63f82397cfe78c9f28f2349eb512ad1a7dbe27dd24c233aefc97f79fd0eb67b3d4992d1
7
+ data.tar.gz: 3e0a0e79502deaf39465e0d758a2af169c430fa5e520ea0839b1cbee032d23f35df5c6bcbea43047092953ca7199ece729f637bdd9c142efacc8dd852aaffc4f
data/Gemfile CHANGED
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
  git_source(:github) { |name| "https://github.com/#{name}.git" }
3
5
 
6
+ eval_gemfile "../Gemfile.dev"
7
+
4
8
  # Specify your gem's dependencies in sentry-ruby.gemspec
5
9
  gemspec
10
+
6
11
  gem "sentry-ruby", path: "../sentry-ruby"
7
12
  gem "sentry-rails", path: "../sentry-rails"
8
13
 
@@ -10,20 +15,29 @@ gem "sentry-rails", path: "../sentry-rails"
10
15
  # loofah changed the required ruby version in a patch so we need to explicitly pin it
11
16
  gem "loofah", "2.20.0" if RUBY_VERSION.to_f < 2.5
12
17
 
13
- # For https://github.com/ruby/psych/issues/655
14
- gem "psych", "5.1.0"
15
-
16
- sidekiq_version = ENV["SIDEKIQ_VERSION"]
17
- sidekiq_version = "7.0" if sidekiq_version.nil?
18
- sidekiq_version = Gem::Version.new(sidekiq_version)
18
+ if ENV["SIDEKIQ_MAIN"]
19
+ gem "sidekiq", github: "sidekiq/sidekiq", branch: "main"
20
+ sidekiq_version = "main"
21
+ else
22
+ sidekiq_version = ENV["SIDEKIQ_VERSION"]
23
+ sidekiq_version = "7.0" if sidekiq_version.nil?
24
+ sidekiq_version = Gem::Version.new(sidekiq_version)
19
25
 
20
- gem "sidekiq", "~> #{sidekiq_version}"
26
+ gem "sidekiq", "~> #{sidekiq_version}"
27
+ end
21
28
 
22
- if RUBY_VERSION.to_f >= 2.7 && sidekiq_version >= Gem::Version.new("6.0")
29
+ if sidekiq_version == "main" || RUBY_VERSION.to_f >= 2.7 && sidekiq_version >= Gem::Version.new("6.0")
23
30
  gem "sidekiq-cron"
24
- gem "sidekiq-scheduler"
31
+
32
+ if sidekiq_version == "main" || sidekiq_version >= Gem::Version.new("8.0")
33
+ gem "sidekiq-scheduler", "~> 6.0.0.beta"
34
+ else
35
+ gem "sidekiq-scheduler", "~> 5.0.0"
36
+ end
25
37
  end
26
38
 
27
39
  gem "rails", "> 5.0.0"
28
40
 
29
- eval_gemfile File.expand_path("../Gemfile", __dir__)
41
+ gem "timecop"
42
+
43
+ gem "vernier", platforms: :ruby if RUBY_VERSION >= "3.2.1"
data/Makefile CHANGED
@@ -1,7 +1,3 @@
1
1
  build:
2
2
  bundle install
3
3
  gem build sentry-sidekiq.gemspec
4
-
5
- test:
6
- WITH_SENTRY_RAILS=1 bundle exec rspec spec/sentry/rails_spec.rb
7
- bundle exec rspec
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "sentry/ruby"
data/example/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "sidekiq"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sidekiq"
2
4
  require "sentry-sidekiq"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Configuration
3
5
  attr_reader :sidekiq
@@ -9,15 +11,26 @@ module Sentry
9
11
  end
10
12
 
11
13
  module Sidekiq
12
- IGNORE_DEFAULT = ["Sidekiq::JobRetry::Skip"]
14
+ IGNORE_DEFAULT = [
15
+ "Sidekiq::JobRetry::Skip",
16
+ "Sidekiq::JobRetry::Handled"
17
+ ]
13
18
 
14
19
  class Configuration
15
20
  # Set this option to true if you want Sentry to only capture the last job
16
21
  # retry if it fails.
17
22
  attr_accessor :report_after_job_retries
18
23
 
24
+ # Only report jobs that don't have `dead: false` set in the job's `sidekiq_options`
25
+ attr_accessor :report_only_dead_jobs
26
+
27
+ # Whether we should inject headers while enqueuing the job in order to have a connected trace
28
+ attr_accessor :propagate_traces
29
+
19
30
  def initialize
20
31
  @report_after_job_retries = false
32
+ @report_only_dead_jobs = false
33
+ @propagate_traces = true
21
34
  end
22
35
  end
23
36
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Sidekiq
3
5
  class ContextFilter
4
- ACTIVEJOB_RESERVED_PREFIX_REGEX = /^_aj_/.freeze
5
- SIDEKIQ_NAME = "Sidekiq".freeze
6
+ ACTIVEJOB_RESERVED_PREFIX_REGEX = /^_aj_/
7
+ SIDEKIQ_NAME = "Sidekiq"
6
8
 
7
9
  attr_reader :context
8
10
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Sidekiq
5
+ module Cron
6
+ module Helpers
7
+ # This is used by Cron::Job and Scheduler
8
+ def self.monitor_config(cron)
9
+ cron_parts = cron.strip.split(" ")
10
+
11
+ if cron_parts.length > 5
12
+ timezone = cron_parts.pop
13
+ cron_without_timezone = cron_parts.join(" ")
14
+
15
+ Sentry::Cron::MonitorConfig.from_crontab(cron_without_timezone, timezone: timezone)
16
+ else
17
+ Sentry::Cron::MonitorConfig.from_crontab(cron)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -12,6 +12,32 @@ module Sentry
12
12
  module Sidekiq
13
13
  module Cron
14
14
  module Job
15
+ def self.enqueueing_method
16
+ ::Sidekiq::Cron::Job.instance_methods.include?(:enque!) ? :enque! : :enqueue!
17
+ end
18
+
19
+ define_method(enqueueing_method) do |*args|
20
+ # make sure the current thread has a clean hub
21
+ Sentry.clone_hub_to_current_thread
22
+
23
+ Sentry.with_scope do |scope|
24
+ Sentry.with_session_tracking do
25
+ begin
26
+ scope.set_transaction_name("#{name} (#{klass})")
27
+
28
+ transaction = start_transaction(scope)
29
+ scope.set_span(transaction) if transaction
30
+ super(*args)
31
+
32
+ finish_transaction(transaction, 200)
33
+ rescue
34
+ finish_transaction(transaction, 500)
35
+ raise
36
+ end
37
+ end
38
+ end
39
+ end
40
+
15
41
  def save
16
42
  # validation failed, do nothing
17
43
  return false unless super
@@ -19,21 +45,43 @@ module Sentry
19
45
  # fail gracefully if can't find class
20
46
  klass_const =
21
47
  begin
22
- ::Sidekiq::Cron::Support.constantize(klass.to_s)
48
+ if ::Sidekiq::Cron::Support.respond_to?(:safe_constantize)
49
+ ::Sidekiq::Cron::Support.safe_constantize(klass.to_s)
50
+ else
51
+ ::Sidekiq::Cron::Support.constantize(klass.to_s)
52
+ end
23
53
  rescue NameError
24
54
  return true
25
55
  end
26
56
 
57
+ return true if klass_const.nil? # Sidekiq::Cron returns nil if class is not found
58
+
27
59
  # only patch if not explicitly included in job by user
28
60
  unless klass_const.send(:ancestors).include?(Sentry::Cron::MonitorCheckIns)
29
61
  klass_const.send(:include, Sentry::Cron::MonitorCheckIns)
30
62
  klass_const.send(:sentry_monitor_check_ins,
31
- slug: name,
32
- monitor_config: Sentry::Cron::MonitorConfig.from_crontab(cron))
63
+ slug: name.to_s,
64
+ monitor_config: Sentry::Sidekiq::Cron::Helpers.monitor_config(parsed_cron.original))
33
65
  end
34
66
 
35
67
  true
36
68
  end
69
+
70
+ def start_transaction(scope)
71
+ Sentry.start_transaction(
72
+ name: scope.transaction_name,
73
+ source: scope.transaction_source,
74
+ op: "queue.sidekiq-cron",
75
+ origin: "auto.queue.sidekiq.cron"
76
+ )
77
+ end
78
+
79
+ def finish_transaction(transaction, status_code)
80
+ return unless transaction
81
+
82
+ transaction.set_http_status(status_code)
83
+ transaction.finish
84
+ end
37
85
  end
38
86
  end
39
87
  end
@@ -1,4 +1,6 @@
1
- require 'sentry/sidekiq/context_filter'
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/sidekiq/context_filter"
2
4
 
3
5
  module Sentry
4
6
  module Sidekiq
@@ -29,6 +31,22 @@ module Sentry
29
31
  end
30
32
  end
31
33
 
34
+ # See if we want to ignore jobs that have dead: false
35
+ return if Sentry.configuration.sidekiq.report_only_dead_jobs && context.dig(:job, "dead") == false
36
+
37
+ # Check if the retry count is below the attempt_threshold
38
+ attempt_threshold = context.dig(:job, "attempt_threshold")
39
+ if attempt_threshold && retryable?(context)
40
+ attempt_threshold = attempt_threshold.to_i
41
+ retry_count = context.dig(:job, "retry_count")
42
+ # attempt 1 - retry_count is nil
43
+ # attempt 2 - this is your first retry so retry_count is 0
44
+ # attempt 3 - you have retried once, retry_count is 1
45
+ attempt = retry_count.nil? ? 1 : retry_count.to_i + 2
46
+
47
+ return if attempt < attempt_threshold
48
+ end
49
+
32
50
  Sentry::Sidekiq.capture_exception(
33
51
  ex,
34
52
  contexts: { sidekiq: context_filter.filtered },
@@ -1,14 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sentry/sidekiq/context_filter'
3
+ require "sentry/sidekiq/context_filter"
4
4
 
5
5
  module Sentry
6
6
  module Sidekiq
7
+ module Helpers
8
+ def set_span_data(span, id:, queue:, latency: nil, retry_count: nil)
9
+ return unless span
10
+
11
+ span.set_data(Span::DataConventions::MESSAGING_MESSAGE_ID, id)
12
+ span.set_data(Span::DataConventions::MESSAGING_DESTINATION_NAME, queue)
13
+ span.set_data(Span::DataConventions::MESSAGING_MESSAGE_RECEIVE_LATENCY, latency) if latency
14
+ span.set_data(Span::DataConventions::MESSAGING_MESSAGE_RETRY_COUNT, retry_count) if retry_count
15
+ end
16
+
17
+ if ::Gem::Version.new(::Sidekiq::VERSION) >= ::Gem::Version.new("8.0.0")
18
+ def calculate_latency(job)
19
+ now_in_ms - job["enqueued_at"] if job["enqueued_at"]
20
+ end
21
+ else
22
+ def calculate_latency(job)
23
+ ((Time.now.to_f - job["enqueued_at"]) * 1000).round if job["enqueued_at"]
24
+ end
25
+ end
26
+
27
+ def now_in_ms
28
+ ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
29
+ end
30
+ end
31
+
7
32
  class SentryContextServerMiddleware
8
- OP_NAME = "queue.sidekiq"
33
+ include Sentry::Sidekiq::Helpers
34
+
35
+ OP_NAME = "queue.process"
9
36
  SPAN_ORIGIN = "auto.queue.sidekiq"
10
37
 
11
- def call(_worker, job, queue)
38
+ def call(worker, job, queue)
12
39
  return yield unless Sentry.initialized?
13
40
 
14
41
  context_filter = Sentry::Sidekiq::ContextFilter.new(job)
@@ -23,7 +50,20 @@ module Sentry
23
50
  scope.set_contexts(sidekiq: job.merge("queue" => queue))
24
51
  scope.set_transaction_name(context_filter.transaction_name, source: :task)
25
52
  transaction = start_transaction(scope, job["trace_propagation_headers"])
26
- scope.set_span(transaction) if transaction
53
+
54
+ if transaction
55
+ scope.set_span(transaction)
56
+
57
+ latency = calculate_latency(job)
58
+
59
+ set_span_data(
60
+ transaction,
61
+ id: job["jid"],
62
+ queue: queue,
63
+ latency: latency,
64
+ retry_count: job["retry_count"] || 0
65
+ )
66
+ end
27
67
 
28
68
  begin
29
69
  yield
@@ -63,13 +103,23 @@ module Sentry
63
103
  end
64
104
 
65
105
  class SentryContextClientMiddleware
66
- def call(_worker_class, job, _queue, _redis_pool)
106
+ include Sentry::Sidekiq::Helpers
107
+
108
+ def call(worker_class, job, queue, _redis_pool)
67
109
  return yield unless Sentry.initialized?
68
110
 
69
111
  user = Sentry.get_current_scope.user
70
112
  job["sentry_user"] = user unless user.empty?
71
- job["trace_propagation_headers"] ||= Sentry.get_trace_propagation_headers
72
- yield
113
+
114
+ if Sentry.configuration.sidekiq.propagate_traces
115
+ job["trace_propagation_headers"] ||= Sentry.get_trace_propagation_headers
116
+ end
117
+
118
+ Sentry.with_child_span(op: "queue.publish", description: worker_class.to_s) do |span|
119
+ set_span_data(span, id: job["jid"], queue: queue)
120
+
121
+ yield
122
+ end
73
123
  end
74
124
  end
75
125
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Sidekiq
3
- VERSION = "5.19.0"
5
+ VERSION = "5.28.1"
4
6
  end
5
7
  end
@@ -38,19 +38,7 @@ module Sentry
38
38
  monitor_config =
39
39
  case interval_type
40
40
  when "cron"
41
- # fugit is a second order dependency of sidekiq-scheduler via rufus-scheduler
42
- parsed_cron = ::Fugit.parse_cron(schedule)
43
- timezone = parsed_cron.timezone
44
-
45
- # fugit supports having the timezone part of the cron string,
46
- # so we need to pull that with some hacky stuff
47
- if timezone
48
- parsed_cron.instance_variable_set(:@timezone, nil)
49
- cron_without_timezone = parsed_cron.to_cron_s
50
- Sentry::Cron::MonitorConfig.from_crontab(cron_without_timezone, timezone: timezone.name)
51
- else
52
- Sentry::Cron::MonitorConfig.from_crontab(schedule)
53
- end
41
+ Sentry::Sidekiq::Cron::Helpers.monitor_config(schedule)
54
42
  when "every", "interval"
55
43
  Sentry::Cron::MonitorConfig.from_interval(rufus_job.frequency.to_i / 60, :minute)
56
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sidekiq"
2
4
  require "sentry-ruby"
3
5
  require "sentry/integrable"
@@ -41,5 +43,6 @@ Sidekiq.configure_client do |config|
41
43
  end
42
44
 
43
45
  # patches
46
+ require "sentry/sidekiq/cron/helpers"
44
47
  require "sentry/sidekiq/cron/job"
45
48
  require "sentry/sidekiq-scheduler/scheduler"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "lib/sentry/sidekiq/version"
2
4
 
3
5
  Gem::Specification.new do |spec|
@@ -11,7 +13,7 @@ Gem::Specification.new do |spec|
11
13
  spec.platform = Gem::Platform::RUBY
12
14
  spec.required_ruby_version = '>= 2.4'
13
15
  spec.extra_rdoc_files = ["README.md", "LICENSE.txt"]
14
- spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples)'`.split("\n")
16
+ spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples|\.rubocop\.yml)'`.split("\n")
15
17
 
16
18
  github_root_uri = 'https://github.com/getsentry/sentry-ruby'
17
19
  spec.homepage = "#{github_root_uri}/tree/#{spec.version}/#{spec.name}"
@@ -28,6 +30,6 @@ Gem::Specification.new do |spec|
28
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
31
  spec.require_paths = ["lib"]
30
32
 
31
- spec.add_dependency "sentry-ruby", "~> 5.19.0"
33
+ spec.add_dependency "sentry-ruby", "~> 5.28.1"
32
34
  spec.add_dependency "sidekiq", ">= 3.0"
33
35
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.19.0
4
+ version: 5.28.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-08-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: sentry-ruby
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 5.19.0
18
+ version: 5.28.1
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: 5.19.0
25
+ version: 5.28.1
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: sidekiq
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -43,8 +42,8 @@ email: accounts@sentry.io
43
42
  executables: []
44
43
  extensions: []
45
44
  extra_rdoc_files:
46
- - README.md
47
45
  - LICENSE.txt
46
+ - README.md
48
47
  files:
49
48
  - ".gitignore"
50
49
  - ".rspec"
@@ -64,21 +63,21 @@ files:
64
63
  - lib/sentry/sidekiq-scheduler/scheduler.rb
65
64
  - lib/sentry/sidekiq/configuration.rb
66
65
  - lib/sentry/sidekiq/context_filter.rb
66
+ - lib/sentry/sidekiq/cron/helpers.rb
67
67
  - lib/sentry/sidekiq/cron/job.rb
68
68
  - lib/sentry/sidekiq/error_handler.rb
69
69
  - lib/sentry/sidekiq/sentry_context_middleware.rb
70
70
  - lib/sentry/sidekiq/version.rb
71
71
  - sentry-sidekiq.gemspec
72
- homepage: https://github.com/getsentry/sentry-ruby/tree/5.19.0/sentry-sidekiq
72
+ homepage: https://github.com/getsentry/sentry-ruby/tree/5.28.1/sentry-sidekiq
73
73
  licenses:
74
74
  - MIT
75
75
  metadata:
76
- homepage_uri: https://github.com/getsentry/sentry-ruby/tree/5.19.0/sentry-sidekiq
77
- source_code_uri: https://github.com/getsentry/sentry-ruby/tree/5.19.0/sentry-sidekiq
78
- changelog_uri: https://github.com/getsentry/sentry-ruby/blob/5.19.0/CHANGELOG.md
76
+ homepage_uri: https://github.com/getsentry/sentry-ruby/tree/5.28.1/sentry-sidekiq
77
+ source_code_uri: https://github.com/getsentry/sentry-ruby/tree/5.28.1/sentry-sidekiq
78
+ changelog_uri: https://github.com/getsentry/sentry-ruby/blob/5.28.1/CHANGELOG.md
79
79
  bug_tracker_uri: https://github.com/getsentry/sentry-ruby/issues
80
- documentation_uri: http://www.rubydoc.info/gems/sentry-sidekiq/5.19.0
81
- post_install_message:
80
+ documentation_uri: http://www.rubydoc.info/gems/sentry-sidekiq/5.28.1
82
81
  rdoc_options: []
83
82
  require_paths:
84
83
  - lib
@@ -93,8 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
92
  - !ruby/object:Gem::Version
94
93
  version: '0'
95
94
  requirements: []
96
- rubygems_version: 3.5.11
97
- signing_key:
95
+ rubygems_version: 3.6.9
98
96
  specification_version: 4
99
97
  summary: A gem that provides Sidekiq integration for the Sentry error logger
100
98
  test_files: []