sentry-rails 4.8.1 → 5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5457a6fe448b94ade0388542ea975187fb7dd532d5a4ee3ec8caf65b6b2d785d
4
- data.tar.gz: a4d537a4d7c3ddebc41afdb1e00187c5a776bfa269c4342f802cc1252146d4f6
3
+ metadata.gz: a6ab84e3b98547af93706b752c0242271cc0fc684269deb3bda71318b03536a0
4
+ data.tar.gz: 324b90217b7331cb38267ccdd39c07d9a87abf52880d40741ea0435e26dec621
5
5
  SHA512:
6
- metadata.gz: 6aff15bc8465765af61e404ea7a709057531a12d018fd93624fa7f470e5d7c2c51c5105c4ef63225ec66a867b54cab57e0d8b40046f145d1a43d7b641c454a58
7
- data.tar.gz: e597ec4f143aca81cc4b3bf5aa47315eb84f02df0f93a4d402b701dfe32a811dcc2eba7c437bde430cd21d2fea98eae3ee4c3f324dedc24a63941b06bdcf82bb
6
+ metadata.gz: 153d75c83b8756638f554c0a8de42154ff1e76e13725fee6a1c576bb936b1df0ac6f84968eb5bd6da1c3348052b491355aa67f233c97db8979a0f534ee65d53d
7
+ data.tar.gz: af6b9717fee9623f8923df70aa52a1b898a9296d2a28f8624f8d1358ad0569213cdb2fd34add7742509b5e247292c6a318541c329bfa0ae64c4fda578260682c
data/.gitignore CHANGED
@@ -5,7 +5,7 @@
5
5
  /doc/
6
6
  /pkg/
7
7
  /spec/reports/
8
- /spec/support/test_rails_app/db
8
+ /spec/dummy/test_rails_app/db
9
9
  /tmp/
10
10
 
11
11
  # rspec failure tracking
data/Gemfile CHANGED
@@ -5,18 +5,24 @@ gemspec
5
5
  gem "sentry-ruby", path: "../sentry-ruby"
6
6
 
7
7
  rails_version = ENV["RAILS_VERSION"]
8
- rails_version = "6.1.0" if rails_version.nil?
8
+ rails_version = "7.0.0" if rails_version.nil?
9
+ rails_version = Gem::Version.new(rails_version)
9
10
 
10
11
  gem 'activerecord-jdbcmysql-adapter', platform: :jruby
11
12
  gem "jdbc-sqlite3", platform: :jruby
12
13
 
13
- if rails_version.to_f < 6
14
+ if rails_version < Gem::Version.new("6.0.0")
14
15
  gem "sqlite3", "~> 1.3.0", platform: :ruby
15
16
  else
16
17
  gem "sqlite3", platform: :ruby
17
18
  end
18
19
 
19
- gem "rails", "~> #{rails_version}"
20
+ if rails_version >= Gem::Version.new("7.0.0")
21
+ gem "rails", github: "rails/rails", branch: "7-0-stable"
22
+ else
23
+ gem "rails", "~> #{rails_version}"
24
+ end
25
+
20
26
  gem "sprockets-rails"
21
27
 
22
28
  gem "sidekiq"
@@ -16,8 +16,8 @@ if defined?(ActiveJob)
16
16
  discard_on ActiveJob::DeserializationError
17
17
  else
18
18
  # mimic what discard_on does for Rails 5.0
19
- rescue_from ActiveJob::DeserializationError do
20
- logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}."
19
+ rescue_from ActiveJob::DeserializationError do |exception|
20
+ logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{exception.cause.inspect}."
21
21
  end
22
22
  end
23
23
 
