sentry-rails 4.4.0 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fba76642a6793e6026aca10630ca659fae4832d31aed0710d31805da14676c3
4
- data.tar.gz: 15a570cc7acefceeb6e601d52cd2b55763761cb9e855d21282ba75b5a05a578b
3
+ metadata.gz: 59ad5d09cc5a579e3790610a661a3ea67fea7a71c11859ad8731d67ccb57f4c9
4
+ data.tar.gz: 982e987f3905ee2b4dec3d254ddfa055cd635857307d2a4a798bf01451b7a757
5
5
  SHA512:
6
- metadata.gz: a57118b831bf7830caea725b3fdd008d4015026db29bb82dbbdccf98476a0686a795868b4d4831e7ee09cd8cbffa0adeadeb2ab646a37f37ee3c74da90f34874
7
- data.tar.gz: 0e0e165ce9d0ee629bd8df754e08615fc1ce73b68928b483ba2abe5304bfe71ee92037c617f63b594b41286f722c62bbc79f44ff116a0546ef833e749fdba81b
6
+ metadata.gz: 4afbf7ca6532f422ce2f772865287363fcefe1133589151610bfc158dfc4377d86789aff2b6fc78d8889a112ca36ad13de69e7fb60ac401a964fd8dde072c186
7
+ data.tar.gz: 792a75fce8fdf69c1cfe62ffd7250952666811d14591758ba6a8897e70691477b5b9dab1990cdea8a5641c93933bacaea0faab7fce833817de9286e82a145391
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/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Changelog
2
2
 
3
+ Individual gem's changelog has been deprecated. Please check the [project changelog](https://github.com/getsentry/sentry-ruby/blob/master/CHANGELOG.md).
4
+
3
5
  ## 4.4.0
4
6
 
5
7
  ### Features
data/Gemfile CHANGED
@@ -2,36 +2,42 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in sentry-ruby.gemspec
4
4
  gemspec
5
+ gem "sentry-ruby", path: "../sentry-ruby"
5
6
 
6
7
  rails_version = ENV["RAILS_VERSION"]
7
- 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)
8
10
 
9
11
  gem 'activerecord-jdbcmysql-adapter', platform: :jruby
10
12
  gem "jdbc-sqlite3", platform: :jruby
11
13
 
12
- if rails_version.to_f < 6
14
+ if rails_version < Gem::Version.new("6.0.0")
13
15
  gem "sqlite3", "~> 1.3.0", platform: :ruby
14
16
  else
15
17
  gem "sqlite3", platform: :ruby
16
18
  end
17
19
 
18
- 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
+
19
26
  gem "sprockets-rails"
20
27
 
28
+ gem "sidekiq"
29
+
21
30
  gem "rspec", "~> 3.0"
22
31
  gem "rspec-retry"
23
32
  gem "rspec-rails", "~> 4.0"
24
- gem "codecov", "0.2.12"
33
+ gem 'simplecov'
34
+ gem "simplecov-cobertura", "~> 1.4"
35
+ gem "rexml"
25
36
 
26
37
  gem "rake", "~> 12.0"
27
38
 
28
- # TODO: Remove this if https://github.com/jruby/jruby/issues/6547 is addressed
29
- gem "i18n", "<= 1.8.7"
30
-
31
- gem "sidekiq"
32
-
33
- gem "sentry-ruby", path: "../sentry-ruby"
34
-
39
+ gem "object_tracer"
40
+ gem "debug", github: "ruby/debug", platform: :ruby if RUBY_VERSION.to_f >= 2.6
35
41
  gem "pry"
36
42
 
37
43
  gem "benchmark-ips"
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2020 st0012
3
+ Copyright (c) 2020 Sentry
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -5,4 +5,10 @@ RSpec::Core::RakeTask.new(:spec).tap do |task|
5
5
  task.rspec_opts = "--order rand"
6
6
  end
7
7
 
8
- task :default => :spec
8
+ task :isolated_specs do
9
+ Dir["spec/isolated/*"].each do |file|
10
+ sh "bundle exec ruby #{file}"
11
+ end
12
+ end
13
+
14
+ task :default => [:spec, :isolated_specs]
@@ -16,17 +16,12 @@ 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
 
