sentry-rails 6.6.2 → 7.0.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Gemfile +50 -0
  4. data/README.md +1 -1
  5. data/bin/test +12 -386
  6. data/bin/test_old +389 -0
  7. data/gemfiles/ruby-2.7_rails-5.2.0.gemfile.lock +266 -0
  8. data/gemfiles/ruby-2.7_rails-6.0.0.gemfile.lock +282 -0
  9. data/gemfiles/ruby-2.7_rails-6.1.0.gemfile.lock +294 -0
  10. data/gemfiles/ruby-2.7_rails-7.0.0.gemfile.lock +311 -0
  11. data/gemfiles/ruby-2.7_rails-7.1.0.gemfile.lock +348 -0
  12. data/gemfiles/ruby-3.0_rails-6.1.0.gemfile.lock +362 -0
  13. data/gemfiles/ruby-3.0_rails-7.0.0.gemfile.lock +379 -0
  14. data/gemfiles/ruby-3.0_rails-7.1.0.gemfile.lock +405 -0
  15. data/gemfiles/ruby-3.1_rails-6.1.0.gemfile.lock +493 -0
  16. data/gemfiles/ruby-3.1_rails-7.0.0.gemfile.lock +513 -0
  17. data/gemfiles/ruby-3.1_rails-7.1.0.gemfile.lock +547 -0
  18. data/gemfiles/ruby-3.1_rails-7.2.0.gemfile.lock +541 -0
  19. data/gemfiles/ruby-3.2_rails-6.1.0.gemfile.lock +493 -0
  20. data/gemfiles/ruby-3.2_rails-7.0.0.gemfile.lock +511 -0
  21. data/gemfiles/ruby-3.2_rails-7.1.0.gemfile.lock +553 -0
  22. data/gemfiles/ruby-3.2_rails-7.2.0.gemfile.lock +545 -0
  23. data/gemfiles/ruby-3.2_rails-8.0.0.gemfile.lock +544 -0
  24. data/gemfiles/ruby-3.3_rails-6.1.0.gemfile.lock +493 -0
  25. data/gemfiles/ruby-3.3_rails-7.0.0.gemfile.lock +511 -0
  26. data/gemfiles/ruby-3.3_rails-7.1.0.gemfile.lock +553 -0
  27. data/gemfiles/ruby-3.3_rails-7.2.0.gemfile.lock +545 -0
  28. data/gemfiles/ruby-3.3_rails-8.0.0.gemfile.lock +544 -0
  29. data/gemfiles/ruby-3.4_rails-7.1.0.gemfile.lock +534 -0
  30. data/gemfiles/ruby-3.4_rails-7.2.0.gemfile.lock +528 -0
  31. data/gemfiles/ruby-3.4_rails-8.0.0.gemfile.lock +523 -0
  32. data/gemfiles/ruby-3.4_rails-8.1.3.gemfile.lock +527 -0
  33. data/gemfiles/ruby-4.0_rails-6.1.0.gemfile.lock +476 -0
  34. data/gemfiles/ruby-4.0_rails-7.0.0.gemfile.lock +492 -0
  35. data/gemfiles/ruby-4.0_rails-7.1.0.gemfile.lock +534 -0
  36. data/gemfiles/ruby-4.0_rails-8.0.0.gemfile.lock +523 -0
  37. data/gemfiles/ruby-4.0_rails-8.1.3.gemfile.lock +527 -0
  38. data/gemfiles/ruby-jruby-9.4.14.0_rails-6.1.0.gemfile.lock +369 -0
  39. data/gemfiles/ruby-jruby-9.4.14.0_rails-7.0.0.gemfile.lock +391 -0
  40. data/gemfiles/ruby-jruby-9.4.14.0_rails-7.1.0.gemfile.lock +425 -0
  41. data/lib/sentry/rails/active_job.rb +188 -29
  42. data/lib/sentry/rails/breadcrumb/active_support_logger.rb +30 -0
  43. data/lib/sentry/rails/capture_exceptions.rb +8 -1
  44. data/lib/sentry/rails/configuration.rb +14 -2
  45. data/lib/sentry/rails/controller_transaction.rb +24 -3
  46. data/lib/sentry/rails/error_reporter_context.rb +24 -0
  47. data/lib/sentry/rails/log_subscribers/action_controller_subscriber.rb +3 -7
  48. data/lib/sentry/rails/log_subscribers/action_mailer_subscriber.rb +5 -9
  49. data/lib/sentry/rails/log_subscribers/active_job_subscriber.rb +5 -7
  50. data/lib/sentry/rails/log_subscribers/active_record_subscriber.rb +1 -5
  51. data/lib/sentry/rails/railtie.rb +8 -1
  52. data/lib/sentry/rails/serializer.rb +35 -0
  53. data/lib/sentry/rails/tracing/active_storage_subscriber.rb +4 -1
  54. data/lib/sentry/rails/version.rb +1 -1
  55. data/sentry-rails.gemspec +2 -2
  56. metadata +45 -9
  57. data/lib/sentry/rails/log_subscribers/parameter_filter.rb +0 -52
