sentry-ruby 5.26.0 → 6.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -4
  3. data/lib/sentry/background_worker.rb +1 -4
  4. data/lib/sentry/breadcrumb.rb +1 -1
  5. data/lib/sentry/breadcrumb_buffer.rb +2 -2
  6. data/lib/sentry/check_in_event.rb +2 -2
  7. data/lib/sentry/client.rb +29 -89
  8. data/lib/sentry/configuration.rb +125 -78
  9. data/lib/sentry/cron/monitor_check_ins.rb +3 -3
  10. data/lib/sentry/cron/monitor_config.rb +2 -2
  11. data/lib/sentry/cron/monitor_schedule.rb +2 -2
  12. data/lib/sentry/debug_structured_logger.rb +94 -0
  13. data/lib/sentry/dsn.rb +32 -0
  14. data/lib/sentry/envelope/item.rb +1 -2
  15. data/lib/sentry/error_event.rb +3 -3
  16. data/lib/sentry/event.rb +4 -10
  17. data/lib/sentry/graphql.rb +1 -1
  18. data/lib/sentry/hub.rb +6 -5
  19. data/lib/sentry/interface.rb +1 -1
  20. data/lib/sentry/interfaces/exception.rb +2 -2
  21. data/lib/sentry/interfaces/request.rb +2 -0
  22. data/lib/sentry/interfaces/single_exception.rb +3 -3
  23. data/lib/sentry/interfaces/stacktrace.rb +3 -3
  24. data/lib/sentry/interfaces/stacktrace_builder.rb +0 -8
  25. data/lib/sentry/interfaces/threads.rb +2 -2
  26. data/lib/sentry/log_event.rb +19 -6
  27. data/lib/sentry/profiler.rb +4 -5
  28. data/lib/sentry/propagation_context.rb +55 -18
  29. data/lib/sentry/rspec.rb +1 -1
  30. data/lib/sentry/span.rb +2 -17
  31. data/lib/sentry/std_lib_logger.rb +6 -1
  32. data/lib/sentry/test_helper.rb +23 -0
  33. data/lib/sentry/transaction.rb +72 -95
  34. data/lib/sentry/transaction_event.rb +4 -9
  35. data/lib/sentry/transport/debug_transport.rb +70 -0
  36. data/lib/sentry/transport/dummy_transport.rb +1 -0
  37. data/lib/sentry/transport/http_transport.rb +9 -5
  38. data/lib/sentry/transport.rb +3 -5
  39. data/lib/sentry/utils/logging_helper.rb +8 -6
  40. data/lib/sentry/utils/sample_rand.rb +97 -0
  41. data/lib/sentry/vernier/profiler.rb +4 -3
  42. data/lib/sentry/version.rb +1 -1
  43. data/lib/sentry-ruby.rb +6 -30
  44. data/sentry-ruby-core.gemspec +1 -1
  45. data/sentry-ruby.gemspec +1 -1
  46. metadata +11 -18
  47. data/lib/sentry/metrics/aggregator.rb +0 -248
  48. data/lib/sentry/metrics/configuration.rb +0 -47
  49. data/lib/sentry/metrics/counter_metric.rb +0 -25
  50. data/lib/sentry/metrics/distribution_metric.rb +0 -25
  51. data/lib/sentry/metrics/gauge_metric.rb +0 -35
  52. data/lib/sentry/metrics/local_aggregator.rb +0 -53
  53. data/lib/sentry/metrics/metric.rb +0 -19
  54. data/lib/sentry/metrics/set_metric.rb +0 -28
  55. data/lib/sentry/metrics/timing.rb +0 -51
  56. data/lib/sentry/metrics.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb03eb536d1060c3a097a1ff3726ae71c8ffbd0012580d75c491665e054ac7c1
4
- data.tar.gz: 65df577369b281f3e1d1d9335e78786a800947c2014b35608d88c863458f24ca
3
+ metadata.gz: b0a6e7b2113d1332eaaf78dc5b8a4989b0a1395796a43fb1f7225cc8115f0ac5
4
+ data.tar.gz: 64aaefd79ec8d357586ec99cea6f21d749f21a5fc80226b9a554f5f6a3f25870
5
5
  SHA512:
