sentry-rails 5.10.0 → 5.26.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Gemfile +36 -31
  4. data/README.md +1 -1
  5. data/Rakefile +13 -10
  6. data/app/jobs/sentry/send_event_job.rb +2 -1
  7. data/bin/console +1 -0
  8. data/lib/generators/sentry_generator.rb +31 -0
  9. data/lib/sentry/rails/action_cable.rb +12 -6
  10. data/lib/sentry/rails/active_job.rb +71 -10
  11. data/lib/sentry/rails/background_worker.rb +2 -0
  12. data/lib/sentry/rails/backtrace_cleaner.rb +7 -7
  13. data/lib/sentry/rails/breadcrumb/active_support_logger.rb +6 -64
  14. data/lib/sentry/rails/breadcrumb/monotonic_active_support_logger.rb +2 -0
  15. data/lib/sentry/rails/capture_exceptions.rb +11 -6
  16. data/lib/sentry/rails/configuration.rb +107 -18
  17. data/lib/sentry/rails/controller_methods.rb +2 -0
  18. data/lib/sentry/rails/controller_transaction.rb +9 -3
  19. data/lib/sentry/rails/engine.rb +2 -0
  20. data/lib/sentry/rails/error_subscriber.rb +9 -1
  21. data/lib/sentry/rails/instrument_payload_cleanup_helper.rb +2 -0
  22. data/lib/sentry/rails/overrides/streaming_reporter.rb +2 -0
  23. data/lib/sentry/rails/railtie.rb +17 -4
  24. data/lib/sentry/rails/rescued_exception_interceptor.rb +12 -1
  25. data/lib/sentry/rails/tracing/abstract_subscriber.rb +2 -1
  26. data/lib/sentry/rails/tracing/action_controller_subscriber.rb +6 -2
  27. data/lib/sentry/rails/tracing/action_view_subscriber.rb +11 -2
  28. data/lib/sentry/rails/tracing/active_record_subscriber.rb +89 -7
  29. data/lib/sentry/rails/tracing/active_storage_subscriber.rb +17 -4
  30. data/lib/sentry/rails/tracing/active_support_subscriber.rb +63 -0
  31. data/lib/sentry/rails/tracing.rb +2 -0
  32. data/lib/sentry/rails/version.rb +3 -1
  33. data/lib/sentry/rails.rb +2 -0
  34. data/lib/sentry-rails.rb +2 -0
  35. data/sentry-rails.gemspec +14 -6
  36. metadata +14 -13
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/tracing/action_controller_subscriber"
2
4
  require "sentry/rails/tracing/action_view_subscriber"
3
5
  require "sentry/rails/tracing/active_record_subscriber"
4
6
  require "sentry/rails/tracing/active_storage_subscriber"
7
+ require "sentry/rails/tracing/active_support_subscriber"
5
8
 
6
9
  module Sentry
7
10
  class Configuration
@@ -12,9 +15,14 @@ module Sentry
12
15
  @excluded_exceptions = @excluded_exceptions.concat(Sentry::Rails::IGNORE_DEFAULT)
13
16
 
14
17
  if ::Rails.logger
15
- @logger = ::Rails.logger
18
+ if defined?(::ActiveSupport::BroadcastLogger) && ::Rails.logger.is_a?(::ActiveSupport::BroadcastLogger)
19
+ dupped_broadcasts = ::Rails.logger.broadcasts.map(&:dup)
20
+ self.sdk_logger = ::ActiveSupport::BroadcastLogger.new(*dupped_broadcasts)
21
+ else
22
+ self.sdk_logger = ::Rails.logger.dup
23
+ end
16
24
  else
17
- @logger.warn(Sentry::LOGGER_PROGNAME) do
25
+ sdk_logger.warn(Sentry::LOGGER_PROGNAME) do
18
26
  <<~MSG
19
27
  sentry-rails can't detect Rails.logger. it may be caused by misplacement of the SDK initialization code
20
28
  please make sure you place the Sentry.init block under the `config/initializers` folder, e.g. `config/initializers/sentry.rb`
@@ -26,21 +34,82 @@ module Sentry
26
34
 
27
35
  module Rails