24
24
  def perform(event, hint = {})
25
- # users don't need the tracing result of this job
26
- if transaction = Sentry.get_current_scope.span
27
- transaction.instance_variable_set(:@sampled, false)
28
- end
29
-
30
25
  Sentry.send_event(event, hint)
31
26
  end
32
27
  end
@@ -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,74 +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 = Sentry.start_transaction(name: scope.transaction_name, op: "active_job")
14
+ def already_supported_by_sentry_integration?
15
+ Sentry.configuration.rails.skippable_job_adapters.include?(self.class.queue_adapter.class.to_s)
16
+ end
25
17
 
26
- 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
27
30
 
28
- block.call
31
+ scope.set_span(transaction) if transaction
29
32
 
30
- finish_transaction(transaction, 200)
31
- rescue Exception => e # rubocop:disable Lint/RescueException
32
- rescue_handler_result = rescue_with_handler(e)
33
- finish_transaction(transaction, 500)
34
- 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)
35
38
 
36
- Sentry::Rails.capture_exception(
37
- e,
38
- extra: sentry_context(job),
39
- tags: {
40
- job_id: job.job_id,
41
- provider_job_id: job.provider_job_id
42
- }
43
- )
44
- raise e
45
- 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
46
51
 
47
- def finish_transaction(transaction, status)
48
- return unless transaction
52
+ def finish_sentry_transaction(transaction, status)
53
+ return unless transaction
49
54
 
50
- transaction.set_http_status(status)
51
- transaction.finish
52
- end
55
+ transaction.set_http_status(status)
56
+ transaction.finish
57
+ end
53
58
 
54
- def already_supported_by_specific_integration?(job)
55
- Sentry.configuration.rails.skippable_job_adapters.include?(job.class.queue_adapter.class.to_s)
56
- 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
57
69
 
58
- def sentry_context(job)
59
- {
60
- active_job: job.class.name,
61
- arguments: job.arguments,
62
- scheduled_at: job.scheduled_at,
63
- job_id: job.job_id,
64
- provider_job_id: job.provider_job_id,
65
- locale: job.locale
66
- }
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
67
83
  end
68
84
  end
69
85
  end
70
86
  end
71
-
72
- class ActiveJob::Base
73
- include Sentry::Rails::ActiveJobExtensions
74
- end
@@ -1,11 +1,9 @@
1
1
  module Sentry
2
2
  class BackgroundWorker
3
- def perform(&block)
4
- @executor.post do
5
- # make sure the background worker returns AR connection if it accidentally acquire one during serialization
6
- ActiveRecord::Base.connection_pool.with_connection do
7
- block.call
8
- end
3
+ def _perform(&block)
4
+ # make sure the background worker returns AR connection if it accidentally acquire one during serialization
5
+ ActiveRecord::Base.connection_pool.with_connection do
6
+ block.call
9
7
  end
10
8
  end
11
9
  end
@@ -1,9 +1,11 @@
1
+ require "sentry/rails/instrument_payload_cleanup_helper"
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module Breadcrumb
4
6
  module ActiveSupportLogger
5
7
  class << self
6
- IGNORED_DATA_TYPES = [:request, :headers, :exception, :exception_object]
8
+ include InstrumentPayloadCleanupHelper
7
9
 
8
10
  def add(name, started, _finished, _unique_id, data)
9
11
  # skip Rails' internal events
@@ -23,12 +25,6 @@ module Sentry
23
25
  Sentry.add_breadcrumb(crumb)
24
26
  end
25
27
 
26
- def cleanup_data(data)
27
- IGNORED_DATA_TYPES.each do |key|
28
- data.delete(key) if data.key?(key)
29
- end
30
- end
31
-
32
28
  def inject
33
29
  @subscriber = ::ActiveSupport::Notifications.subscribe(/.*/) do |name, started, finished, unique_id, data|
34
30
  # we only record events that has a started timestamp