6
- metadata.gz: b4e398e02a8fe619afc8982760c290a168785c4557dca30767fbeb039032d7907135e84d0598a5ac90b7b1cfb8413ceda83ebe4647702ba5c10c613ff0543574
7
- data.tar.gz: 285a3e0f650a41bcb6cc4539f48c7efcb9bc3442f31c76f301089c4672e6d9c790f407dd9d53db4e216df4eacd9be019b5bc46e453437fa1614aa48555541c9a
6
+ metadata.gz: 4db52a4de3aada20c59eec1f949d8577fa2acf6a1772ca450fca97ddbbbf6676654ee60a585c82af020d54bd24b5ccc593dcad86e54e44bfcaafc6d385e4fac5
7
+ data.tar.gz: d3489ef60748f864cb4ad59f5f823ad5ca6a3ff6aee54e6c859a140af4947bffca91c37ae1dca5ee9d46d18b2b4e33f1df69085d328168eed96842443fd00719
data/Gemfile CHANGED
@@ -3,7 +3,7 @@
3
3
  source "https://rubygems.org"
4
4
  git_source(:github) { |name| "https://github.com/#{name}.git" }
5
5
 
6
- eval_gemfile "../Gemfile"
6
+ eval_gemfile "../Gemfile.dev"
7
7
 
8
8
  gem "sentry-ruby", path: "./"
9
9
 
@@ -11,8 +11,6 @@ rack_version = ENV["RACK_VERSION"]
11
11
  rack_version = "3.0.0" if rack_version.nil?
12
12
  gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
13
13
 
14
- gem "ostruct" if RUBY_VERSION >= "3.4"
15
-
16
14
  redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
17
15
  gem "redis", "~> #{redis_rb_version}"
18
16
 
@@ -22,7 +20,7 @@ gem "timecop"
22
20
  gem "stackprof" unless RUBY_PLATFORM == "java"
23
21
  gem "vernier", platforms: :ruby if RUBY_VERSION >= "3.2.1"
24
22
 
25
- gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7
23
+ gem "graphql", ">= 2.2.6"
26
24
 
27
25
  gem "benchmark-ips"
28
26
  gem "benchmark_driver"
@@ -23,10 +23,7 @@ module Sentry
23
23
  @shutdown_callback = nil
24
24
 
25
25
  @executor =
26
- if configuration.async
27
- log_debug("config.async is set, BackgroundWorker is disabled")
28
- Concurrent::ImmediateExecutor.new
29
- elsif @number_of_threads == 0
26
+ if @number_of_threads == 0
30
27
  log_debug("config.background_worker_threads is set to 0, all events will be sent synchronously")
31
28
  Concurrent::ImmediateExecutor.new
32
29
  else
@@ -34,7 +34,7 @@ module Sentry
34
34
  end
35
35
 
36
36
  # @return [Hash]