28
36
  IGNORE_DEFAULT = [
29
- 'AbstractController::ActionNotFound',
30
- 'ActionController::BadRequest',
31
- 'ActionController::InvalidAuthenticityToken',
32
- 'ActionController::InvalidCrossOriginRequest',
33
- 'ActionController::MethodNotAllowed',
34
- 'ActionController::NotImplemented',
35
- 'ActionController::ParameterMissing',
36
- 'ActionController::RoutingError',
37
- 'ActionController::UnknownAction',
38
- 'ActionController::UnknownFormat',
39
- 'ActionDispatch::Http::MimeNegotiation::InvalidType',
40
- 'ActionController::UnknownHttpMethod',
41
- 'ActionDispatch::Http::Parameters::ParseError',
42
- 'ActiveRecord::RecordNotFound'
37
+ "AbstractController::ActionNotFound",
38
+ "ActionController::BadRequest",
39
+ "ActionController::InvalidAuthenticityToken",
40
+ "ActionController::InvalidCrossOriginRequest",
41
+ "ActionController::MethodNotAllowed",
42
+ "ActionController::NotImplemented",
43
+ "ActionController::ParameterMissing",
44
+ "ActionController::RoutingError",
45
+ "ActionController::UnknownAction",
46
+ "ActionController::UnknownFormat",
47
+ "ActionDispatch::Http::MimeNegotiation::InvalidType",
48
+ "ActionController::UnknownHttpMethod",
49
+ "ActionDispatch::Http::Parameters::ParseError",
50
+ "ActiveRecord::RecordNotFound"
43
51
  ].freeze
52
+
53
+ ACTIVE_SUPPORT_LOGGER_SUBSCRIPTION_ITEMS_DEFAULT = {
54
+ # action_controller
55
+ "write_fragment.action_controller" => %i[key],
56
+ "read_fragment.action_controller" => %i[key],
57
+ "exist_fragment?.action_controller" => %i[key],
58
+ "expire_fragment.action_controller" => %i[key],
59
+ "start_processing.action_controller" => %i[controller action params format method path],
60
+ "process_action.action_controller" => %i[controller action params format method path status view_runtime db_runtime],
61
+ "send_file.action_controller" => %i[path],
62
+ "redirect_to.action_controller" => %i[status location],
63
+ "halted_callback.action_controller" => %i[filter],
64
+ # action_dispatch
65
+ "process_middleware.action_dispatch" => %i[middleware],
66
+ # action_view
67
+ "render_template.action_view" => %i[identifier layout],
68
+ "render_partial.action_view" => %i[identifier],
69
+ "render_collection.action_view" => %i[identifier count cache_hits],
70
+ "render_layout.action_view" => %i[identifier],
71
+ # active_record
72
+ "sql.active_record" => %i[sql name statement_name cached],
73
+ "instantiation.active_record" => %i[record_count class_name],
74
+ # action_mailer
75
+ # not including to, from, or subject..etc. because of PII concern
76
+ "deliver.action_mailer" => %i[mailer date perform_deliveries],
77
+ "process.action_mailer" => %i[mailer action params],
78
+ # active_support
79
+ "cache_read.active_support" => %i[key store hit],
80
+ "cache_generate.active_support" => %i[key store],
81
+ "cache_fetch_hit.active_support" => %i[key store],
82
+ "cache_write.active_support" => %i[key store],
83
+ "cache_delete.active_support" => %i[key store],
84
+ "cache_exist?.active_support" => %i[key store],
85
+ # active_job
86
+ "enqueue_at.active_job" => %i[],
87
+ "enqueue.active_job" => %i[],
88
+ "enqueue_retry.active_job" => %i[],
89
+ "perform_start.active_job" => %i[],
90
+ "perform.active_job" => %i[],
91
+ "retry_stopped.active_job" => %i[],
92
+ "discard.active_job" => %i[],
93
+ # action_cable
94
+ "perform_action.action_cable" => %i[channel_class action],
95
+ "transmit.action_cable" => %i[channel_class],
96
+ "transmit_subscription_confirmation.action_cable" => %i[channel_class],
97
+ "transmit_subscription_rejection.action_cable" => %i[channel_class],
98
+ "broadcast.action_cable" => %i[broadcasting],
99
+ # active_storage
100
+ "service_upload.active_storage" => %i[service key checksum],
101
+ "service_streaming_download.active_storage" => %i[service key],
102
+ "service_download_chunk.active_storage" => %i[service key],
103
+ "service_download.active_storage" => %i[service key],
104
+ "service_delete.active_storage" => %i[service key],
105
+ "service_delete_prefixed.active_storage" => %i[service prefix],
106
+ "service_exist.active_storage" => %i[service key exist],
107
+ "service_url.active_storage" => %i[service key url],
108
+ "service_update_metadata.active_storage" => %i[service key],
109
+ "preview.active_storage" => %i[key],
110
+ "analyze.active_storage" => %i[analyzer]
111
+ }.freeze
112
+
44
113
  class Configuration