@@ -0,0 +1,44 @@
1
+ require "sentry/rails/instrument_payload_cleanup_helper"
2
+
3
+ module Sentry
4
+ module Rails
5
+ module Breadcrumb
6
+ module MonotonicActiveSupportLogger
7
+ class << self
8
+ include InstrumentPayloadCleanupHelper
9
+
10
+ def add(name, started, _finished, _unique_id, data)
11
+ # skip Rails' internal events
12
+ return if name.start_with?("!")
13
+
14
+ if data.is_a?(Hash)
15
+ # we should only mutate the copy of the data
16
+ data = data.dup
17
+ cleanup_data(data)
18
+ end
19
+
20
+ crumb = Sentry::Breadcrumb.new(
21
+ data: data,
22
+ category: name,
23
+ timestamp: started.to_i
24
+ )
25
+ Sentry.add_breadcrumb(crumb)
26
+ end
27
+
28
+ def inject
29
+ @subscriber = ::ActiveSupport::Notifications.monotonic_subscribe(/.*/) do |name, started, finished, unique_id, data|
30
+ # we only record events that has a float as started timestamp
31
+ if started.is_a?(Float)
32
+ add(name, started, finished, unique_id, data)
33
+ end
34
+ end
35
+ end
36
+
37
+ def detach
38
+ ::ActiveSupport::Notifications.unsubscribe(@subscriber)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,6 +1,7 @@
1
1
  require "sentry/rails/tracing/action_controller_subscriber"
2
2
  require "sentry/rails/tracing/action_view_subscriber"
3
3
  require "sentry/rails/tracing/active_record_subscriber"
4
+ require "sentry/rails/tracing/active_storage_subscriber"
4
5
 
5
6
  module Sentry
6
7
  class Configuration
@@ -59,7 +60,8 @@ module Sentry
59
60
  @tracing_subscribers = Set.new([
60
61
  Sentry::Rails::Tracing::ActionControllerSubscriber,
61
62
  Sentry::Rails::Tracing::ActionViewSubscriber,
62
- Sentry::Rails::Tracing::ActiveRecordSubscriber
63
+ Sentry::Rails::Tracing::ActiveRecordSubscriber,
64
+ Sentry::Rails::Tracing::ActiveStorageSubscriber
63
65
  ])
64
66
  end
65
67
  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
@@ -0,0 +1,13 @@
1
+ module Sentry
2
+ module Rails
3
+ module InstrumentPayloadCleanupHelper
4
+ IGNORED_DATA_TYPES = [:request, :response, :headers, :exception, :exception_object, Tracing::START_TIMESTAMP_NAME]
5
+
6
+ def cleanup_data(data)
7
+ IGNORED_DATA_TYPES.each do |key|
8
+ data.delete(key) if data.key?(key)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -7,9 +7,32 @@ module Sentry
7
7
  # middlewares can't be injected after initialize
8
8
  initializer "sentry.use_rack_middleware" do |app|
9
9
  # placed after all the file-sending middlewares so we can avoid unnecessary transactions
10
- app.config.middleware.insert_after ActionDispatch::Executor, Sentry::Rails::CaptureExceptions
11
- # need to be placed at last to smuggle app exceptions via env
12
- app.config.middleware.use(Sentry::Rails::RescuedExceptionInterceptor)
10
+ app.config.middleware.insert_after ActionDispatch::ShowExceptions, Sentry::Rails::CaptureExceptions
11
+ # need to place as close to DebugExceptions as possible to intercept most of the exceptions, including those raised by middlewares
12
+ app.config.middleware.insert_after ActionDispatch::DebugExceptions, Sentry::Rails::RescuedExceptionInterceptor
13
+ end
14
+
15
+ # because the extension works by registering the around_perform callcack, it should always be ran
16
+ # before the application is eager-loaded (before user's jobs register their own callbacks)
17
+ # See https://github.com/getsentry/sentry-ruby/issues/1249#issuecomment-853871871 for the detail explanation
18
+ initializer "sentry.extend_active_job", before: :eager_load! do |app|
19
+ ActiveSupport.on_load(:active_job) do
20
+ require "sentry/rails/active_job"
21
+ prepend Sentry::Rails::ActiveJobExtensions
22
+ end
23
+ end
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
13
36
  end