@@ -1,17 +1,66 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
+ require "sentry/rails/serializer"
5
+ require "sentry/rails/error_reporter_context"
4
6
 
5
7
  module Sentry
6
8
  module Rails
7
9
  module ActiveJobExtensions
10
+ SENTRY_PAYLOAD_KEY = "_sentry"
11
+
12
+ USER_FIELDS_ALLOWLIST = %w[id email username].freeze
13
+
14
+ def self.prepended(base)
15
+ base.attr_accessor :_sentry
16
+ end
17
+
8
18
  def perform_now
9
19
  if !Sentry.initialized? || already_supported_by_sentry_integration?
10
20
  super
11
21
  else
12
- SentryReporter.record(self) do
13
- super
22
+ data = _sentry || {}
23
+ SentryReporter.record(
24
+ self,
25
+ trace_headers: data["trace_propagation_headers"],
26
+ user: data["user"]
27
+ ) { super }
28
+ end
29
+ end
30
+
31
+ def serialize
32
+ payload = super
33
+ return payload if !Sentry.initialized? || already_supported_by_sentry_integration?
34
+
35
+ begin
36
+ sentry_data = {}
37
+ if Sentry.configuration.rails.active_job_propagate_traces
38
+ headers = Sentry.get_trace_propagation_headers
39
+ sentry_data["trace_propagation_headers"] = headers if headers && !headers.empty?
14
40
  end
41
+
42
+ if Sentry.configuration.data_collection.user_info
43
+ user = Sentry.get_current_scope.user
44
+ allowed = user.transform_keys(&:to_s).slice(*USER_FIELDS_ALLOWLIST)
45
+ sentry_data["user"] = allowed unless allowed.empty?
46
+ end
47
+
48
+ payload[SENTRY_PAYLOAD_KEY] = sentry_data unless sentry_data.empty?
49
+ rescue StandardError => e
50
+ Sentry.sdk_logger&.error("sentry-rails: failed to inject _sentry payload: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(5).join("\n ")}")
51
+ end
52
+
53
+ payload
54
+ end
55
+
56
+ def deserialize(job_data)
57
+ super
58
+ return if !Sentry.initialized? || already_supported_by_sentry_integration?
59
+
60
+ begin
61
+ self._sentry = job_data[SENTRY_PAYLOAD_KEY]
62
+ rescue StandardError => e
63
+ Sentry.sdk_logger&.error("sentry-rails: failed to extract _sentry payload: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(5).join("\n ")}")
15
64
  end
16
65
  end
17
66
 
@@ -20,27 +69,104 @@ module Sentry
20
69
  end
21
70
 
22
71
  class SentryReporter
23
- OP_NAME = "queue.active_job"
72
+ OP_NAME = "queue.process"
24
73
  SPAN_ORIGIN = "auto.queue.active_job"
25
74
 