@@ -0,0 +1,96 @@
1
+ module Sentry
2
+ module Rails
3
+ module ActionCableExtensions
4
+ class ErrorHandler
5
+ class << self
6
+ def capture(connection, transaction_name:, extra_context: nil, &block)
7
+ return block.call unless Sentry.initialized?
8
+ # ActionCable's ConnectionStub (for testing) doesn't implement the exact same interfaces as Connection::Base.
9
+ # One thing that's missing is `env`. So calling `connection.env` direclty will fail in test environments when `stub_connection` is used.
10
+ # See https://github.com/getsentry/sentry-ruby/pull/1684 for more information.
11
+ env = connection.respond_to?(:env) ? connection.env : {}
12
+
13
+ Sentry.with_scope do |scope|
14
+ scope.set_rack_env(env)
15
+ scope.set_context("action_cable", extra_context) if extra_context
16
+ scope.set_transaction_name(transaction_name)
17
+ transaction = start_transaction(env, scope.transaction_name)
18
+ scope.set_span(transaction) if transaction
19
+
20
+ begin
21
+ block.call
22
+ finish_transaction(transaction, 200)
23
+ rescue Exception => e # rubocop:disable Lint/RescueException
24
+ Sentry::Rails.capture_exception(e)
25
+ finish_transaction(transaction, 500)
26
+
27
+ raise
28
+ end
29
+ end
30
+ end
31
+
32
+ def start_transaction(env, transaction_name)
33
+ sentry_trace = env["HTTP_SENTRY_TRACE"]
34
+ options = { name: transaction_name, op: "rails.action_cable".freeze }
35
+ transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
36
+ Sentry.start_transaction(transaction: transaction, **options)
37
+ end
38
+
39
+ def finish_transaction(transaction, status_code)
40
+ return unless transaction
41
+
42
+ transaction.set_http_status(status_code)
43
+ transaction.finish
44
+ end
45
+ end
46
+ end
47
+
48
+ module Connection
49
+ private
50
+
51
+ def handle_open
52
+ ErrorHandler.capture(self, transaction_name: "#{self.class.name}#connect") do
53
+ super
54
+ end
55
+ end
56
+
57
+ def handle_close
58
+ ErrorHandler.capture(self, transaction_name: "#{self.class.name}#disconnect") do
59
+ super
60
+ end
61
+ end
62
+ end
63
+
64
+ module Channel
65
+ module Subscriptions
66
+ def self.included(base)
67
+ base.class_eval do
68
+ set_callback :subscribe, :around, ->(_, block) { sentry_capture(:subscribed, &block) }, prepend: true
69
+ set_callback :unsubscribe, :around, ->(_, block) { sentry_capture(:unsubscribed, &block) }, prepend: true
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def sentry_capture(hook, &block)
76
+ extra_context = { params: params }
77
+
78
+ ErrorHandler.capture(connection, transaction_name: "#{self.class.name}##{hook}", extra_context: extra_context, &block)
79
+ end
80
+ end
81
+
82
+ module Actions
83
+ private
84
+
85
+ def dispatch_action(action, data)
86
+ extra_context = { params: params, data: data }
87
+
88
+ ErrorHandler.capture(connection, transaction_name: "#{self.class.name}##{action}", extra_context: extra_context) do
89
+ super
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,79 +1,86 @@
1
1
  module Sentry
2
2
  module Rails
3
3
  module ActiveJobExtensions
4
- def self.included(base)
5
- base.class_eval do
6
- around_perform do |job, block|
7
- if Sentry.initialized?
8
- if already_supported_by_specific_integration?(job)
9
- block.call
10
- else
11
- Sentry.with_scope do |scope|
12
- capture_and_reraise_with_sentry(job, scope, block)
13
- end
14
- end
15
- else
16
- block.call
17
- end
4
+ def perform_now
5
+ if !Sentry.initialized? || already_supported_by_sentry_integration?
6
+ super
7
+ else
8
+ SentryReporter.record(self) do
9
+ super
18
10
  end
19
11
  end
20
12
  end
21
13
 
22
- def capture_and_reraise_with_sentry(job, scope, block)
23
- scope.set_transaction_name(job.class.name)
24
- transaction =
25
- if job.is_a?(::Sentry::SendEventJob)
26
- nil
27
- else
28
- Sentry.start_transaction(name: scope.transaction_name, op: "active_job")
29
- end
14
+ def already_supported_by_sentry_integration?
15
+ Sentry.configuration.rails.skippable_job_adapters.include?(self.class.queue_adapter.class.to_s)
16
+ end
30
17
 
31
- scope.set_span(transaction) if transaction
18
+ class SentryReporter
19
+ class << self
20
+ def record(job, &block)
21
+ Sentry.with_scope do |scope|
22
+ begin
23
+ scope.set_transaction_name(job.class.name)
24
+ transaction =
25
+ if job.is_a?(::Sentry::SendEventJob)
26
+ nil
27
+ else
28
+ Sentry.start_transaction(name: scope.transaction_name, op: "active_job")
29
+ end
32
30
 