14
37
 
15
38
  config.after_initialize do |app|
@@ -18,12 +41,18 @@ module Sentry
18
41
  configure_project_root
19
42
  configure_trusted_proxies
20
43
  extend_controller_methods if defined?(ActionController)
21
- extend_active_job if defined?(ActiveJob)
22
44
  patch_background_worker if defined?(ActiveRecord)
23
45
  override_streaming_reporter if defined?(ActionView)
24
46
  setup_backtrace_cleanup_callback
25
47
  inject_breadcrumbs_logger
26
48
  activate_tracing
49
+
50
+ register_error_subscriber(app) if ::Rails.version.to_f >= 7.0
51
+ end
52
+
53
+ runner do
54
+ next unless Sentry.initialized?
55
+ Sentry.configuration.background_worker_threads = 0
27
56
  end
28
57
 
29
58
  def configure_project_root
@@ -34,11 +63,6 @@ module Sentry
34
63
  Sentry.configuration.trusted_proxies += Array(::Rails.application.config.action_dispatch.trusted_proxies)
35
64
  end
36
65
 
37
- def extend_active_job
38
- require "sentry/rails/active_job"
39
- ActiveJob::Base.send(:prepend, Sentry::Rails::ActiveJobExtensions)
40
- end
41
-
42
66
  def extend_controller_methods
43
67
  require "sentry/rails/controller_methods"
44
68
  require "sentry/rails/controller_transaction"
@@ -60,12 +84,19 @@ module Sentry
60
84
  require 'sentry/rails/breadcrumb/active_support_logger'
61
85
  Sentry::Rails::Breadcrumb::ActiveSupportLogger.inject
62
86
  end
87
+
88
+ if Sentry.configuration.breadcrumbs_logger.include?(:monotonic_active_support_logger)
89
+ return warn "Usage of `monotonic_active_support_logger` require a version of Rails >= 6.1, please upgrade your Rails version or use another logger" if ::Rails.version.to_f < 6.1
90
+
91
+ require 'sentry/rails/breadcrumb/monotonic_active_support_logger'
92
+ Sentry::Rails::Breadcrumb::MonotonicActiveSupportLogger.inject
93
+ end
63
94
  end
64
95
 
65
96
  def setup_backtrace_cleanup_callback
66
97
  backtrace_cleaner = Sentry::Rails::BacktraceCleaner.new
67
98
 
68
- Sentry.configuration.backtrace_cleanup_callback = lambda do |backtrace|
99
+ Sentry.configuration.backtrace_cleanup_callback ||= lambda do |backtrace|
69
100
  backtrace_cleaner.clean(backtrace)
70
101
  end
71
102
  end
@@ -86,5 +117,10 @@ module Sentry
86
117
  Sentry::Rails::Tracing.patch_active_support_notifications
87
118
  end
88
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
89
125
  end
90
126
  end
@@ -11,22 +11,6 @@ module Sentry
11
11
  begin
12
12
  @app.call(env)
13
13
  rescue => e
14
- request = ActionDispatch::Request.new(env)
15
-
16
- # Rails' ShowExceptions#render_exception will mutate env for the exceptions app
17
- # so we need to hold a copy of env to report the accurate data (like request's url)
18
- if request.show_exceptions?
19
- scope = Sentry.get_current_scope
20
- copied_env = scope.rack_env.dup
21
- copied_env["sentry.original_transaction"] = scope.transaction_name
22
- scope.set_rack_env(copied_env)
23
-
24
- if report_rescued_exceptions?
25
- Sentry::Rails.capture_exception(e)
26
- env["sentry.already_captured"] = true
27
- end
28
- end
29
-
30
14
  env["sentry.rescued_exception"] = e if report_rescued_exceptions?
31
15
  raise e
32
16
  end
@@ -9,22 +9,30 @@ module Sentry
9
9
  end
10
10
 
11
11
  def unsubscribe!
12
- ActiveSupport::Notifications.unsubscribe(self::EVENT_NAME)
12
+ self::EVENT_NAMES.each do |name|
13
+ ActiveSupport::Notifications.unsubscribe(name)
14
+ end
13
15
  end