37
- def to_hash
37
+ def to_h
38
38
  {
39
39
  category: @category,
40
40
  data: serialized_data,
@@ -48,9 +48,9 @@ module Sentry
48
48
  end
49
49
 
50
50
  # @return [Hash]
51
- def to_hash
51
+ def to_h
52
52
  {
53
- values: members.map(&:to_hash)
53
+ values: members.map(&:to_h)
54
54
  }
55
55
  end
56
56
 
@@ -48,13 +48,13 @@ module Sentry
48
48
  end
49
49
 
50
50
  # @return [Hash]
51
- def to_hash
51
+ def to_h
52
52
  data = super
53
53
  data[:check_in_id] = check_in_id
54
54
  data[:monitor_slug] = monitor_slug
55
55
  data[:status] = status
56
56
  data[:duration] = duration if duration
57
- data[:monitor_config] = monitor_config.to_hash if monitor_config
57
+ data[:monitor_config] = monitor_config.to_h if monitor_config
58
58
  data
59
59
  end
60
60
  end
data/lib/sentry/client.rb CHANGED
@@ -60,8 +60,7 @@ module Sentry
60
60
  return
61
61
  end
62
62
 
63
- event_type = event.is_a?(Event) ? event.type : event["type"]
64
- data_category = Envelope::Item.data_category(event_type)
63
+ data_category = Envelope::Item.data_category(event.type)
65
64
 
66
65
  is_transaction = event.is_a?(TransactionEvent)
67
66
  spans_before = is_transaction ? event.spans.size : 0
@@ -78,9 +77,7 @@ module Sentry
78
77
  transport.record_lost_event(:event_processor, "span", num: spans_delta) if spans_delta > 0
79
78
  end
80
79
 
81
- if async_block = configuration.async
82
- dispatch_async_event(async_block, event, hint)
83
- elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
80
+ if configuration.background_worker_threads != 0 && hint.fetch(:background, true)
84
81
  unless dispatch_background_event(event, hint)
85
82
  transport.record_lost_event(:queue_overflow, data_category)
86
83
  transport.record_lost_event(:queue_overflow, "span", num: spans_before + 1) if is_transaction
@@ -195,9 +192,10 @@ module Sentry
195
192
  def event_from_log(message, level:, **options)
196
193
  return unless configuration.sending_allowed?
197
194
 
198
- attributes = options.reject { |k, _| k == :level || k == :severity }
195
+ attributes = options.reject { |k, _| k == :level || k == :severity || k == :origin }
196
+ origin = options[:origin]
199
197
 
200
- LogEvent.new(level: level, body: message, attributes: attributes)
198
+ LogEvent.new(level: level, body: message, attributes: attributes, origin: origin)
201
199
  end
202
200
 
203
201
  # Initializes an Event object with the given Transaction object.
@@ -209,22 +207,13 @@ module Sentry
209
207
 
210
208
  # @!macro send_event
211
209
  def send_event(event, hint = nil)
212
- event_type = event.is_a?(Event) ? event.type : event["type"]
213
- data_category = Envelope::Item.data_category(event_type)
210
+ data_category = Envelope::Item.data_category(event.type)
214
211
  spans_before = event.is_a?(TransactionEvent) ? event.spans.size : 0
215
212
 
216
- if event_type != TransactionEvent::TYPE && configuration.before_send
213
+ if event.is_a?(ErrorEvent) && configuration.before_send
217
214
  event = configuration.before_send.call(event, hint)
218
215
 
219
- case event
220
- when ErrorEvent, CheckInEvent
221
- # do nothing
222
- when Hash
223
- log_debug(<<~MSG)
224
- Returning a Hash from before_send is deprecated and will be removed in the next major version.
225
- Please return a Sentry::ErrorEvent object instead.
226
- MSG
227
- else
216
+ if !event.is_a?(ErrorEvent)
228
217
  # Avoid serializing the event object in this case because we aren't sure what it is and what it contains
229
218
  log_debug(<<~MSG)
230
219
  Discarded event because before_send didn't return a Sentry::ErrorEvent object but an instance of #{event.class}
@@ -234,21 +223,10 @@ module Sentry
234
223
  end
235
224
  end
236
225
 
237
- if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
226
+ if event.is_a?(TransactionEvent) && configuration.before_send_transaction
238
227
  event = configuration.before_send_transaction.call(event, hint)
239
228
 
240
- if event.is_a?(TransactionEvent) || event.is_a?(Hash)
241
- spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
242
- spans_delta = spans_before - spans_after
243
- transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
244
-
245
- if event.is_a?(Hash)
246
- log_debug(<<~MSG)
247
- Returning a Hash from before_send_transaction is deprecated and will be removed in the next major version.
248
- Please return a Sentry::TransactionEvent object instead.
249
- MSG
250
- end
251
- else
229
+ if !event.is_a?(TransactionEvent)
252
230
  # Avoid serializing the event object in this case because we aren't sure what it is and what it contains
253
231
  log_debug(<<~MSG)
254
232
  Discarded event because before_send_transaction didn't return a Sentry::TransactionEvent object but an instance of #{event.class}
@@ -257,6 +235,23 @@ module Sentry
257
235
  transport.record_lost_event(:before_send, "span", num: spans_before + 1)
258
236
  return
259
237
  end
238
+
239
+ spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
240
+ spans_delta = spans_before - spans_after
241
+ transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
242
+ end
243
+
244
+ if event.is_a?(CheckInEvent) && configuration.before_send_check_in
245
+ event = configuration.before_send_check_in.call(event, hint)
246
+
247
+ if !event.is_a?(CheckInEvent)
248
+ # Avoid serializing the event object in this case because we aren't sure what it is and what it contains
249
+ log_debug(<<~MSG)
250
+ Discarded event because before_send_check_in didn't return a Sentry::CheckInEvent object but an instance of #{event.class}
251
+ MSG
252
+ transport.record_lost_event(:before_send, data_category)
253
+ return
254
+ end
260
255
  end
261
256
 
262
257
  transport.send_event(event) if configuration.sending_to_dsn_allowed?
@@ -290,7 +285,7 @@ module Sentry
290
285
  processed_log_event = configuration.before_send_log.call(log_event)
291
286
 
292
287
  if processed_log_event
293
- envelope_items << processed_log_event.to_hash
288
+ envelope_items << processed_log_event.to_h
294
289
  else
295
290
  discarded_count += 1
296
291
  end
@@ -298,7 +293,7 @@ module Sentry
298
293
 
299
294
  envelope_items
300
295
  else
301
- envelope_items = log_events.map(&:to_hash)
296
+ envelope_items = log_events.map(&:to_h)
302
297
  end
303
298
 
304
299
  envelope.add_item(
@@ -333,38 +328,6 @@ module Sentry
333
328
  raise
334
329
  end
335
330
 
336
- # @deprecated use Sentry.get_traceparent instead.
337
- #
338
- # Generates a Sentry trace for distribted tracing from the given Span.
339
- # Returns `nil` if `config.propagate_traces` is `false`.
340
- # @param span [Span] the span to generate trace from.
341
- # @return [String, nil]
342
- def generate_sentry_trace(span)
343
- return unless configuration.propagate_traces
344
-
345
- trace = span.to_sentry_trace
346
- log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
347
- trace
348
- end
349
-
350
- # @deprecated Use Sentry.get_baggage instead.
351
- #
352
- # Generates a W3C Baggage header for distributed tracing from the given Span.
353
- # Returns `nil` if `config.propagate_traces` is `false`.
354
- # @param span [Span] the span to generate trace from.
355
- # @return [String, nil]
356
- def generate_baggage(span)
357
- return unless configuration.propagate_traces
358
-
359
- baggage = span.to_baggage
360
-
361
- if baggage && !baggage.empty?
362
- log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
363
- end
364
-
365
- baggage
366
- end
367
-
368
331
  private
369
332
 
370
333
  def dispatch_background_event(event, hint)
@@ -372,28 +335,5 @@ module Sentry
372
335
  send_event(event, hint)
373
336
  end
374
337
  end
375
-
376
- def dispatch_async_event(async_block, event, hint)
377
- # We have to convert to a JSON-like hash, because background job
378
- # processors (esp ActiveJob) may not like weird types in the event hash
379
-
380
- event_hash =
381
- begin
382
- event.to_json_compatible
383
- rescue => e
384
- log_error("Converting #{event.type} (#{event.event_id}) to JSON compatible hash failed", e, debug: configuration.debug)
385
- return
386
- end
387
-
388
- if async_block.arity == 2
389
- hint = JSON.parse(JSON.generate(hint))
390
- async_block.call(event_hash, hint)
391
- else
392
- async_block.call(event_hash)
393
- end
394
- rescue => e
395
- log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
396
- send_event(event, hint)
397
- end
398
338
  end
399
339
  end
@@ -9,10 +9,10 @@ require "sentry/dsn"
9
9
  require "sentry/release_detector"
10
10
  require "sentry/transport/configuration"
11
11
  require "sentry/cron/configuration"
12
- require "sentry/metrics/configuration"
13
12
  require "sentry/linecache"
14
13
  require "sentry/interfaces/stacktrace_builder"
15
14
  require "sentry/logger"
15
+ require "sentry/structured_logger"
16
16
  require "sentry/log_event_buffer"
17
17
 
18
18
  module Sentry
@@ -30,13 +30,6 @@ module Sentry
30
30
  # @return [Regexp, nil]
31
31
  attr_accessor :app_dirs_pattern
32
32
 
33
- # Provide an object that responds to `call` to send events asynchronously.
34
- # E.g.: lambda { |event| Thread.new { Sentry.send_event(event) } }
35
- #
36
- # @deprecated It will be removed in the next major release. Please read https://github.com/getsentry/sentry-ruby/issues/1522 for more information
37
- # @return [Proc, nil]
38
- attr_reader :async
39
-
40
33
  # to send events in a non-blocking way, sentry-ruby has its own background worker
41
34
  # by default, the worker holds a thread pool that has [the number of processors] threads
42
35
  # but you can configure it with this configuration option
@@ -74,11 +67,10 @@ module Sentry
74
67
  # @return [Proc]
75
68
  attr_reader :before_breadcrumb
76
69
 
77
- # Optional Proc, called before sending an event to the server
70
+ # Optional Proc, called before sending an error event to the server
78
71
  # @example
79
72
  # config.before_send = lambda do |event, hint|
80
73
  # # skip ZeroDivisionError exceptions
81
- # # note: hint[:exception] would be a String if you use async callback
82
74
  # if hint[:exception].is_a?(ZeroDivisionError)
83
75
  # nil
84
76
  # else
@@ -88,7 +80,7 @@ module Sentry
88
80
  # @return [Proc]
89
81
  attr_reader :before_send
90
82
 
91
- # Optional Proc, called before sending an event to the server
83
+ # Optional Proc, called before sending a transaction event to the server
92
84
  # @example
93
85
  # config.before_send_transaction = lambda do |event, hint|
94
86
  # # skip unimportant transactions or strip sensitive data
@@ -101,6 +93,18 @@ module Sentry
101
93
  # @return [Proc]
102
94
  attr_reader :before_send_transaction
103
95
 
96
+ # Optional Proc, called before sending a check-in event to the server
97
+ # @example
98
+ # config.before_send_check_in = lambda do |event, hint|
99
+ # if event.monitor_slug == "unimportant_job"
100
+ # nil
101
+ # else
102
+ # event
103
+ # end
104
+ # end
105
+ # @return [Proc]
106
+ attr_reader :before_send_check_in
107
+
104
108
  # Optional Proc, called before sending an event to the server
105
109
  # @example
106
110
  # config.before_send_log = lambda do |log|
@@ -117,7 +121,6 @@ module Sentry
117
121
  #
118
122
  # And if you also use sentry-rails:
119
123
  # - :active_support_logger
120
- # - :monotonic_active_support_logger
121
124
  #
122
125
  # @return [Array<Symbol>]
123
126
  attr_reader :breadcrumbs_logger
@@ -144,7 +147,7 @@ module Sentry
144
147
  attr_reader :dsn
145
148
 
146
149
  # Whitelist of enabled_environments that will send notifications to Sentry. Array of Strings.
147
- # @return [Array<String>]
150
+ # @return [Array<String>, nil]
148
151
  attr_accessor :enabled_environments
149
152
 
150
153
  # Logger 'progname's to exclude from breadcrumbs
@@ -173,18 +176,6 @@ module Sentry
173
176
  # @return [Boolean, String]
174
177
  attr_accessor :spotlight
175
178
 
176
- # @deprecated Use {#include_local_variables} instead.
177
- alias_method :capture_exception_frame_locals, :include_local_variables
178
-
179
- # @deprecated Use {#include_local_variables=} instead.
180
- def capture_exception_frame_locals=(value)
181
- log_warn <<~MSG
182
- `capture_exception_frame_locals` is now deprecated in favor of `include_local_variables`.
183
- MSG
184
-
185
- self.include_local_variables = value
186
- end
187
-
188
179
  # You may provide your own LineCache for matching paths with source files.
189
180
  # This may be useful if you need to get source code from places other than the disk.
190
181
  # @see LineCache
@@ -196,17 +187,10 @@ module Sentry
196
187
  # @return [Logger]
197
188
  attr_accessor :sdk_logger
198
189
 
199
- # @deprecated Use {#sdk_logger=} instead.
200
- def logger=(logger)
201
- warn "[sentry] `config.logger=` is deprecated. Please use `config.sdk_logger=` instead."
202
- self.sdk_logger = logger
203
- end
204
-
205
- # @deprecated Use {#sdk_logger} instead.
206
- def logger
207
- warn "[sentry] `config.logger` is deprecated. Please use `config.sdk_logger` instead."
208
- self.sdk_logger
209
- end
190
+ # File path for DebugTransport to log events to. If not set, defaults to a temporary file.
191
+ # This is useful for debugging and testing purposes.
192
+ # @return [String, nil]
193
+ attr_accessor :sdk_debug_transport_log_file
210
194
 
211
195
  # Project directory root for in_app detection. Could be Rails root, etc.
212
196
  # Set automatically for Rails.
@@ -267,10 +251,6 @@ module Sentry
267
251
  # @return [Cron::Configuration]
268
252
  attr_reader :cron
269
253
 
270
- # Metrics related configuration.
271
- # @return [Metrics::Configuration]
272
- attr_reader :metrics
273
-
274
254
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
275
255
  # @return [Float, nil]
276
256
  attr_reader :traces_sample_rate
@@ -289,11 +269,9 @@ module Sentry
289
269
  # @return [Boolean]
290
270
  attr_accessor :enable_logs
291
271
 
292
- # Easier way to use performance tracing
293
- # If set to true, will set traces_sample_rate to 1.0
294
- # @deprecated It will be removed in the next major release.
295
- # @return [Boolean, nil]
296
- attr_reader :enable_tracing
272
+ # Structured logging configuration.
273
+ # @return [StructuredLoggingConfiguration]
274
+ attr_reader :structured_logging
297
275
 
298
276
  # Send diagnostic client reports about dropped events, true by default
299
277
  # tries to attach to an existing envelope max once every 30s
@@ -315,6 +293,18 @@ module Sentry
315
293
  # @return [Array<String, Regexp>]
316
294
  attr_accessor :trace_propagation_targets
317
295
 
296
+ # Collection of HTTP status codes or ranges of codes to ignore when tracing incoming requests.
297
+ # If a transaction's http.response.status_code matches one of these values,
298
+ # the transaction will be dropped and marked as not sampled.
299
+ # Defaults to TRACE_IGNORE_STATUS_CODES_DEFAULT.
300
+ #
301
+ # @example
302
+ # # ignore 404 and 502 <= status_code <= 511
303
+ # config.trace_ignore_status_codes = [404, (502..511)]
304
+ #
305
+ # @return [Array<Integer>, Array<Range>]
306
+ attr_reader :trace_ignore_status_codes
307
+
318
308
  # The instrumenter to use, :sentry or :otel
319
309
  # @return [Symbol]
320
310
  attr_reader :instrumenter
@@ -329,6 +319,15 @@ module Sentry
329
319
  # @return [Float, nil]
330
320
  attr_reader :profiles_sample_rate
331
321
 
322
+ # Interval in microseconds at which to take samples.
323
+ # The default is 1e6 / 101, or 101Hz.
324
+ # Note that the 101 is intentional to avoid lockstep sampling.
325
+ #
326
+ # @example
327
+ # config.profiles_sample_interval = 1e5 / 101
328
+ # @return [Float]
329
+ attr_accessor :profiles_sample_interval
330
+
332
331
  # Array of patches to apply.
333
332
  # Default is {DEFAULT_PATCHES}
334
333
  # @return [Array<Symbol>]
@@ -366,6 +365,8 @@ module Sentry
366
365
  SERVER_PORT
367
366
  ].freeze
368
367
 
368
+ TRACE_IGNORE_STATUS_CODES_DEFAULT = [(301..303), (305..399), (401..404)]
369
+
369
370
  HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
370
371
  "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
371
372
 
@@ -381,6 +382,9 @@ module Sentry
381
382
 
382
383
  APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
383
384
 
385
+ # 101 Hz in microseconds
386
+ DEFAULT_PROFILES_SAMPLE_INTERVAL = 1e6 / 101
387
+
384
388
  class << self
385
389
  # Post initialization callbacks are called at the end of initialization process
386
390
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -390,7 +394,23 @@ module Sentry
390
394
 
391
395
  # allow extensions to add their hooks to the Configuration class
392
396
  def add_post_initialization_callback(&block)
393
- post_initialization_callbacks << block
397
+ callbacks[:initialize][:after] << block
398
+ end
399
+
400
+ def before(event, &block)
401
+ callbacks[event.to_sym][:before] << block
402
+ end
403
+
404
+ def after(event, &block)
405
+ callbacks[event.to_sym][:after] << block
406
+ end
407
+
408
+ # @!visibility private
409
+ def callbacks
410
+ @callbacks ||= {
411
+ initialize: { before: [], after: [] },
412
+ configured: { before: [], after: [] }
413
+ }
394
414
  end
395
415
 
396
416
  def validations
@@ -434,6 +454,8 @@ module Sentry
434
454
  validate :profiles_sample_rate, optional: true, type: :numeric
435
455
 
436
456
  def initialize
457
+ run_callbacks(:before, :initialize)
458
+
437
459
  self.app_dirs_pattern = APP_DIRS_PATTERN
438
460
  self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
439
461
  self.background_worker_threads = (processor_count / 2.0).ceil
@@ -445,7 +467,7 @@ module Sentry
445
467
  self.context_lines = 3
446
468
  self.include_local_variables = false
447
469
  self.environment = environment_from_env
448
- self.enabled_environments = []
470
+ self.enabled_environments = nil
449
471
  self.exclude_loggers = []
450
472
  self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
451
473
  self.inspect_exception_causes_for_exclusion = true
@@ -470,26 +492,32 @@ module Sentry
470
492
  self.server_name = server_name_from_env
471
493
  self.instrumenter = :sentry
472
494
  self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
495
+ self.trace_ignore_status_codes = TRACE_IGNORE_STATUS_CODES_DEFAULT
473
496
  self.enabled_patches = DEFAULT_PATCHES.dup
474
497
 
475
498
  self.before_send = nil
476
499
  self.before_send_transaction = nil
500
+ self.before_send_check_in = nil
477
501
  self.before_send_log = nil
478
502
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
479
503
  self.traces_sampler = nil
480
- self.enable_tracing = nil
481
504
  self.enable_logs = false
482
505
 
483
506
  self.profiler_class = Sentry::Profiler
507
+ self.profiles_sample_interval = DEFAULT_PROFILES_SAMPLE_INTERVAL
484
508
 
485
509
  @transport = Transport::Configuration.new
486
510
  @cron = Cron::Configuration.new
487
- @metrics = Metrics::Configuration.new
511
+ @structured_logging = StructuredLoggingConfiguration.new
488
512
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
489
513
 
490
- run_post_initialization_callbacks
491
-
492
514
  self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
515
+
516
+ run_callbacks(:after, :initialize)
517
+
518
+ yield(self) if block_given?
519
+
520
+ run_callbacks(:after, :configured)
493
521
  end
494
522
 
495
523
  def validate
@@ -522,22 +550,6 @@ module Sentry
522
550
  @release = value
523
551
  end
524
552
 
525
- def async=(value)
526
- check_callable!("async", value)
527
-
528
- log_warn <<~MSG
529
-
530
- sentry-ruby now sends events asynchronously by default with its background worker (supported since 4.1.0).
531
- The `config.async` callback has become redundant while continuing to cause issues.
532
- (The problems of `async` are detailed in https://github.com/getsentry/sentry-ruby/issues/1522)
533
-
534
- Therefore, we encourage you to remove it and let the background worker take care of async job sending.
535
- It's deprecation is planned in the next major release (6.0), which is scheduled around the 3rd quarter of 2022.
536
- MSG
537
-
538
- @async = value
539
- end
540
-
541
553
  def breadcrumbs_logger=(logger)
542
554
  loggers =
543
555
  if logger.is_a?(Array)
@@ -563,6 +575,12 @@ module Sentry
563
575
  @before_send_transaction = value
564
576
  end
565
577
 
578
+ def before_send_check_in=(value)
579
+ check_callable!("before_send_check_in", value)
580
+
581
+ @before_send_check_in = value
582
+ end
583
+
566
584
  def before_breadcrumb=(value)
567
585
  check_callable!("before_breadcrumb", value)
568
586
 
@@ -577,15 +595,12 @@ module Sentry
577
595
  @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
578
596
  end
579
597
 
580
- def enable_tracing=(enable_tracing)
581
- unless enable_tracing.nil?
582
- log_warn <<~MSG
583
- `enable_tracing` is now deprecated in favor of `traces_sample_rate = 1.0`.
584
- MSG
598
+ def trace_ignore_status_codes=(codes)
599
+ unless codes.is_a?(Array) && codes.all? { |code| valid_status_code_entry?(code) }
600
+ raise ArgumentError, "trace_ignore_status_codes must be an Array of integers or ranges between (100-599) where begin <= end"
585
601
  end
586
602
 
587
- @enable_tracing = enable_tracing
588
- @traces_sample_rate ||= 1.0 if enable_tracing
603
+ @trace_ignore_status_codes = codes
589
604
  end
590
605
 
591
606
  def traces_sample_rate=(traces_sample_rate)
@@ -641,7 +656,7 @@ module Sentry
641
656
  end
642
657
 
643
658
  def enabled_in_current_env?
644
- enabled_environments.empty? || enabled_environments.include?(environment)
659
+ enabled_environments.nil? || enabled_environments.include?(environment)
645
660
  end
646
661
 
647
662
  def valid_sample_rate?(sample_rate)
@@ -652,7 +667,7 @@ module Sentry
652
667
  def tracing_enabled?
653
668
  valid_sampler = !!((valid_sample_rate?(@traces_sample_rate)) || @traces_sampler)
654
669
 
655
- (@enable_tracing != false) && valid_sampler && sending_allowed?
670
+ valid_sampler && sending_allowed?
656
671
  end
657
672
 
658
673
  def profiling_enabled?
@@ -773,8 +788,8 @@ module Sentry
773
788
  File.directory?("/etc/heroku") && !ENV["CI"]
774
789
  end
775
790
 
776
- def run_post_initialization_callbacks
777
- self.class.post_initialization_callbacks.each do |hook|
791
+ def run_callbacks(hook, event)
792
+ self.class.callbacks[event][hook].each do |hook|
778
793
  instance_eval(&hook)
779
794
  end
780
795
  end
@@ -783,5 +798,37 @@ module Sentry
783
798
  available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count)
784
799
  available_processor_count || Concurrent.processor_count
785
800
  end
801
+
802
+ def valid_http_status_code?(code)
803
+ code.is_a?(Integer) && code >= 100 && code <= 599
804
+ end
805
+
806
+ def valid_status_code_entry?(entry)
807
+ case entry
808
+ when Integer
809
+ valid_http_status_code?(entry)
810
+ when Range
811
+ valid_http_status_code?(entry.begin) &&
812
+ valid_http_status_code?(entry.end) &&
813
+ entry.begin <= entry.end
814
+ else
815
+ false
816
+ end
817
+ end
818
+ end
819
+
820
+ class StructuredLoggingConfiguration
821
+ # File path for DebugStructuredLogger to log events to
822
+ # @return [String, Pathname, nil]
823
+ attr_accessor :file_path
824
+
825
+ # The class to use as a structured logger.
826
+ # @return [Class]
827
+ attr_accessor :logger_class
828
+
829
+ def initialize
830
+ @file_path = nil
831
+ @logger_class = Sentry::StructuredLogger
832
+ end
786
833
  end
787
834
  end