75
+ # Emitted as messaging.system when the configured queue adapter's
76
+ # identity cannot be resolved from the job. ActiveJob is adapter-agnostic
77
+ # and supports arbitrary third-party backends, so the concrete value is
78
+ # normally derived from the adapter itself; this is only the last resort.
79
+ MESSAGING_SYSTEM_FALLBACK = "activejob"
80
+
26
81
  EVENT_HANDLERS = {
27
82
  "enqueue_retry.active_job" => :retry_handler
28
83
  }
29
84
 
30
85
  class << self
31
- def record(job, &block)
86
+ include ErrorReporterContext
87
+
88
+ def producer_callback_registered?
89
+ @producer_callback_registered ||= false
90
+ end
91
+
92
+ def producer_callback_registered!
93
+ @producer_callback_registered = true
94
+ end
95
+
96
+ def record_producer_span(job, &enqueue)
97
+ return yield if !Sentry.initialized? || job.already_supported_by_sentry_integration?
98
+
99
+ enqueued = false
100
+
101
+ run_enqueue = lambda do
102
+ enqueued = true
103
+ enqueue.call
104
+ end
105
+
106
+ begin
107
+ Sentry.with_child_span(op: "queue.publish", description: job.class.name) do |span|
108
+ if span
109
+ span.set_origin(SPAN_ORIGIN)
110
+ span.set_data(Sentry::Span::DataConventions::MESSAGING_SYSTEM, messaging_system(job))
111
+ span.set_data(Sentry::Span::DataConventions::MESSAGING_MESSAGE_ID, job.job_id)
112
+ span.set_data(Sentry::Span::DataConventions::MESSAGING_DESTINATION_NAME, job.queue_name)
113
+
114
+ if (count = retry_count(job))
115
+ span.set_data(Sentry::Span::DataConventions::MESSAGING_MESSAGE_RETRY_COUNT, count)
116
+ end
117
+ end
118
+
119
+ run_enqueue.call
120
+ end
121
+ rescue StandardError => e
122
+ raise if enqueued
123
+
124
+ log_producer_span_error(e)
125
+
126
+ run_enqueue.call
127
+ end
128
+ end
129
+
130
+ def log_producer_span_error(e)
131
+ Sentry.sdk_logger&.error("sentry-rails: failed to record producer span: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(5).join("\n ")}")
132
+ end
133
+
134
+ def record(job, trace_headers: nil, user: nil, &block)
135
+ original_hub = Sentry.get_current_hub_internal
136
+ Sentry.clone_hub_to_current_thread
137
+
32
138
  Sentry.with_scope do |scope|
33
139
  begin
140
+ scope.set_user(user.transform_keys(&:to_sym)) if user && !user.empty?
141
+
34
142
  scope.set_transaction_name(job.class.name, source: :task)
143
+ scope.set_tags(queue: job.queue_name)
35
144
 
36
- transaction = Sentry.start_transaction(
145
+ scope.set_contexts(active_job: {
146
+ job_class: job.class.name,
147
+ job_id: job.job_id,
148
+ queue: job.queue_name,
149
+ provider_job_id: job.provider_job_id
150
+ })
151
+
152
+ transaction_options = {
37
153
  name: scope.transaction_name,
38
154
  source: scope.transaction_source,
39
155
  op: OP_NAME,
40
156
  origin: SPAN_ORIGIN
41
- )
157
+ }
158
+
159
+ transaction = if trace_headers && !trace_headers.empty?
160
+ continued = Sentry.continue_trace(trace_headers, **transaction_options)
161
+ Sentry.start_transaction(transaction: continued, **transaction_options)
162
+ else
163
+ Sentry.start_transaction(**transaction_options)
164
+ end
42
165
 
43
- scope.set_span(transaction) if transaction
166
+ if transaction
167
+ set_messaging_data(transaction, job)
168
+ scope.set_span(transaction)
169
+ end
44
170
 
45
171
  yield.tap do
46
172
  finish_sentry_transaction(transaction, 200)