14
16
 
15
- def subscribe_to_event(event_name)
16
- if ::Rails.version.to_i == 5
17
- ActiveSupport::Notifications.subscribe(event_name) do |*args|
18
- next unless Tracing.get_current_transaction
17
+ if ::Rails.version.to_i == 5
18
+ def subscribe_to_event(event_names)
19
+ event_names.each do |event_name|
20
+ ActiveSupport::Notifications.subscribe(event_name) do |*args|
21
+ next unless Tracing.get_current_transaction
19
22
 
20
- event = ActiveSupport::Notifications::Event.new(*args)
21
- yield(event_name, event.duration, event.payload)
23
+ event = ActiveSupport::Notifications::Event.new(*args)
24
+ yield(event_name, event.duration, event.payload)
25
+ end
22
26
  end
23
- else
24
- ActiveSupport::Notifications.subscribe(event_name) do |event|
25
- next unless Tracing.get_current_transaction
26
-
27
- yield(event_name, event.duration, event.payload)
27
+ end
28
+ else
29
+ def subscribe_to_event(event_names)
30
+ event_names.each do |event_name|
31
+ ActiveSupport::Notifications.subscribe(event_name) do |event|
32
+ next unless Tracing.get_current_transaction
33
+
34
+ yield(event_name, event.duration, event.payload)
35
+ end
28
36
  end
29
37
  end
30
38
  end
@@ -1,25 +1,27 @@
1
1
  require "sentry/rails/tracing/abstract_subscriber"
2
+ require "sentry/rails/instrument_payload_cleanup_helper"
2
3
 
3
4
  module Sentry
4
5
  module Rails
5
6
  module Tracing
6
7
  class ActionControllerSubscriber < AbstractSubscriber
7
- EVENT_NAME = "process_action.action_controller".freeze
8
+ extend InstrumentPayloadCleanupHelper
9
+
10
+ EVENT_NAMES = ["process_action.action_controller"].freeze
8
11
 
9
12
  def self.subscribe!
10
- subscribe_to_event(EVENT_NAME) do |event_name, duration, payload|
13
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
11
14
  controller = payload[:controller]
12
15
  action = payload[:action]
13
16
 
14
17
  record_on_current_span(
15
18
  op: event_name,
16
- start_timestamp: payload[:start_timestamp],
19
+ start_timestamp: payload[START_TIMESTAMP_NAME],
17
20
  description: "#{controller}##{action}",
18
21
  duration: duration
19
22
  ) do |span|
20
23
  payload = payload.dup
21
- payload.delete(:headers)
22
- payload.delete(:request)
24
+ cleanup_data(payload)
23
25
  span.set_data(:payload, payload)
24
26
  span.set_http_status(payload[:status])
25
27
  end
@@ -4,11 +4,12 @@ module Sentry
4
4
  module Rails
5
5
  module Tracing
6
6
  class ActionViewSubscriber < AbstractSubscriber
7
- EVENT_NAME = "render_template.action_view".freeze
7
+ EVENT_NAMES = ["render_template.action_view"].freeze
8
+ SPAN_PREFIX = "template.".freeze
8
9
 
9
10
  def self.subscribe!
10
- subscribe_to_event(EVENT_NAME) do |event_name, duration, payload|
11
- record_on_current_span(op: event_name, start_timestamp: payload[:start_timestamp], description: payload[:identifier], duration: duration)
11
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
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
@@ -4,14 +4,15 @@ module Sentry
4
4
  module Rails
5
5
  module Tracing
6
6
  class ActiveRecordSubscriber < AbstractSubscriber