45
114
  # Rails 7.0 introduced a new error reporter feature, which the SDK once opted-in by default.
46
115
  # But after receiving multiple issue reports, the integration seemed to cause serious troubles to some users.
@@ -50,8 +119,8 @@ module Sentry
50
119
 
51
120
  # Rails catches exceptions in the ActionDispatch::ShowExceptions or
52
121
  # ActionDispatch::DebugExceptions middlewares, depending on the environment.
53
- # When `rails_report_rescued_exceptions` is true (it is by default), Sentry
54
- # will report exceptions even when they are rescued by these middlewares.
122
+ # When `report_rescued_exceptions` is true (it is by default), Sentry will
123
+ # report exceptions even when they are rescued by these middlewares.
55
124
  attr_accessor :report_rescued_exceptions
56
125
 
57
126
  # Some adapters, like sidekiq, already have their own sentry integration.
@@ -60,6 +129,14 @@ module Sentry
60
129
 
61
130
  attr_accessor :tracing_subscribers
62
131
 
132
+ # When the ActiveRecordSubscriber is enabled, capture the source location of the query in the span data.
133
+ # This is enabled by default, but can be disabled by setting this to false.
134
+ attr_accessor :enable_db_query_source
135
+
136
+ # The threshold in milliseconds for the ActiveRecordSubscriber to capture the source location of the query
137
+ # in the span data. Default is 100ms.
138
+ attr_accessor :db_query_source_threshold_ms
139
+
63
140
  # sentry-rails by default skips asset request' transactions by checking if the path matches
64
141
  #
65
142
  # ```rb
@@ -75,6 +152,13 @@ module Sentry
75
152
  # ```
76
153
  attr_accessor :assets_regexp
77
154
 
155
+ # Hash of subscription items that will be shown in breadcrumbs active support logger.
156
+ # @return [Hash<String, Array<Symbol>>]
157
+ attr_accessor :active_support_logger_subscription_items
158
+
159
+ # Set this option to true if you want Sentry to capture each retry failure
160
+ attr_accessor :active_job_report_on_retry_error
161
+
78
162
  def initialize
79
163
  @register_error_subscriber = false
80
164
  @report_rescued_exceptions = true
@@ -84,9 +168,14 @@ module Sentry
84
168
  end
85
169
  @tracing_subscribers = Set.new([
86
170
  Sentry::Rails::Tracing::ActionViewSubscriber,
171
+ Sentry::Rails::Tracing::ActiveSupportSubscriber,
87
172
  Sentry::Rails::Tracing::ActiveRecordSubscriber,
88
173
  Sentry::Rails::Tracing::ActiveStorageSubscriber
89
174
  ])
175
+ @enable_db_query_source = true
176
+ @db_query_source_threshold_ms = 100
177
+ @active_support_logger_subscription_items = Sentry::Rails::ACTIVE_SUPPORT_LOGGER_SUBSCRIPTION_ITEMS_DEFAULT.dup
178
+ @active_job_report_on_retry_error = false
90
179
  end
91
180
  end
92
181
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module ControllerMethods
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module ControllerTransaction
6
+ SPAN_ORIGIN = "auto.view.rails"
7
+
4
8
  def self.included(base)
5
9
  base.prepend_around_action(:sentry_around_action)
6
10
  end
@@ -11,7 +15,7 @@ module Sentry
11
15
  if Sentry.initialized?
12
16
  transaction_name = "#{self.class}##{action_name}"
13
17
  Sentry.get_current_scope.set_transaction_name(transaction_name, source: :view)
14
- Sentry.with_child_span(op: "view.process_action.action_controller", description: transaction_name) do |child_span|
18
+ Sentry.with_child_span(op: "view.process_action.action_controller", description: transaction_name, origin: SPAN_ORIGIN) do |child_span|
15
19
  if child_span
16
20
  begin
17
21
  result = yield
@@ -19,8 +23,10 @@ module Sentry
19
23
  child_span.set_http_status(response.status)