@@ -53,17 +179,60 @@ module Sentry
53
179
  raise
54
180
  end
55
181
  end
182
+ ensure
183
+ Sentry.set_current_hub_internal(original_hub)
184
+ end
185
+
186
+ def set_messaging_data(transaction, job)
187
+ transaction.set_data(Sentry::Span::DataConventions::MESSAGING_SYSTEM, messaging_system(job))
188
+ transaction.set_data(Sentry::Span::DataConventions::MESSAGING_MESSAGE_ID, job.job_id)
189
+ transaction.set_data(Sentry::Span::DataConventions::MESSAGING_DESTINATION_NAME, job.queue_name)
190
+ if (count = retry_count(job))
191
+ transaction.set_data(Sentry::Span::DataConventions::MESSAGING_MESSAGE_RETRY_COUNT, count)
192
+ end
193
+
194
+ if (latency = compute_latency(job))
195
+ transaction.set_data(Sentry::Span::DataConventions::MESSAGING_MESSAGE_RECEIVE_LATENCY, latency)
196
+ end
197
+ end
198
+
199
+ def messaging_system(job)
200
+ name = job.class.queue_adapter_name if job.class.respond_to?(:queue_adapter_name)
201
+ name = name.to_s
202
+ name.empty? ? MESSAGING_SYSTEM_FALLBACK : name
203
+ end
204
+
205
+ # Number of retries the job has already gone through, as observed at
206
+ # the moment the span is opened. On the consumer the span opens before
207
+ # ActiveJob increments +executions+, so a job's first attempt reads 0,
208
+ # its first retry reads 1, and so on. On the producer the retry enqueue
209
+ # runs after the failed attempt bumped +executions+, so it observes the
210
+ # same progression.
211
+ def retry_count(job)
212
+ job.executions.to_i if job.respond_to?(:executions)
213
+ end
214
+
215
+ def compute_latency(job)
216
+ return unless job.respond_to?(:enqueued_at) && job.enqueued_at
217
+
218
+ enqueued_time = job.enqueued_at.is_a?(String) ? Time.parse(job.enqueued_at) : job.enqueued_at
219
+ (Time.now.to_f - enqueued_time.to_f) * 1000.0
56
220
  end
57
221
 
58
222
  def capture_exception(job, e)
59
- Sentry::Rails.capture_exception(
60
- e,
223
+ options = {
61
224
  extra: sentry_context(job),
62
225
  tags: {
63
226
  job_id: job.job_id,
64
227
  provider_job_id: job.provider_job_id
65
- }
66
- )
228
+ },
229
+ # Send synchronously: a worker process may exit before the async
230
+ # background worker flushes its queue, which would drop the event.
231
+ hint: { background: false }
232
+ }
233
+ options[:contexts] = execution_context if Sentry.configuration.data_collection.queues
234
+
235
+ Sentry::Rails.capture_exception(e, **options)
67
236
  end
68
237
 
69
238
  def register_event_handlers
@@ -104,33 +273,23 @@ module Sentry
104
273
  end
105
274
 
106
275
  def sentry_context(job)
