sentry-rails 5.1.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rspec +1 -1
  4. data/Gemfile +44 -26
  5. data/README.md +1 -1
  6. data/Rakefile +13 -10
  7. data/app/jobs/sentry/send_event_job.rb +2 -1
  8. data/bin/console +1 -0
  9. data/bin/test +389 -0
  10. data/lib/generators/sentry_generator.rb +31 -0
  11. data/lib/sentry/rails/action_cable.rb +18 -7
  12. data/lib/sentry/rails/active_job.rb +118 -55
  13. data/lib/sentry/rails/background_worker.rb +13 -4
  14. data/lib/sentry/rails/backtrace_cleaner.rb +7 -7
  15. data/lib/sentry/rails/breadcrumb/active_support_logger.rb +5 -7
  16. data/lib/sentry/rails/capture_exceptions.rb +34 -15
  17. data/lib/sentry/rails/configuration.rb +171 -21
  18. data/lib/sentry/rails/controller_methods.rb +2 -0
  19. data/lib/sentry/rails/controller_transaction.rb +34 -2
  20. data/lib/sentry/rails/engine.rb +2 -0
  21. data/lib/sentry/rails/error_subscriber.rb +26 -12
  22. data/lib/sentry/rails/log_subscriber.rb +76 -0
  23. data/lib/sentry/rails/log_subscribers/action_controller_subscriber.rb +118 -0
  24. data/lib/sentry/rails/log_subscribers/action_mailer_subscriber.rb +92 -0
  25. data/lib/sentry/rails/log_subscribers/active_job_subscriber.rb +163 -0
  26. data/lib/sentry/rails/log_subscribers/active_record_subscriber.rb +164 -0
  27. data/lib/sentry/rails/log_subscribers/parameter_filter.rb +52 -0
  28. data/lib/sentry/rails/overrides/streaming_reporter.rb +2 -11
  29. data/lib/sentry/rails/railtie.rb +35 -13
  30. data/lib/sentry/rails/rescued_exception_interceptor.rb +12 -1
  31. data/lib/sentry/rails/structured_logging.rb +32 -0
  32. data/lib/sentry/rails/tracing/abstract_subscriber.rb +8 -10
  33. data/lib/sentry/rails/tracing/action_view_subscriber.rb +11 -2
  34. data/lib/sentry/rails/tracing/active_record_subscriber.rb +70 -6
  35. data/lib/sentry/rails/tracing/active_storage_subscriber.rb +17 -4
  36. data/lib/sentry/rails/tracing/active_support_subscriber.rb +63 -0
  37. data/lib/sentry/rails/tracing.rb +3 -1
  38. data/lib/sentry/rails/version.rb +3 -1
  39. data/lib/sentry/rails.rb +3 -0
  40. data/lib/sentry-rails.rb +2 -0
  41. data/sentry-rails.gemspec +16 -8
  42. metadata +26 -21
  43. data/CODE_OF_CONDUCT.md +0 -74
  44. data/lib/sentry/rails/breadcrumb/monotonic_active_support_logger.rb +0 -44
  45. data/lib/sentry/rails/instrument_payload_cleanup_helper.rb +0 -13
  46. data/lib/sentry/rails/tracing/action_controller_subscriber.rb +0 -33
@@ -1,19 +1,33 @@
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.
4
- # See https://github.com/rails/rails/blob/main/activesupport/lib/active_support/error_reporter.rb for more information.
6
+ # See https://github.com/rails/rails/blob/main/activesupport/lib/active_support/error_reporter.rb to learn more about reporting APIs.
7
+ # If you want Sentry to subscribe to the error reporter, please set `config.rails.register_error_subscriber` to `true`.
5
8
  class ErrorSubscriber