20
24
  child_span.set_data(:format, request.format)
21
25
  child_span.set_data(:method, request.method)
22
- child_span.set_data(:path, request.path)
23
- child_span.set_data(:params, request.params)
26
+
27
+ pii = Sentry.configuration.send_default_pii
28
+ child_span.set_data(:path, pii ? request.fullpath : request.filtered_path)
29
+ child_span.set_data(:params, pii ? request.params : request.filtered_parameters)
24
30
  end
25
31
 
26
32
  result
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace Sentry
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  # This is not a user-facing class. You should use it with Rails 7.0's error reporter feature and its interfaces.
@@ -19,7 +21,13 @@ module Sentry
19
21
  tags.merge!(context.delete(:tags))
20
22
  end
21
23
 
22
- Sentry::Rails.capture_exception(error, level: severity, contexts: { "rails.error" => context }, tags: tags)
24
+ hint = {}
25
+ if context[:hint].is_a?(Hash)
26
+ context = context.dup
27
+ hint.merge!(context.delete(:hint))
28
+ end
29
+
30
+ Sentry::Rails.capture_exception(error, level: severity, contexts: { "rails.error" => context }, tags: tags, hint: hint)
23
31
  end
24
32
  end
25
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module InstrumentPayloadCleanupHelper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module Overrides
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/capture_exceptions"
2
4
  require "sentry/rails/rescued_exception_interceptor"
3
5
  require "sentry/rails/backtrace_cleaner"
@@ -12,7 +14,7 @@ module Sentry
12
14
  app.config.middleware.insert_after ActionDispatch::DebugExceptions, Sentry::Rails::RescuedExceptionInterceptor
13
15
  end
14
16
 
15
- # because the extension works by registering the around_perform callcack, it should always be ran
17
+ # because the extension works by registering the around_perform callback, it should always be run
16
18
  # before the application is eager-loaded (before user's jobs register their own callbacks)
17
19
  # See https://github.com/getsentry/sentry-ruby/issues/1249#issuecomment-853871871 for the detail explanation
18
20
  initializer "sentry.extend_active_job", before: :eager_load! do |app|
@@ -40,6 +42,7 @@ module Sentry
40
42
 
41
43
  configure_project_root
42
44
  configure_trusted_proxies
45
+ configure_cron_timezone
43
46
  extend_controller_methods if defined?(ActionController)
44
47
  patch_background_worker if defined?(ActiveRecord)
45
48
  override_streaming_reporter if defined?(ActionView)
@@ -48,6 +51,11 @@ module Sentry
48
51
  activate_tracing
49
52
 
50
53
  register_error_subscriber(app) if ::Rails.version.to_f >= 7.0 && Sentry.configuration.rails.register_error_subscriber
54
+
55
+ # Presence of ActiveJob is no longer a reliable cue
56
+ if defined?(Sentry::Rails::ActiveJobExtensions)
57
+ Sentry::Rails::ActiveJobExtensions::SentryReporter.register_event_handlers
58
+ end
51
59
  end
52
60
 
53
61
  runner do
@@ -70,6 +78,11 @@ module Sentry
70
78
  Sentry.configuration.trusted_proxies += Array(::Rails.application.config.action_dispatch.trusted_proxies)
71
79
  end
72
80
 
81
+ def configure_cron_timezone
82
+ tz_info = ::ActiveSupport::TimeZone.find_tzinfo(::Rails.application.config.time_zone)
83
+ Sentry.configuration.cron.default_timezone = tz_info.name
84
+ end
85
+
73
86
  def extend_controller_methods
74
87
  require "sentry/rails/controller_methods"
75
88
  require "sentry/rails/controller_transaction"
@@ -88,14 +101,14 @@ module Sentry
88
101
 
89
102
  def inject_breadcrumbs_logger
90
103
  if Sentry.configuration.breadcrumbs_logger.include?(:active_support_logger)
91
- require 'sentry/rails/breadcrumb/active_support_logger'
92
- Sentry::Rails::Breadcrumb::ActiveSupportLogger.inject
104
+ require "sentry/rails/breadcrumb/active_support_logger"
105
+ Sentry::Rails::Breadcrumb::ActiveSupportLogger.inject(Sentry.configuration.rails.active_support_logger_subscription_items)
93
106
  end
94
107
 