107
- {
276
+ context = {
108
277
  active_job: job.class.name,
109
- arguments: sentry_serialize_arguments(job.arguments),
110
278
  scheduled_at: job.scheduled_at,
111
279
  job_id: job.job_id,
112
280
  provider_job_id: job.provider_job_id,
113
281
  locale: job.locale
114
282
  }
283
+
284
+ if Sentry.configuration.data_collection.queues
285
+ context[:arguments] = sentry_serialize_arguments(job.arguments)
286
+ end
287
+
288
+ context
115
289
  end
116
290
 
117
291
  def sentry_serialize_arguments(argument)
118
- case argument
119
- when Range
120
- if (argument.begin || argument.end).is_a?(ActiveSupport::TimeWithZone)
121
- argument.to_s
122
- else
123
- argument.map { |v| sentry_serialize_arguments(v) }
124
- end
125
- when Hash
126
- argument.transform_values { |v| sentry_serialize_arguments(v) }
127
- when Array, Enumerable
128
- argument.map { |v| sentry_serialize_arguments(v) }
129
- when ->(v) { v.respond_to?(:to_global_id) }
130
- argument.to_global_id.to_s rescue argument
131
- else
132
- argument
133
- end
292
+ Sentry::Rails::Serializer.serialize(argument)
134
293
  end
135
294
 
136
295
  private
@@ -1,16 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sentry/utils/http_tracing"
4
+
3
5
  module Sentry
4
6
  module Rails
5
7
  module Breadcrumb
6
8
  module ActiveSupportLogger
7
9
  class << self
10
+ include Sentry::Utils::HttpTracing
8
11
  def add(name, started, _finished, _unique_id, data)
12
+ return unless Sentry.initialized?
13
+
9
14
  # skip Rails' internal events
10
15
  return if name.start_with?("!")
11
16
 
12
17
  if data.is_a?(Hash)
13
18
  data = data.slice(*@allowed_keys[name])
19
+ filter_data_collection!(data)
14
20
  end
15
21
 
16
22
  crumb = Sentry::Breadcrumb.new(
@@ -35,6 +41,30 @@ module Sentry
35
41
  def detach
36
42
  ::ActiveSupport::Notifications.unsubscribe(@subscriber)
37
43
  end
44
+
45
+ private
46
+
47
+ def filter_data_collection!(data)
48
+ filter_params!(data)
49
+ filter_path!(data)
50
+ end
51
+
52
+ def filter_params!(data)
53
+ return unless data.key?(:params) && data[:params].is_a?(Hash)
54
+
55
+ data[:params] = Sentry.configuration.data_collection.url_query_params.filter(data[:params])
56
+ data.delete(:params) if data[:params].empty?
57
+ end
58
+
59
+ def filter_path!(data)
60
+ return unless data.key?(:path) && data[:path].is_a?(String)
61
+
62
+ path, query = data[:path].split("?", 2)
63
+ return unless query
64
+
65
+ filtered_query = filter_query_params(query)
66
+ data[:path] = filtered_query ? "#{path}?#{filtered_query}" : path
67
+ end
38
68
  end
39
69
  end
40
70
  end
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sentry/rails/error_reporter_context"
4
+
3
5
  module Sentry
4
6
  module Rails
5
7
  class CaptureExceptions < Sentry::Rack::CaptureExceptions
8
+ include ErrorReporterContext
9
+
6
10
  RAILS_7_1 = Gem::Version.new(::Rails.version) >= Gem::Version.new("7.1.0.alpha")
7
11
  SPAN_ORIGIN = "auto.http.rails"
8
12
 
@@ -30,7 +34,10 @@ module Sentry
30
34
  return unless Sentry.initialized?
31
35
  return if show_exceptions?(exception, env) && !Sentry.configuration.rails.report_rescued_exceptions
32
36
 
33
- Sentry::Rails.capture_exception(exception).tap do |event|
37
+ options = {}
38
+ options[:contexts] = execution_context if Sentry.configuration.data_collection.user_info
39
+
40
+ Sentry::Rails.capture_exception(exception, **options).tap do |event|
34
41
  env[ERROR_EVENT_ID_KEY] = event.event_id if event
35
42
  end
36
43
  end
@@ -35,7 +35,13 @@ module Sentry
35
35
  end
36
36
 
37
37
  after(:configured) do
38
- rails.structured_logging.enabled = enable_logs if rails.structured_logging.enabled.nil?
38
+ collection = data_collection.url_query_params
39
+ if collection.mode == :deny_list
40
+ filter_parameters = ::Rails.application.config.filter_parameters.select do |filter|
41
+ filter.is_a?(String) || filter.is_a?(Symbol) || filter.is_a?(Regexp)
42
+ end
43
+ collection.terms = (Array(collection.terms) + filter_parameters).uniq
44
+ end
39
45
  end
40
46
  end
41
47
 
@@ -172,6 +178,11 @@ module Sentry
172
178
  # Set this option to true if you want Sentry to capture each retry failure
173
179
  attr_accessor :active_job_report_on_retry_error
174
180
 
181
+ # Whether we should inject trace propagation headers into the serialized job
182
+ # payload in order to have a connected trace between producer and consumer.
183
+ # Defaults to true. Set to false to opt out.
184
+ attr_accessor :active_job_propagate_traces
185
+
175
186
  # Configuration for structured logging feature
176
187
  # @return [StructuredLoggingConfiguration]
177
188
  attr_reader :structured_logging
@@ -193,6 +204,7 @@ module Sentry
193
204
  @db_query_source_threshold_ms = 100
194
205
  @active_support_logger_subscription_items = Sentry::Rails::ACTIVE_SUPPORT_LOGGER_SUBSCRIPTION_ITEMS_DEFAULT.dup
195
206
  @active_job_report_on_retry_error = false
207
+ @active_job_propagate_traces = true
196
208
  @structured_logging = StructuredLoggingConfiguration.new
197
209
  end
198
210
  end
@@ -212,7 +224,7 @@ module Sentry
212
224
  }.freeze
213
225
 
214
226
  def initialize
215
- @enabled = nil
227
+ @enabled = true
216
228
  @subscribers = DEFAULT_SUBSCRIBERS.dup
217
229
  end
218
230
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sentry/utils/http_tracing"
4
+
3
5
  module Sentry
4
6
  module Rails
5
7
  module ControllerTransaction
@@ -24,9 +26,28 @@ module Sentry
24
26
  child_span.set_data(:format, request.format)
25
27
  child_span.set_data(:method, request.method)
26
28
 
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)
29
+ data_collection = Sentry.configuration.data_collection
30
+
31
+ path = request.path
32
+ query_parameters = request.query_parameters
33
+
34
+ if query_parameters.is_a?(Hash)
35
+ filtered_query_parameters = data_collection.url_query_params.filter(query_parameters)
36
+
37
+ unless filtered_query_parameters.empty?
38
+ formatted_query = Sentry::Utils::HttpTracing.format_query(filtered_query_parameters)
39
+ path = "#{path}?#{formatted_query}"
40
+ end
41
+ end
42
+
43
+ child_span.set_data(:path, path)
44
+
45
+ # all params request + body
46
+ params = request.params
47
+ if params.is_a?(Hash)
48
+ filtered_params = data_collection.url_query_params.filter(params)
49
+ child_span.set_data(:params, filtered_params)
50
+ end
30
51
  end