6
- def report(error, handled:, severity:, context:)
7
- # a component may already have an integration to capture exceptions while its operation is also wrapped inside an `app.executor.wrap` (e.g. ActionCable)
8
- # in such condition, the exception would be captured repeatedly. it usually happens in this order:
9
- #
10
- # 1. exception captured and reported by the component integration and re-raised
11
- # 2. exception captured by the executor, which then reports it with executor.error_reporter
12
- #
13
- # and because there's no direct communication between the 2 callbacks, we need a way to identify if an exception has been captured before
14
- # using a Sentry-specific intance variable should be the last impactful way
15
- return if error.instance_variable_get(:@__sentry_captured)
16
- Sentry::Rails.capture_exception(error, level: severity, contexts: { "rails.error" => context }, tags: { handled: handled })
9
+ SKIP_SOURCES = Regexp.union([/.*_cache_store.active_support/])
10
+
11
+ def report(error, handled:, severity:, context:, source: nil)
12
+ tags = { handled: handled }
13
+
14
+ if source
15
+ return if SKIP_SOURCES.match?(source)
16
+ tags[:source] = source
17
+ end
18
+
19
+ if context[:tags].is_a?(Hash)
20
+ context = context.dup
21
+ tags.merge!(context.delete(:tags))
22
+ end
23
+
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)
17
31
  end
18
32
  end