95
108
  if Sentry.configuration.breadcrumbs_logger.include?(:monotonic_active_support_logger)
96
109
  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
97
110
 
98
- require 'sentry/rails/breadcrumb/monotonic_active_support_logger'
111
+ require "sentry/rails/breadcrumb/monotonic_active_support_logger"
99
112
  Sentry::Rails::Breadcrumb::MonotonicActiveSupportLogger.inject
100
113
  end
101
114
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  class RescuedExceptionInterceptor
@@ -17,7 +19,16 @@ module Sentry
17
19
  end
18
20
 
19
21
  def report_rescued_exceptions?
20
- Sentry.configuration.rails.report_rescued_exceptions
22
+ # In rare edge cases, `Sentry.configuration` might be `nil` here.
23
+ # Hence, we use a safe navigation and fallback to a reasonable default
24
+ # of `true` in case the configuration couldn't be loaded.
25
+ # See https://github.com/getsentry/sentry-ruby/issues/2386
26
+ report_rescued_exceptions = Sentry.configuration&.rails&.report_rescued_exceptions
27
+ return report_rescued_exceptions unless report_rescued_exceptions.nil?
28
+
29
+ # `true` is the default for `report_rescued_exceptions`, as specified in
30
+ # `sentry-rails/lib/sentry/rails/configuration.rb`.
31
+ true
21
32
  end
22
33
  end
23
34
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module Tracing
4
6
  class AbstractSubscriber
5
-
6
7
  class << self
7
8
  def subscribe!
8
9
  raise NotImplementedError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/tracing/abstract_subscriber"
2
4
  require "sentry/rails/instrument_payload_cleanup_helper"
3
5
 
@@ -8,10 +10,11 @@ module Sentry
8
10
  extend InstrumentPayloadCleanupHelper
9
11
 
10
12
  EVENT_NAMES = ["process_action.action_controller"].freeze
11
- OP_NAME = "view.process_action.action_controller".freeze
13
+ OP_NAME = "view.process_action.action_controller"
14
+ SPAN_ORIGIN = "auto.view.rails"
12
15
 
13
16
  def self.subscribe!
14
- Sentry.logger.warn <<~MSG
17
+ Sentry.sdk_logger.warn <<~MSG
15
18
  DEPRECATION WARNING: sentry-rails has changed its approach on controller span recording and #{self.name} is now depreacted.
16
19
  Please stop using or referencing #{self.name} as it will be removed in the next major release.
17
20
  MSG
@@ -22,6 +25,7 @@ module Sentry
22
25
 
23
26
  record_on_current_span(
24
27
  op: OP_NAME,
28
+ origin: SPAN_ORIGIN,
25
29
  start_timestamp: payload[START_TIMESTAMP_NAME],
26
30
  description: "#{controller}##{action}",
27
31
  duration: duration
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/tracing/abstract_subscriber"
2
4
 
3
5
  module Sentry
@@ -5,11 +7,18 @@ module Sentry
5
7
  module Tracing
6
8
  class ActionViewSubscriber < AbstractSubscriber
7
9
  EVENT_NAMES = ["render_template.action_view"].freeze
8
- SPAN_PREFIX = "template.".freeze
10
+ SPAN_PREFIX = "template."
11
+ SPAN_ORIGIN = "auto.template.rails"
9
12
 
10
13
  def self.subscribe!
11
14
  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)
15
+ record_on_current_span(
16
+ op: SPAN_PREFIX + event_name,
17
+ origin: SPAN_ORIGIN,
18
+ start_timestamp: payload[START_TIMESTAMP_NAME],
19
+ description: payload[:identifier],
20
+ duration: duration
21
+ )
13
22
  end
14
23
  end
15
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/tracing/abstract_subscriber"
2
4
 
3
5
  module Sentry
@@ -5,16 +7,96 @@ module Sentry
5
7
  module Tracing
6
8
  class ActiveRecordSubscriber < AbstractSubscriber
7
9
  EVENT_NAMES = ["sql.active_record"].freeze
8
- SPAN_PREFIX = "db.".freeze
10
+ SPAN_PREFIX = "db."
11
+ SPAN_ORIGIN = "auto.db.rails"
9
12
  EXCLUDED_EVENTS = ["SCHEMA", "TRANSACTION"].freeze
10
13
 