31
52
 
32
53
  result
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/rails/serializer"
4
+
5
+ module Sentry
6
+ module Rails
7
+ module ErrorReporterContext
8
+ SUPPORTS_EXECUTION_CONTEXT = Gem::Version.new(::Rails.version) >= Gem::Version.new("7.0.0")
9
+
10
+ if SUPPORTS_EXECUTION_CONTEXT
11
+ def execution_context
12
+ context = ::ActiveSupport::ExecutionContext.to_h
13
+ return {} if context.empty?
14
+
15
+ { "rails.error" => Sentry::Rails::Serializer.serialize(context) }
16
+ end
17
+ else
18
+ def execution_context
19
+ {}
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/rails/log_subscriber"
4
- require "sentry/rails/log_subscribers/parameter_filter"
5
4
 
6
5
  module Sentry
7
6
  module Rails
@@ -16,13 +15,10 @@ module Sentry
16
15
  # @example Usage
17
16
  # # Enable structured logging for ActionController
18
17
  # Sentry.init do |config|
19
- # config.enable_logs = true
20
18
  # config.rails.structured_logging = true
21
19
  # config.rails.structured_logging.subscribers = { action_controller: Sentry::Rails::LogSubscribers::ActionControllerSubscriber }
22
20
  # end
23
21
  class ActionControllerSubscriber < Sentry::Rails::LogSubscriber
24
- include ParameterFilter
25
-
26
22
  # Handle process_action.action_controller events