7
- EVENT_NAME = "sql.active_record".freeze
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
- subscribe_to_event(EVENT_NAME) do |event_name, duration, payload|
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], 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
@@ -0,0 +1,34 @@
1
+ require "sentry/rails/tracing/abstract_subscriber"
2
+
3
+ module Sentry
4
+ module Rails
5
+ module Tracing
6
+ class ActiveStorageSubscriber < AbstractSubscriber
7
+ EVENT_NAMES = %w(
8
+ service_upload.active_storage
9
+ service_download.active_storage
10
+ service_streaming_download.active_storage
11
+ service_download_chunk.active_storage
12
+ service_delete.active_storage
13
+ service_delete_prefixed.active_storage
14
+ service_exist.active_storage
15
+ service_url.active_storage
16
+ service_mirror.active_storage
17
+ service_update_metadata.active_storage
18
+ preview.active_storage
19
+ analyze.active_storage
20
+ ).freeze
21
+
22
+ def self.subscribe!
23
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
24
+ record_on_current_span(op: event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:service], duration: duration) do |span|
25
+ payload.each do |key, value|
26
+ span.set_data(key, value) unless key == START_TIMESTAMP_NAME
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,6 +1,8 @@
1
1
  module Sentry
2
2
  module Rails
3
3
  module Tracing
4
+ START_TIMESTAMP_NAME = :sentry_start_timestamp
5
+
4
6
  def self.register_subscribers(subscribers)
5
7
  @subscribers = subscribers
6
8
  end
@@ -9,11 +11,19 @@ module Sentry
9
11
  @subscribers
10
12
  end
11
13
 
14
+ def self.subscribed_tracing_events
15
+ @subscribed_tracing_events ||= []
16
+ end
17
+
12
18
  def self.subscribe_tracing_events
13
19
  # need to avoid duplicated subscription
14
20
  return if @subscribed
15
21
 
16
- subscribers.each(&:subscribe!)
22
+ subscribers.each do |subscriber|
23
+ subscriber.subscribe!
24
+ @subscribed_tracing_events ||= []
25
+ @subscribed_tracing_events += subscriber::EVENT_NAMES
26
+ end
17
27
 
18
28
  @subscribed = true
19
29
  end
@@ -22,6 +32,7 @@ module Sentry
22
32
  return unless @subscribed
23
33
 
24
34
  subscribers.each(&:unsubscribe!)
35
+ subscribed_tracing_events.clear
25
36
 
26
37
  @subscribed = false
27
38
  end
@@ -35,9 +46,10 @@ module Sentry
35
46
 
36
47
  SentryNotificationExtension.module_eval do
37
48
  def instrument(name, payload = {}, &block)
38
- is_public_event = name[0] != "!"
39
-
40
- payload[:start_timestamp] = Time.now.utc.to_f if is_public_event
49
+ # only inject timestamp to the events the SDK subscribes to
50
+ if Tracing.subscribed_tracing_events.include?(name)
51
+ payload[START_TIMESTAMP_NAME] = Time.now.utc.to_f if name[0] != "!" && payload.is_a?(Hash)
52
+ end
41
53
 
42
54
  super(name, payload, &block)
43
55
  end
@@ -1,5 +1,5 @@
1
1
  module Sentry
2
2
  module Rails
3
- VERSION = "4.4.0"
3
+ VERSION = "5.1.1"
4
4
  end
5
5
  end
data/lib/sentry/rails.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require "rails"
2
2
  require "sentry-ruby"
3
3
  require "sentry/integrable"
4
+ require "sentry/rails/tracing"
4
5
  require "sentry/rails/configuration"
5
6
  require "sentry/rails/engine"
6
7
  require "sentry/rails/railtie"
7
- require "sentry/rails/tracing"
8
8
 
9
9
  module Sentry
10
10
  module Rails
data/sentry-rails.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ["Sentry Team"]
7
7
  spec.description = spec.summary = "A gem that provides Rails integration for the Sentry error logger"
8
8
  spec.email = "accounts@sentry.io"
9
- spec.license = 'Apache-2.0'
9
+ spec.license = 'MIT'
10
10
  spec.homepage = "https://github.com/getsentry/sentry-ruby"
11
11
 
12
12
  spec.platform = Gem::Platform::RUBY
@@ -16,12 +16,12 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = spec.homepage
19
- spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/sentry-rails/CHANGELOG.md"
19
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
20
20
 
21
21
  spec.bindir = "exe"
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
25
  spec.add_dependency "railties", ">= 5.0"