11
- def self.subscribe!
12
- subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
13
- next if EXCLUDED_EVENTS.include? payload[:name]
14
+ SUPPORT_SOURCE_LOCATION = ActiveSupport::BacktraceCleaner.method_defined?(:clean_frame)
15
+
16
+ if SUPPORT_SOURCE_LOCATION
17
+ class_attribute :backtrace_cleaner, default: (ActiveSupport::BacktraceCleaner.new.tap do |cleaner|
18
+ cleaner.add_silencer { |line| line.include?("sentry-ruby/lib") || line.include?("sentry-rails/lib") }
19
+ end)
20
+ end
21
+
22
+ class << self
23
+ def subscribe!
24
+ record_query_source = SUPPORT_SOURCE_LOCATION && Sentry.configuration.rails.enable_db_query_source
25
+ query_source_threshold = Sentry.configuration.rails.db_query_source_threshold_ms
26
+
27
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
28
+ next if EXCLUDED_EVENTS.include? payload[:name]
29
+
30
+ record_on_current_span(
31
+ op: SPAN_PREFIX + event_name,
32
+ origin: SPAN_ORIGIN,
33
+ start_timestamp: payload[START_TIMESTAMP_NAME],
34
+ description: payload[:sql],
35
+ duration: duration
36
+ ) do |span|
37
+ span.set_tag(:cached, true) if payload.fetch(:cached, false) # cached key is only set for hits in the QueryCache, from Rails 5.1
38
+
39
+ connection = payload[:connection]
40
+
41
+ if payload[:connection_id]
42
+ span.set_data(:connection_id, payload[:connection_id])
43
+
44
+ # we fallback to the base connection on rails < 6.0.0 since the payload doesn't have it
45
+ connection ||= ActiveRecord::Base.connection_pool.connections.find { |conn| conn.object_id == payload[:connection_id] }
46
+ end
47
+
48
+ next unless connection
49
+
50
+ db_config =
51
+ if connection.pool.respond_to?(:db_config)
52
+ connection.pool.db_config.configuration_hash
53
+ elsif connection.pool.respond_to?(:spec)
54
+ connection.pool.spec.config
55
+ end
14
56
 
15
- record_on_current_span(op: SPAN_PREFIX + event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:sql], duration: duration) do |span|
16
- span.set_data(:connection_id, payload[:connection_id])
17
- span.set_tag(:cached, true) if payload.fetch(:cached, false) # cached key is only set for hits in the QueryCache, from Rails 5.1
57
+ next unless db_config
58
+
59
+ span.set_data(Span::DataConventions::DB_SYSTEM, db_config[:adapter]) if db_config[:adapter]
60
+ span.set_data(Span::DataConventions::DB_NAME, db_config[:database]) if db_config[:database]
61
+ span.set_data(Span::DataConventions::SERVER_ADDRESS, db_config[:host]) if db_config[:host]
62
+ span.set_data(Span::DataConventions::SERVER_PORT, db_config[:port]) if db_config[:port]
63
+ span.set_data(Span::DataConventions::SERVER_SOCKET_ADDRESS, db_config[:socket]) if db_config[:socket]
64
+
65
+ next unless record_query_source
66
+
67
+ # both duration and query_source_threshold are in ms
68
+ next unless duration >= query_source_threshold
69
+
70
+ source_location = query_source_location
71
+
72
+ if source_location
73
+ backtrace_line = Sentry::Backtrace::Line.parse(source_location)
74
+
75
+ span.set_data(Span::DataConventions::FILEPATH, backtrace_line.file) if backtrace_line.file
76
+ span.set_data(Span::DataConventions::LINENO, backtrace_line.number) if backtrace_line.number
77
+ span.set_data(Span::DataConventions::FUNCTION, backtrace_line.method) if backtrace_line.method
78
+ # Only JRuby has namespace in the backtrace
79
+ span.set_data(Span::DataConventions::NAMESPACE, backtrace_line.module_name) if backtrace_line.module_name
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # Thread.each_caller_location is an API added in Ruby 3.2 that doesn't always collect the entire stack like
86
+ # Kernel#caller or #caller_locations do. See https://github.com/rails/rails/pull/49095 for more context.
87
+ if SUPPORT_SOURCE_LOCATION && Thread.respond_to?(:each_caller_location)
88
+ def query_source_location
89
+ Thread.each_caller_location do |location|
90
+ frame = backtrace_cleaner.clean_frame(location)
91
+ return frame if frame
92
+ end
93
+ nil
94
+ end
95
+ else
96
+ # Since Sentry is mostly used in production, we don't want to fallback to the slower implementation
97
+ # and adds potentially big overhead to the application.
98
+ def query_source_location
99
+ nil
18
100
  end