33
- block.call
31
+ scope.set_span(transaction) if transaction
34
32
 
35
- finish_transaction(transaction, 200)
36
- rescue Exception => e # rubocop:disable Lint/RescueException
37
- rescue_handler_result = rescue_with_handler(e)
38
- finish_transaction(transaction, 500)
39
- return rescue_handler_result if rescue_handler_result
33
+ yield.tap do
34
+ finish_sentry_transaction(transaction, 200)
35
+ end
36
+ rescue Exception => e # rubocop:disable Lint/RescueException
37
+ finish_sentry_transaction(transaction, 500)
40
38
 
41
- Sentry::Rails.capture_exception(
42
- e,
43
- extra: sentry_context(job),
44
- tags: {
45
- job_id: job.job_id,
46
- provider_job_id: job.provider_job_id
47
- }
48
- )
49
- raise e
50
- end
39
+ Sentry::Rails.capture_exception(
40
+ e,
41
+ extra: sentry_context(job),
42
+ tags: {
43
+ job_id: job.job_id,
44
+ provider_job_id: job.provider_job_id
45
+ }
46
+ )
47
+ raise
48
+ end
49
+ end
50
+ end
51
51
 
52
- def finish_transaction(transaction, status)
53
- return unless transaction
52
+ def finish_sentry_transaction(transaction, status)
53
+ return unless transaction
54
54
 
55
- transaction.set_http_status(status)
56
- transaction.finish
57
- end
55
+ transaction.set_http_status(status)
56
+ transaction.finish
57
+ end
58
58
 
59
- def already_supported_by_specific_integration?(job)
60
- Sentry.configuration.rails.skippable_job_adapters.include?(job.class.queue_adapter.class.to_s)
61
- end
59
+ def sentry_context(job)
60
+ {
61
+ active_job: job.class.name,
62
+ arguments: sentry_serialize_arguments(job.arguments),
63
+ scheduled_at: job.scheduled_at,
64
+ job_id: job.job_id,
65
+ provider_job_id: job.provider_job_id,
66
+ locale: job.locale
67
+ }
68
+ end
62
69
 
63
- def sentry_context(job)
64
- {
65
- active_job: job.class.name,
66
- arguments: job.arguments,
67
- scheduled_at: job.scheduled_at,
68
- job_id: job.job_id,
69
- provider_job_id: job.provider_job_id,
70
- locale: job.locale
71
- }
70
+ def sentry_serialize_arguments(argument)
71
+ case argument
72
+ when Hash
73
+ argument.transform_values { |v| sentry_serialize_arguments(v) }
74
+ when Array, Enumerable
75
+ argument.map { |v| sentry_serialize_arguments(v) }
76
+ when ->(v) { v.respond_to?(:to_global_id) }
77
+ argument.to_global_id.to_s rescue argument
78
+ else
79
+ argument
80
+ end
81
+ end
82
+ end
72
83
  end
73
84
  end
74
85
  end
75
86
  end
76
-
77
- class ActiveJob::Base
78
- include Sentry::Rails::ActiveJobExtensions
79
- end
@@ -0,0 +1,11 @@
1
+ module Sentry
2
+ module Rails
3
+ # This is not a user-facing class. You should use it with Rails 7.0's error reporter feature and its interfaces.
4
+ # See https://github.com/rails/rails/blob/main/activesupport/lib/active_support/error_reporter.rb for more information.
5
+ class ErrorSubscriber
6
+ def report(error, handled:, severity:, context:)
7
+ Sentry::Rails.capture_exception(error, level: severity, contexts: { "rails.error" => context }, tags: { handled: handled })
8
+ end
9
+ end
10
+ end
11
+ end
@@ -22,6 +22,19 @@ module Sentry
22
22
  end
23
23
  end
24
24
 
25
+ initializer "sentry.extend_action_cable", before: :eager_load! do |app|
26
+ ActiveSupport.on_load(:action_cable_connection) do
27
+ require "sentry/rails/action_cable"
28
+ prepend Sentry::Rails::ActionCableExtensions::Connection
29
+ end
30
+
31
+ ActiveSupport.on_load(:action_cable_channel) do
32
+ require "sentry/rails/action_cable"
33
+ include Sentry::Rails::ActionCableExtensions::Channel::Subscriptions
34
+ prepend Sentry::Rails::ActionCableExtensions::Channel::Actions
35
+ end
36
+ end
37
+
25
38
  config.after_initialize do |app|