19
33
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
4
+ require "sentry/utils/logging_helper"
5
+
6
+ module Sentry
7
+ module Rails
8
+ # Base class for Sentry log subscribers that extends ActiveSupport::LogSubscriber
9
+ # to provide structured logging capabilities for Rails components.
10
+ #
11
+ # This class follows Rails' LogSubscriber pattern and provides common functionality
12
+ # for capturing Rails instrumentation events and logging them through Sentry's
13
+ # structured logging system.
14
+ #
15
+ # @example Creating a custom log subscriber
16
+ # class MySubscriber < Sentry::Rails::LogSubscriber
17
+ # attach_to :my_component
18
+ #
19
+ # def my_event(event)
20
+ # log_structured_event(
21
+ # message: "My event occurred",
22
+ # level: :info,
23
+ # attributes: {
24
+ # duration_ms: event.duration,
25
+ # custom_data: event.payload[:custom_data]
26
+ # }
27
+ # )
28
+ # end
29
+ # end
30
+ class LogSubscriber < ActiveSupport::LogSubscriber
31
+ include Sentry::LoggingHelper
32
+
33
+ ORIGIN = "auto.log.rails.log_subscriber"
34
+
35
+ class << self
36
+ if ::Rails.version.to_f < 6.0
37
+ # Rails 5.x does not provide detach_from
38
+ def detach_from(namespace, notifications = ActiveSupport::Notifications)
39
+ listeners = public_instance_methods(false)
40
+ .flat_map { |key|
41
+ notifications.notifier.listeners_for("#{key}.#{namespace}")
42
+ }
43
+ .select { |listener| listener.instance_variable_get(:@delegate).is_a?(self) }
44
+
45
+ listeners.map do |listener|
46
+ notifications.notifier.unsubscribe(listener)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ protected
53
+
54
+ # Log a structured event using Sentry's structured logger
55
+ #
56
+ # @param message [String] The log message
57
+ # @param level [Symbol] The log level (:trace, :debug, :info, :warn, :error, :fatal)
58
+ # @param attributes [Hash] Additional structured attributes to include
59
+ # @param origin [String] The origin of the log event
60
+ def log_structured_event(message:, level: :info, attributes: {}, origin: ORIGIN)
61
+ Sentry.logger.public_send(level, message, **attributes, origin: origin)
62
+ rescue => e
63
+ # Silently handle any errors in logging to avoid breaking the application
64
+ Sentry.configuration.sdk_logger.debug("Failed to log structured event: #{e.message}")
65
+ end
66
+
67
+ # Calculate duration in milliseconds from an event
68
+ #
69
+ # @param event [ActiveSupport::Notifications::Event] The event
70
+ # @return [Float] Duration in milliseconds
71
+ def duration_ms(event)
72
+ event.duration.round(2)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/rails/log_subscriber"
4
+ require "sentry/rails/log_subscribers/parameter_filter"
5
+
6
+ module Sentry
7
+ module Rails
8
+ module LogSubscribers
9
+ # LogSubscriber for ActionController events that captures HTTP request processing
10
+ # and logs them using Sentry's structured logging system.
11
+ #
12
+ # This subscriber captures process_action.action_controller events and formats them
13
+ # with relevant request information including controller, action, HTTP status,
14
+ # request parameters, and performance metrics.
15
+ #
16
+ # @example Usage
17
+ # # Enable structured logging for ActionController
18
+ # Sentry.init do |config|
19
+ # config.enable_logs = true
20
+ # config.rails.structured_logging = true
21
+ # config.rails.structured_logging.subscribers = { action_controller: Sentry::Rails::LogSubscribers::ActionControllerSubscriber }
22
+ # end
23
+ class ActionControllerSubscriber < Sentry::Rails::LogSubscriber
24
+ include ParameterFilter
25
+
26
+ # Handle process_action.action_controller events
27
+ #
28
+ # @param event [ActiveSupport::Notifications::Event] The controller action event
29
+ def process_action(event)
30
+ return unless Sentry.initialized?
31
+
32
+ payload = event.payload
33
+
34
+ controller = payload[:controller]
35
+ action = payload[:action]
36
+
37
+ status = extract_status(payload)
38
+
39
+ attributes = {
40
+ controller: controller,
41
+ action: action,
42
+ duration_ms: duration_ms(event),
43
+ method: payload[:method],
44
+ path: payload[:path],
45
+ format: payload[:format]
46
+ }
47
+
48
+ attributes[:status] = status if status
49
+
50
+ if payload[:view_runtime]
51
+ attributes[:view_runtime_ms] = payload[:view_runtime].round(2)
52
+ end
53
+
54
+ if payload[:db_runtime]
55
+ attributes[:db_runtime_ms] = payload[:db_runtime].round(2)
56
+ end
57
+
58
+ if Sentry.configuration.send_default_pii && payload[:params]
59
+ filtered_params = filter_sensitive_params(payload[:params])
60
+ attributes[:params] = filtered_params unless filtered_params.empty?
61
+ end
62
+
63
+ level = level_for_request(payload)
64
+ message = "#{controller}##{action}"
65
+
66
+ log_structured_event(
67
+ message: message,
68
+ level: level,
69
+ attributes: attributes
70
+ )
71
+ end
72
+
73
+ private
74
+
75
+ def extract_status(payload)
76
+ if payload[:status]
77
+ payload[:status]
78
+ elsif payload[:exception]
79
+ case payload[:exception].first
80
+ when "ActionController::RoutingError"
81
+ 404
82
+ when "ActionController::BadRequest"
83
+ 400
84
+ else
85
+ 500
86
+ end
87
+ end
88
+ end
89
+
90
+ def level_for_request(payload)
91
+ status = payload[:status]
92
+
93
+ # In Rails < 6.0 status is not set when an action raised an exception
94
+ if status.nil? && payload[:exception]
95
+ case payload[:exception].first
96
+ when "ActionController::RoutingError"
97
+ :warn
98
+ when "ActionController::BadRequest"
99
+ :warn
100
+ else
101
+ :error
102
+ end
103
+ elsif status.nil?
104
+ :info
105
+ elsif status >= 200 && status < 400
106
+ :info
107
+ elsif status >= 400 && status < 500
108
+ :warn
109
+ elsif status >= 500
110
+ :error
111
+ else
112
+ :info
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/rails/log_subscriber"
4
+ require "sentry/rails/log_subscribers/parameter_filter"
5
+
6
+ module Sentry
7
+ module Rails
8
+ module LogSubscribers
9
+ # LogSubscriber for ActionMailer events that captures email delivery
10
+ # and processing events using Sentry's structured logging system.
11
+ #
12
+ # This subscriber captures deliver.action_mailer and process.action_mailer events
13
+ # and formats them with relevant email information while respecting PII settings.
14
+ #
15
+ # @example Usage
16
+ # # Enable structured logging for ActionMailer
17
+ # Sentry.init do |config|
18
+ # config.enable_logs = true
19
+ # config.rails.structured_logging = true
20
+ # config.rails.structured_logging.subscribers = { action_mailer: Sentry::Rails::LogSubscribers::ActionMailerSubscriber }
21
+ # end
22
+ class ActionMailerSubscriber < Sentry::Rails::LogSubscriber
23
+ include ParameterFilter
24
+
25
+ # Handle deliver.action_mailer events
26
+ #
27
+ # @param event [ActiveSupport::Notifications::Event] The email delivery event
28
+ def deliver(event)
29
+ return unless Sentry.initialized?
30
+
31
+ payload = event.payload
32
+
33
+ mailer = payload[:mailer]
34
+
35
+ attributes = {
36
+ mailer: mailer,
37
+ duration_ms: duration_ms(event),
38
+ perform_deliveries: payload[:perform_deliveries]
39
+ }
40
+
41
+ attributes[:delivery_method] = payload[:delivery_method] if payload[:delivery_method]
42
+ attributes[:date] = payload[:date].to_s if payload[:date]
43
+
44
+ if Sentry.configuration.send_default_pii
45
+ attributes[:message_id] = payload[:message_id] if payload[:message_id]
46
+ end
47
+
48
+ message = "Email delivered via #{mailer}"
49
+
50
+ # Log the structured event
51
+ log_structured_event(
52
+ message: message,
53
+ level: :info,
54
+ attributes: attributes
55
+ )
56
+ end
57
+
58
+ # Handle process.action_mailer events
59
+ #
60
+ # @param event [ActiveSupport::Notifications::Event] The email processing event
61
+ def process(event)
62
+ return unless Sentry.initialized?
63
+
64
+ payload = event.payload
65
+
66
+ mailer = payload[:mailer]
67
+ action = payload[:action]
68
+ duration = duration_ms(event)
69
+
70
+ attributes = {
71
+ mailer: mailer,
72
+ action: action,
73
+ duration_ms: duration
74
+ }
75
+
76
+ if Sentry.configuration.send_default_pii && payload[:params]
77
+ filtered_params = filter_sensitive_params(payload[:params])
78
+ attributes[:params] = filtered_params unless filtered_params.empty?
79
+ end
80
+
81
+ message = "#{mailer}##{action}"
82
+
83
+ log_structured_event(
84
+ message: message,
85
+ level: :info,
86
+ attributes: attributes
87
+ )
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/rails/log_subscriber"
4
+ require "sentry/rails/log_subscribers/parameter_filter"
5
+
6
+ module Sentry
7
+ module Rails
8
+ module LogSubscribers
9
+ # LogSubscriber for ActiveJob events that captures background job execution
10
+ # and logs them using Sentry's structured logging system.
11
+ #
12
+ # This subscriber captures various ActiveJob events including job execution,
13
+ # enqueueing, retries, and failures with relevant job information.
14
+ #
15
+ # @example Usage
16
+ # # Enable structured logging for ActiveJob
17
+ # Sentry.init do |config|
18
+ # config.enable_logs = true
19
+ # config.rails.structured_logging = true
20
+ # config.rails.structured_logging.subscribers = { active_job: Sentry::Rails::LogSubscribers::ActiveJobSubscriber }
21
+ # end
22
+ class ActiveJobSubscriber < Sentry::Rails::LogSubscriber
23
+ include ParameterFilter
24
+
25
+ # Handle perform.active_job events
26
+ #
27
+ # @param event [ActiveSupport::Notifications::Event] The job performance event
28
+ def perform(event)
29
+ return unless Sentry.initialized?
30
+
31
+ job = event.payload[:job]
32
+ duration = duration_ms(event)
33
+
34
+ attributes = {
35
+ job_class: job.class.name,
36
+ job_id: job.job_id,
37
+ queue_name: job.queue_name,
38
+ duration_ms: duration,
39
+ executions: job.executions,
40
+ priority: job.priority
41
+ }
42
+
43
+ attributes[:adapter] = job.class.queue_adapter.class.name
44
+
45
+ if job.scheduled_at
46
+ attributes[:scheduled_at] = job.scheduled_at.iso8601
47
+ attributes[:delay_ms] = ((Time.current - job.scheduled_at) * 1000).round(2)
48
+ end
49
+
50
+ if Sentry.configuration.send_default_pii && job.arguments.present?
51
+ filtered_args = filter_sensitive_arguments(job.arguments)
52
+ attributes[:arguments] = filtered_args unless filtered_args.empty?
53
+ end
54
+
55
+ message = "Job performed: #{job.class.name}"
56
+
57
+ log_structured_event(
58
+ message: message,
59
+ level: :info,
60
+ attributes: attributes
61
+ )
62
+ end
63
+
64
+ # Handle enqueue.active_job events
65
+ #
66
+ # @param event [ActiveSupport::Notifications::Event] The job enqueue event
67
+ def enqueue(event)
68
+ return unless Sentry.initialized?
69
+
70
+ job = event.payload[:job]
71
+
72
+ attributes = {
73
+ job_class: job.class.name,
74
+ job_id: job.job_id,
75
+ queue_name: job.queue_name,
76
+ priority: job.priority
77
+ }
78
+
79
+ attributes[:adapter] = job.class.queue_adapter.class.name if job.class.respond_to?(:queue_adapter)
80
+
81
+ if job.scheduled_at
82
+ attributes[:scheduled_at] = job.scheduled_at.iso8601
83
+ attributes[:delay_seconds] = (job.scheduled_at - Time.current).round(2)
84
+ end
85
+
86
+ message = "Job enqueued: #{job.class.name}"
87
+
88
+ log_structured_event(
89
+ message: message,
90
+ level: :info,
91
+ attributes: attributes
92
+ )
93
+ end
94
+
95
+ def retry_stopped(event)
96
+ return unless Sentry.initialized?
97
+
98
+ job = event.payload[:job]
99
+ error = event.payload[:error]
100
+
101
+ attributes = {
102
+ job_class: job.class.name,
103
+ job_id: job.job_id,
104
+ queue_name: job.queue_name,
105
+ executions: job.executions,
106
+ error_class: error.class.name,
107
+ error_message: error.message
108
+ }
109
+
110
+ message = "Job retry stopped: #{job.class.name}"
111
+
112
+ log_structured_event(
113
+ message: message,
114
+ level: :error,
115
+ attributes: attributes
116
+ )
117
+ end
118
+
119
+ def discard(event)
120
+ return unless Sentry.initialized?
121
+
122
+ job = event.payload[:job]
123
+ error = event.payload[:error]
124
+
125
+ attributes = {
126
+ job_class: job.class.name,
127
+ job_id: job.job_id,
128
+ queue_name: job.queue_name,
129
+ executions: job.executions
130
+ }
131
+
132
+ attributes[:error_class] = error.class.name if error
133
+ attributes[:error_message] = error.message if error
134
+
135
+ message = "Job discarded: #{job.class.name}"
136
+
137
+ log_structured_event(
138
+ message: message,
139
+ level: :warn,
140
+ attributes: attributes
141
+ )
142
+ end
143
+
144
+ private
145
+
146
+ def filter_sensitive_arguments(arguments)
147
+ return [] unless arguments.is_a?(Array)
148
+
149
+ arguments.map do |arg|
150
+ case arg
151
+ when Hash
152
+ filter_sensitive_params(arg)
153
+ when String
154
+ arg.length > 100 ? "[FILTERED: #{arg.length} chars]" : arg
155
+ else
156
+ arg
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/rails/log_subscriber"
4
+ require "sentry/rails/log_subscribers/parameter_filter"
5
+
6
+ module Sentry
7
+ module Rails
8
+ module LogSubscribers
9
+ # LogSubscriber for ActiveRecord events that captures database queries
10
+ # and logs them using Sentry's structured logging system.
11
+ #
12
+ # This subscriber captures sql.active_record events and formats them
13
+ # with relevant database information including SQL queries, duration,
14
+ # database configuration, and caching information.
15
+ #
16
+ # @example Usage
17
+ # # Automatically attached when structured logging is enabled for :active_record
18
+ # Sentry.init do |config|
19
+ # config.enable_logs = true
20
+ # config.rails.structured_logging = true
21
+ # config.rails.structured_logging.subscribers = { active_record: Sentry::Rails::LogSubscribers::ActiveRecordSubscriber }
22
+ # end
23
+ class ActiveRecordSubscriber < Sentry::Rails::LogSubscriber
24
+ include ParameterFilter
25
+
26
+ EXCLUDED_NAMES = ["SCHEMA", "TRANSACTION"].freeze
27
+ EMPTY_ARRAY = [].freeze
28
+
29
+ # Handle sql.active_record events
30
+ #
31
+ # @param event [ActiveSupport::Notifications::Event] The SQL event
32
+ def sql(event)
33
+ return unless Sentry.initialized?
34
+ return if logger && !logger.info?
35
+ return if EXCLUDED_NAMES.include?(event.payload[:name])
36
+
37
+ sql = event.payload[:sql]
38
+ statement_name = event.payload[:name]
39
+
40
+ # Rails 5.0.0 doesn't include :cached in the payload, it was added in Rails 5.1
41
+ cached = event.payload.fetch(:cached, false)
42
+ connection_id = event.payload[:connection_id]
43
+
44
+ attributes = {
45
+ sql: sql,
46
+ duration_ms: duration_ms(event),
47
+ cached: cached
48
+ }
49
+
50
+ binds = event.payload[:binds]
51
+
52
+ if Sentry.configuration.send_default_pii && (binds && !binds.empty?)
53
+ type_casted_binds = type_casted_binds(event)
54
+
55
+ type_casted_binds.each_with_index do |value, index|
56
+ bind = binds[index]
57
+ name = bind.respond_to?(:name) ? bind.name : index.to_s
58
+
59
+ attributes["db.query.parameter.#{name}"] = value.to_s
60
+ end
61
+ end
62
+
63
+ attributes[:statement_name] = statement_name if statement_name && statement_name != "SQL"
64
+ attributes[:connection_id] = connection_id if connection_id
65
+
66
+ maybe_add_db_config_attributes(attributes, event.payload)
67
+
68
+ message = build_log_message(statement_name)
69
+
70
+ log_structured_event(
71
+ message: message,
72
+ level: :info,
73
+ attributes: attributes
74
+ )
75
+ rescue => e
76
+ log_debug("[#{self.class}] failed to log sql event: #{e.message}")
77
+ end
78
+
79
+ def type_casted_binds(event)
80
+ binds = event.payload[:type_casted_binds]
81
+
82
+ # When a query is cached, binds are a callable,
83
+ # and under JRuby they're always a callable.
84
+ if binds.respond_to?(:call)
85
+ binds.call
86
+ else
87
+ binds
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def build_log_message(statement_name)
94
+ if statement_name && statement_name != "SQL"
95
+ "Database query: #{statement_name}"
96
+ else
97
+ "Database query"
98
+ end
99
+ end
100
+
101
+ def maybe_add_db_config_attributes(attributes, payload)
102
+ db_config = extract_db_config(payload)
103
+
104
+ return unless db_config
105
+
106
+ attributes[:db_system] = db_config[:adapter] if db_config[:adapter]
107
+
108
+ if db_config[:database]
109
+ db_name = db_config[:database]
110
+
111
+ if db_config[:adapter] == "sqlite3" && db_name.include?("/")
112
+ db_name = File.basename(db_name)
113
+ end
114
+
115
+ attributes[:db_name] = db_name
116
+ end
117
+
118
+ attributes[:server_address] = db_config[:host] if db_config[:host]
119
+ attributes[:server_port] = db_config[:port] if db_config[:port]
120
+ attributes[:server_socket_address] = db_config[:socket] if db_config[:socket]
121
+ end
122
+
123
+ def extract_db_config(payload)
124
+ connection = payload[:connection]
125
+
126
+ return unless connection
127
+
128
+ extract_db_config_from_connection(connection)
129
+ end
130
+
131
+ if ::Rails.version.to_f >= 6.1
132
+ def extract_db_config_from_connection(connection)
133
+ if connection.pool.respond_to?(:db_config)
134
+ db_config = connection.pool.db_config
135
+ if db_config.respond_to?(:configuration_hash)
136
+ return db_config.configuration_hash
137
+ elsif db_config.respond_to?(:config)
138
+ return db_config.config
139
+ end
140
+ end
141
+
142
+ extract_db_config_fallback(connection)
143
+ end
144
+ else
145
+ # Rails 6.0 and earlier use spec API
146
+ def extract_db_config_from_connection(connection)
147
+ if connection.pool.respond_to?(:spec)
148
+ spec = connection.pool.spec
149
+ if spec.respond_to?(:config)
150
+ return spec.config
151
+ end
152
+ end
153
+
154
+ extract_db_config_fallback(connection)
155
+ end
156
+ end
157
+
158
+ def extract_db_config_fallback(connection)
159
+ connection.config if connection.respond_to?(:config)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end