19
101
  end
20
102
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/tracing/abstract_subscriber"
2
4
 
3
5
  module Sentry
4
6
  module Rails
5
7
  module Tracing
6
8
  class ActiveStorageSubscriber < AbstractSubscriber
7
- EVENT_NAMES = %w(
9
+ EVENT_NAMES = %w[
8
10
  service_upload.active_storage
9
11
  service_download.active_storage
10
12
  service_streaming_download.active_storage
@@ -17,13 +19,24 @@ module Sentry
17
19
  service_update_metadata.active_storage
18
20
  preview.active_storage
19
21
  analyze.active_storage
20
- ).freeze
22
+ ].freeze
23
+
24
+ SPAN_ORIGIN = "auto.file.rails"
21
25
 
22
26
  def self.subscribe!
23
27
  subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
24
- record_on_current_span(op: "file.#{event_name}".freeze, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:service], duration: duration) do |span|
28
+ record_on_current_span(
29
+ op: "file.#{event_name}",
30
+ origin: SPAN_ORIGIN,
31
+ start_timestamp: payload[START_TIMESTAMP_NAME],
32
+ description: payload[:service],
33
+ duration: duration
34
+ ) do |span|
25
35
  payload.each do |key, value|
26
- span.set_data(key, value) unless key == START_TIMESTAMP_NAME
36
+ next if key == START_TIMESTAMP_NAME
37
+ next if key == :key && !Sentry.configuration.send_default_pii
38
+
39
+ span.set_data(key, value)
27
40
  end
28
41
  end
29
42
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/rails/tracing/abstract_subscriber"
4
+
5
+ module Sentry
6
+ module Rails
7
+ module Tracing
8
+ class ActiveSupportSubscriber < AbstractSubscriber
9
+ READ_EVENT_NAMES = %w[
10
+ cache_read.active_support
11
+ ].freeze
12
+
13
+ WRITE_EVENT_NAMES = %w[
14
+ cache_write.active_support
15
+ cache_increment.active_support
16
+ cache_decrement.active_support
17
+ ].freeze
18
+
19
+ REMOVE_EVENT_NAMES = %w[
20
+ cache_delete.active_support
21
+ ].freeze
22
+
23
+ FLUSH_EVENT_NAMES = %w[
24
+ cache_prune.active_support
25
+ ].freeze
26
+
27
+ EVENT_NAMES = READ_EVENT_NAMES + WRITE_EVENT_NAMES + REMOVE_EVENT_NAMES + FLUSH_EVENT_NAMES
28
+
29
+ SPAN_ORIGIN = "auto.cache.rails"
30
+
31
+ def self.subscribe!
32
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
33
+ record_on_current_span(
34
+ op: operation_name(event_name),
35
+ origin: SPAN_ORIGIN,
36
+ start_timestamp: payload[START_TIMESTAMP_NAME],
37
+ description: payload[:store],
38
+ duration: duration
39
+ ) do |span|
40
+ span.set_data("cache.key", [*payload[:key]].select { |key| Utils::EncodingHelper.valid_utf_8?(key) })
41
+ span.set_data("cache.hit", payload[:hit] == true) # Handle nil case
42
+ end
43
+ end
44
+ end
45
+
46
+ def self.operation_name(event_name)
47
+ case
48
+ when READ_EVENT_NAMES.include?(event_name)
49
+ "cache.get"
50
+ when WRITE_EVENT_NAMES.include?(event_name)
51
+ "cache.put"
52
+ when REMOVE_EVENT_NAMES.include?(event_name)
53
+ "cache.remove"
54
+ when FLUSH_EVENT_NAMES.include?(event_name)
55
+ "cache.flush"
56
+ else
57
+ "other"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
5
  module Tracing
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rails
3
- VERSION = "5.10.0"
5
+ VERSION = "5.26.0"
4
6
  end
5
7
  end
data/lib/sentry/rails.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails"
2
4
  require "sentry-ruby"
3
5
  require "sentry/integrable"
data/lib/sentry-rails.rb CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/rails/version"
2
4
  require "sentry/rails"