26
39
  next unless Sentry.initialized?
27
40
 
@@ -33,6 +46,8 @@ module Sentry
33
46
  setup_backtrace_cleanup_callback
34
47
  inject_breadcrumbs_logger
35
48
  activate_tracing
49
+
50
+ register_error_subscriber(app) if ::Rails.version.to_f >= 7.0
36
51
  end
37
52
 
38
53
  runner do
@@ -102,5 +117,10 @@ module Sentry
102
117
  Sentry::Rails::Tracing.patch_active_support_notifications
103
118
  end
104
119
  end
120
+
121
+ def register_error_subscriber(app)
122
+ require "sentry/rails/error_subscriber"
123
+ app.executor.error_reporter.subscribe(Sentry::Rails::ErrorSubscriber.new)
124
+ end
105
125
  end
106
126
  end
@@ -5,10 +5,11 @@ module Sentry
5
5
  module Tracing
6
6
  class ActionViewSubscriber < AbstractSubscriber
7
7
  EVENT_NAMES = ["render_template.action_view"].freeze
8
+ SPAN_PREFIX = "template.".freeze
8
9
 
9
10
  def self.subscribe!
10
11
  subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
11
- record_on_current_span(op: event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:identifier], duration: duration)
12
+ record_on_current_span(op: SPAN_PREFIX + event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:identifier], duration: duration)
12
13
  end
13
14
  end
14
15
  end
@@ -5,13 +5,14 @@ module Sentry
5
5
  module Tracing
6
6
  class ActiveRecordSubscriber < AbstractSubscriber
7
7
  EVENT_NAMES = ["sql.active_record"].freeze
8
+ SPAN_PREFIX = "db.".freeze
8
9
  EXCLUDED_EVENTS = ["SCHEMA", "TRANSACTION"].freeze
9
10
 
10
11
  def self.subscribe!
11
12
  subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
12
13
  next if EXCLUDED_EVENTS.include? payload[:name]
13
14
 
14
- record_on_current_span(op: event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:sql], duration: duration) do |span|
15
+ record_on_current_span(op: SPAN_PREFIX + event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:sql], duration: duration) do |span|
15
16
  span.set_data(:connection_id, payload[:connection_id])
16
17
  end
17
18
  end
@@ -1,5 +1,5 @@
1
1
  module Sentry
2
2
  module Rails
3
- VERSION = "4.8.1"
3
+ VERSION = "5.2.0"
4
4
  end
5
5
  end
data/sentry-rails.gemspec CHANGED
@@ -23,5 +23,5 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ["lib"]
24
24
 
25
25
  spec.add_dependency "railties", ">= 5.0"
26
- spec.add_dependency "sentry-ruby-core", "~> 4.8.1"
26
+ spec.add_dependency "sentry-ruby-core", "~> 5.2.0"
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.1
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-22 00:00:00.000000000 Z
11
+ date: 2022-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 4.8.1
33
+ version: 5.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 4.8.1
40
+ version: 5.2.0
41
41
  description: A gem that provides Rails integration for the Sentry error logger
42
42
  email: accounts@sentry.io
43
43
  executables: []
@@ -60,6 +60,7 @@ files:
60
60
  - bin/setup
61
61
  - lib/sentry-rails.rb
62
62
  - lib/sentry/rails.rb
63
+ - lib/sentry/rails/action_cable.rb
63
64
  - lib/sentry/rails/active_job.rb
64
65
  - lib/sentry/rails/background_worker.rb
65
66
  - lib/sentry/rails/backtrace_cleaner.rb
@@ -70,6 +71,7 @@ files:
70
71
  - lib/sentry/rails/controller_methods.rb
71
72
  - lib/sentry/rails/controller_transaction.rb
72
73
  - lib/sentry/rails/engine.rb
74
+ - lib/sentry/rails/error_subscriber.rb
73
75
  - lib/sentry/rails/instrument_payload_cleanup_helper.rb
74
76
  - lib/sentry/rails/overrides/streaming_reporter.rb
75
77
  - lib/sentry/rails/railtie.rb