26
- spec.add_dependency "sentry-ruby-core", "~> 4.4.0.pre.beta"
26
+ spec.add_dependency "sentry-ruby-core", "~> 5.1.1"
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.4.0
4
+ version: 5.1.1
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-05-03 00:00:00.000000000 Z
11
+ date: 2022-02-24 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.4.0.pre.beta
33
+ version: 5.1.1
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.4.0.pre.beta
40
+ version: 5.1.1
41
41
  description: A gem that provides Rails integration for the Sentry error logger
42
42
  email: accounts@sentry.io
43
43
  executables: []
@@ -46,7 +46,6 @@ extra_rdoc_files:
46
46
  - README.md
47
47
  - LICENSE.txt
48
48
  files:
49
- - ".craft.yml"
50
49
  - ".gitignore"
51
50
  - ".rspec"
52
51
  - CHANGELOG.md
@@ -61,15 +60,19 @@ files:
61
60
  - bin/setup
62
61
  - lib/sentry-rails.rb
63
62
  - lib/sentry/rails.rb
63
+ - lib/sentry/rails/action_cable.rb
64
64
  - lib/sentry/rails/active_job.rb
65
65
  - lib/sentry/rails/background_worker.rb
66
66
  - lib/sentry/rails/backtrace_cleaner.rb
67
67
  - lib/sentry/rails/breadcrumb/active_support_logger.rb
68
+ - lib/sentry/rails/breadcrumb/monotonic_active_support_logger.rb
68
69
  - lib/sentry/rails/capture_exceptions.rb
69
70
  - lib/sentry/rails/configuration.rb
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
75
+ - lib/sentry/rails/instrument_payload_cleanup_helper.rb
73
76
  - lib/sentry/rails/overrides/streaming_reporter.rb
74
77
  - lib/sentry/rails/railtie.rb
75
78
  - lib/sentry/rails/rescued_exception_interceptor.rb
@@ -78,15 +81,16 @@ files:
78
81
  - lib/sentry/rails/tracing/action_controller_subscriber.rb
79
82
  - lib/sentry/rails/tracing/action_view_subscriber.rb
80
83
  - lib/sentry/rails/tracing/active_record_subscriber.rb
84
+ - lib/sentry/rails/tracing/active_storage_subscriber.rb
81
85
  - lib/sentry/rails/version.rb
82
86
  - sentry-rails.gemspec
83
87
  homepage: https://github.com/getsentry/sentry-ruby
84
88
  licenses:
85
- - Apache-2.0
89
+ - MIT
86
90
  metadata:
87
91
  homepage_uri: https://github.com/getsentry/sentry-ruby
88
92
  source_code_uri: https://github.com/getsentry/sentry-ruby
89
- changelog_uri: https://github.com/getsentry/sentry-ruby/blob/master/sentry-rails/CHANGELOG.md
93
+ changelog_uri: https://github.com/getsentry/sentry-ruby/blob/master/CHANGELOG.md
90
94
  post_install_message:
91
95
  rdoc_options: []
92
96
  require_paths:
@@ -102,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
106
  - !ruby/object:Gem::Version
103
107
  version: '0'
104
108
  requirements: []
105
- rubygems_version: 3.0.3.1
109
+ rubygems_version: 3.1.6
106
110
  signing_key:
107
111
  specification_version: 4
108
112
  summary: A gem that provides Rails integration for the Sentry error logger
data/.craft.yml DELETED
@@ -1,20 +0,0 @@
1
- minVersion: '0.13.2'
2
- github:
3
- owner: getsentry
4
- repo: sentry-ruby
5
- changelogPolicy: simple
6
- preReleaseCommand: ruby ../.scripts/bump-version.rb
7
- releaseBranchPrefix: release-sentry-rails
8
- statusProvider:
9
- name: github
10
- artifactProvider:
11
- name: github
12
- targets:
13
- - name: gem
14
- - name: registry
15
- type: sdk
16
- config:
17
- canonical: 'gem:sentry-rails'
18
- - name: github
19
- tagPrefix: sentry-rails-v
20
- changelog: sentry-rails/CHANGELOG.md