27
23
  #
28
24
  # @param event [ActiveSupport::Notifications::Event] The controller action event
@@ -55,9 +51,9 @@ module Sentry
55
51
  attributes[:db_runtime_ms] = payload[:db_runtime].round(2)
56
52
  end
57
53
 
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?
54
+ if payload[:params].is_a?(Hash)
55
+ params = Sentry.configuration.data_collection.url_query_params.filter(payload[:params])
56
+ attributes[:params] = params unless params.empty?
61
57
  end
62
58
 
63
59
  level = level_for_request(payload)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/rails/log_subscriber"
4
- require "sentry/rails/log_subscribers/parameter_filter"
5
4
 
6
5
  module Sentry
7
6
  module Rails
@@ -15,13 +14,10 @@ module Sentry
15
14
  # @example Usage
16
15
  # # Enable structured logging for ActionMailer
17
16
  # Sentry.init do |config|
18
- # config.enable_logs = true
19
17
  # config.rails.structured_logging = true
20
18
  # config.rails.structured_logging.subscribers = { action_mailer: Sentry::Rails::LogSubscribers::ActionMailerSubscriber }
21
19
  # end
22
20
  class ActionMailerSubscriber < Sentry::Rails::LogSubscriber
23
- include ParameterFilter
24
-
25
21
  # Handle deliver.action_mailer events
26
22
  #
27
23
  # @param event [ActiveSupport::Notifications::Event] The email delivery event
@@ -41,8 +37,8 @@ module Sentry
41
37
  attributes[:delivery_method] = payload[:delivery_method] if payload[:delivery_method]
42
38
  attributes[:date] = payload[:date].to_s if payload[:date]
43
39
 
44
- if Sentry.configuration.send_default_pii
45
- attributes[:message_id] = payload[:message_id] if payload[:message_id]
40
+ if Sentry.configuration.data_collection.user_info && payload[:message_id]
41
+ attributes[:message_id] = payload[:message_id]
46
42
  end
47
43
 
48
44
  message = "Email delivered via #{mailer}"
@@ -73,9 +69,9 @@ module Sentry
73
69
  duration_ms: duration
74
70
  }
75
71
 
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?
72
+ if payload[:params].is_a?(Hash)
73
+ params = Sentry.configuration.data_collection.url_query_params.filter(payload[:params])
74
+ attributes[:params] = params unless params.empty?
79
75
  end
80
76
 
81
77
  message = "#{mailer}##{action}"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/rails/log_subscriber"
4
- require "sentry/rails/log_subscribers/parameter_filter"
5
4
 
6
5
  module Sentry
7
6
  module Rails
@@ -15,13 +14,10 @@ module Sentry
15
14
  # @example Usage
16
15
  # # Enable structured logging for ActiveJob
17
16
  # Sentry.init do |config|
18
- # config.enable_logs = true
19
17
  # config.rails.structured_logging = true
20
18
  # config.rails.structured_logging.subscribers = { active_job: Sentry::Rails::LogSubscribers::ActiveJobSubscriber }
21
19
  # end
22
20
  class ActiveJobSubscriber < Sentry::Rails::LogSubscriber
23
- include ParameterFilter
24
-
25
21
  # Handle perform.active_job events
26
22
  #
27
23
  # @param event [ActiveSupport::Notifications::Event] The job performance event
@@ -47,7 +43,7 @@ module Sentry
47
43
  attributes[:delay_ms] = ((Time.current - job.scheduled_at) * 1000).round(2)
48
44
  end
49
45
 
50
- if Sentry.configuration.send_default_pii && job.arguments.present?
46
+ if Sentry.configuration.data_collection.queues && job.arguments.present?
51
47
  filtered_args = filter_sensitive_arguments(job.arguments)
52
48
  attributes[:arguments] = filtered_args unless filtered_args.empty?
53
49
  end
@@ -149,9 +145,11 @@ module Sentry
149
145
  arguments.map do |arg|
150
146
  case arg
151
147
  when Hash
152
- filter_sensitive_params(arg)
148
+ # we're using url_query_params here since rails filter_parameters end up there
149
+ # and we don't have a dedicated queue params config yet
150
+ Sentry.configuration.data_collection.url_query_params.filter(arg, cookie: false)
153
151
  when String
154
- arg.length > 100 ? "[FILTERED: #{arg.length} chars]" : arg
152
+ arg.length > 100 ? "[Filtered: #{arg.length} chars]" : arg
155
153
  else
156
154
  arg
157
155
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/rails/log_subscriber"
4
- require "sentry/rails/log_subscribers/parameter_filter"
5
4
 
6
5
  module Sentry
7
6
  module Rails
@@ -16,13 +15,10 @@ module Sentry
16
15
  # @example Usage
17
16
  # # Automatically attached when structured logging is enabled for :active_record
18
17
  # Sentry.init do |config|
19
- # config.enable_logs = true
20
18
  # config.rails.structured_logging = true
21
19
  # config.rails.structured_logging.subscribers = { active_record: Sentry::Rails::LogSubscribers::ActiveRecordSubscriber }
22
20
  # end
23
21
  class ActiveRecordSubscriber < Sentry::Rails::LogSubscriber
24
- include ParameterFilter
25
-
26
22
  EXCLUDED_NAMES = ["SCHEMA", "TRANSACTION"].freeze
27
23
  EMPTY_ARRAY = [].freeze
28
24
 
@@ -49,7 +45,7 @@ module Sentry
49
45
 
50
46
  binds = event.payload[:binds]
51
47
 
52
- if Sentry.configuration.send_default_pii && (binds && !binds.empty?)
48
+ if Sentry.configuration.data_collection.database_query_data && (binds && !binds.empty?)
53
49
  type_casted_binds = type_casted_binds(event)
54
50
 
55
51
  type_casted_binds.each_with_index do |value, index|
@@ -21,6 +21,13 @@ module Sentry
21
21
  ActiveSupport.on_load(:active_job) do
22
22
  require "sentry/rails/active_job"
23
23
  prepend Sentry::Rails::ActiveJobExtensions
24
+
25
+ unless Sentry::Rails::ActiveJobExtensions::SentryReporter.producer_callback_registered?
26
+ around_enqueue do |job, block|
27
+ Sentry::Rails::ActiveJobExtensions::SentryReporter.record_producer_span(job, &block)
28
+ end
29
+ Sentry::Rails::ActiveJobExtensions::SentryReporter.producer_callback_registered!
30
+ end
24
31
  end
25
32
  end
26
33
 
@@ -135,7 +142,7 @@ module Sentry
135
142
  end
136
143
 
137
144
  def activate_structured_logging
138
- if Sentry.configuration.rails.structured_logging.enabled? && Sentry.configuration.enable_logs
145
+ if Sentry.configuration.rails.structured_logging.enabled?
139
146
  Sentry::Rails::StructuredLogging.attach(Sentry.configuration.rails.structured_logging)
140
147
  end
141
148
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Rails
5
+ module Serializer
6
+ def self.serialize(value)
7
+ case value
8
+ when Range
9
+ serialize_range(value)
10
+ when Hash
11
+ value.transform_values { |item| serialize(item) }
12
+ when Array, Enumerable
13
+ value.map { |item| serialize(item) }
14
+ when ->(item) { item.respond_to?(:to_global_id) }
15
+ serialize_global_id(value)
16
+ else
17
+ value
18
+ end
19
+ end
20
+
21
+ def self.serialize_global_id(value)
22
+ value.to_global_id.to_s
23
+ rescue StandardError
24
+ value
25
+ end
26
+
27
+ def self.serialize_range(range)
28
+ return range.to_s if range.begin.nil? || range.end.nil?
29
+ return range.to_s if range.begin.is_a?(::ActiveSupport::TimeWithZone)
30
+
31
+ range.map { |item| serialize(item) }
32
+ end
33
+ end
34
+ end
35
+ end