sentry-ruby-core 4.7.3 → 4.8.3

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/Gemfile +6 -2
  4. data/README.md +7 -7
  5. data/bin/console +5 -1
  6. data/lib/sentry/background_worker.rb +30 -2
  7. data/lib/sentry/backtrace.rb +1 -0
  8. data/lib/sentry/breadcrumb/sentry_logger.rb +2 -0
  9. data/lib/sentry/breadcrumb.rb +2 -0
  10. data/lib/sentry/breadcrumb_buffer.rb +2 -0
  11. data/lib/sentry/client.rb +13 -1
  12. data/lib/sentry/configuration.rb +136 -112
  13. data/lib/sentry/core_ext/object/deep_dup.rb +2 -0
  14. data/lib/sentry/core_ext/object/duplicable.rb +1 -0
  15. data/lib/sentry/dsn.rb +2 -0
  16. data/lib/sentry/envelope.rb +26 -0
  17. data/lib/sentry/event.rb +5 -0
  18. data/lib/sentry/exceptions.rb +2 -0
  19. data/lib/sentry/hub.rb +7 -0
  20. data/lib/sentry/integrable.rb +2 -0
  21. data/lib/sentry/interface.rb +2 -0
  22. data/lib/sentry/interfaces/exception.rb +2 -0
  23. data/lib/sentry/interfaces/single_exception.rb +31 -0
  24. data/lib/sentry/interfaces/stacktrace.rb +10 -0
  25. data/lib/sentry/interfaces/stacktrace_builder.rb +2 -0
  26. data/lib/sentry/interfaces/threads.rb +2 -0
  27. data/lib/sentry/linecache.rb +3 -0
  28. data/lib/sentry/net/http.rb +3 -0
  29. data/lib/sentry/rack/capture_exceptions.rb +2 -0
  30. data/lib/sentry/rack.rb +2 -0
  31. data/lib/sentry/rake.rb +16 -6
  32. data/lib/sentry/release_detector.rb +39 -0
  33. data/lib/sentry/scope.rb +8 -4
  34. data/lib/sentry/span.rb +1 -0
  35. data/lib/sentry/transaction.rb +6 -1
  36. data/lib/sentry/transport/configuration.rb +2 -0
  37. data/lib/sentry/transport/dummy_transport.rb +2 -0
  38. data/lib/sentry/transport/http_transport.rb +6 -4
  39. data/lib/sentry/transport.rb +77 -19
  40. data/lib/sentry/utils/argument_checking_helper.rb +2 -0
  41. data/lib/sentry/utils/custom_inspection.rb +14 -0
  42. data/lib/sentry/utils/exception_cause_chain.rb +10 -10
  43. data/lib/sentry/utils/logging_helper.rb +2 -0
  44. data/lib/sentry/utils/real_ip.rb +2 -0
  45. data/lib/sentry/utils/request_id.rb +2 -0
  46. data/lib/sentry/version.rb +3 -1
  47. data/lib/sentry-ruby.rb +102 -28
  48. data/sentry-ruby.gemspec +1 -1
  49. metadata +6 -2
@@ -1,21 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "concurrent/utility/processor_counter"
2
4
 
3
5
  require "sentry/utils/exception_cause_chain"
6
+ require 'sentry/utils/custom_inspection'
4
7
  require "sentry/dsn"
8
+ require "sentry/release_detector"
5
9
  require "sentry/transport/configuration"
6
10
  require "sentry/linecache"
7
11
  require "sentry/interfaces/stacktrace_builder"
8
12
 
9
13
  module Sentry
10
14
  class Configuration
15
+ include CustomInspection
11
16
  include LoggingHelper
12
17
  # Directories to be recognized as part of your app. e.g. if you
13
18
  # have an `engines` dir at the root of your project, you may want
14
19
  # to set this to something like /(app|config|engines|lib)/
20
+ #
21
+ # @return [Regexp, nil]
15
22
  attr_accessor :app_dirs_pattern
16
23
 
17
24
  # Provide an object that responds to `call` to send events asynchronously.
18
25
  # E.g.: lambda { |event| Thread.new { Sentry.send_event(event) } }
26
+ #
27
+ # @deprecated It will be removed in the next major release. Please read https://github.com/getsentry/sentry-ruby/issues/1522 for more information
28
+ # @return [Proc, nil]
19
29
  attr_reader :async
20
30
 
21
31
  # to send events in a non-blocking way, sentry-ruby has its own background worker
@@ -25,71 +35,94 @@ module Sentry
25
35
  #
26
36
  # if you want to send events synchronously, set the value to 0
27
37
  # E.g.: config.background_worker_threads = 0
38
+ # @return [Integer]
28
39
  attr_accessor :background_worker_threads
29
40
 
30
41
  # a proc/lambda that takes an array of stack traces
31
42
  # it'll be used to silence (reduce) backtrace of the exception
32
43
  #
33
- # for example:
34
- #
35
- # ```ruby
36
- # Sentry.configuration.backtrace_cleanup_callback = lambda do |backtrace|
37
- # Rails.backtrace_cleaner.clean(backtrace)
38
- # end
39
- # ```
44
+ # @example
45
+ # config.backtrace_cleanup_callback = lambda do |backtrace|
46
+ # Rails.backtrace_cleaner.clean(backtrace)
47
+ # end
40
48
  #
49
+ # @return [Proc]
41
50
  attr_accessor :backtrace_cleanup_callback
42
51
 
43
52
  # Optional Proc, called before adding the breadcrumb to the current scope
44
- # E.g.: lambda { |breadcrumb, hint| breadcrumb }
45
- # E.g.: lambda { |breadcrumb, hint| nil }
46
- # E.g.: lambda { |breadcrumb, hint|
47
- # breadcrumb.message = 'a'
48
- # breadcrumb
49
- # }
53
+ # @example
54
+ # config.before = lambda do |breadcrumb, hint|
55
+ # breadcrumb.message = 'a'
56
+ # breadcrumb
57
+ # end
58
+ # @return [Proc]
50
59
  attr_reader :before_breadcrumb
51
60
 
52
- # Optional Proc, called before sending an event to the server/
53
- # E.g.: lambda { |event, hint| event }
54
- # E.g.: lambda { |event, hint| nil }
55
- # E.g.: lambda { |event, hint|
56
- # event[:message] = 'a'
57
- # event
58
- # }
61
+ # Optional Proc, called before sending an event to the server
62
+ # @example
63
+ # config.before_send = lambda do |event, hint|
64
+ # # skip ZeroDivisionError exceptions
65
+ # # note: hint[:exception] would be a String if you use async callback
66
+ # if hint[:exception].is_a?(ZeroDivisionError)
67
+ # nil
68
+ # else
69
+ # event
70
+ # end
71
+ # end
72
+ # @return [Proc]
59
73
  attr_reader :before_send
60
74
 
61
75
  # An array of breadcrumbs loggers to be used. Available options are:
62
76
  # - :sentry_logger
77
+ # - :http_logger
78
+ #
79
+ # And if you also use sentry-rails:
63
80
  # - :active_support_logger
81
+ # - :monotonic_active_support_logger
82
+ #
83
+ # @return [Array<Symbol>]
64
84
  attr_reader :breadcrumbs_logger
65
85
 
86
+ # Whether to capture local variables from the raised exception's frame. Default is false.
87
+ # @return [Boolean]
88
+ attr_accessor :capture_exception_frame_locals
89
+
66
90
  # Max number of breadcrumbs a breadcrumb buffer can hold
91
+ # @return [Integer]
67
92
  attr_accessor :max_breadcrumbs
68
93
 
69
94
  # Number of lines of code context to capture, or nil for none
95
+ # @return [Integer, nil]
70
96
  attr_accessor :context_lines
71
97
 
72
98
  # RACK_ENV by default.
99
+ # @return [String]
73
100
  attr_reader :environment
74
101
 
75
102
  # Whether the SDK should run in the debugging mode. Default is false.
76
103
  # If set to true, SDK errors will be logged with backtrace
104
+ # @return [Boolean]
77
105
  attr_accessor :debug
78
106
 
79
107
  # the dsn value, whether it's set via `config.dsn=` or `ENV["SENTRY_DSN"]`
108
+ # @return [String]
80
109
  attr_reader :dsn
81
110
 
82
111
  # Whitelist of enabled_environments that will send notifications to Sentry. Array of Strings.
112
+ # @return [Array<String>]
83
113
  attr_accessor :enabled_environments
84
114
 
85
115
  # Logger 'progname's to exclude from breadcrumbs
116
+ # @return [Array<String>]
86
117
  attr_accessor :exclude_loggers
87
118
 
88
119
  # Array of exception classes that should never be sent. See IGNORE_DEFAULT.
89
120
  # You should probably append to this rather than overwrite it.
121
+ # @return [Array<String>]
90
122
  attr_accessor :excluded_exceptions
91
123
 
92
124
  # Boolean to check nested exceptions when deciding if to exclude. Defaults to true
125
+ # @return [Boolean]
93
126
  attr_accessor :inspect_exception_causes_for_exclusion
94
127
  alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion
95
128
 
@@ -100,61 +133,80 @@ module Sentry
100
133
 
101
134
  # Logger used by Sentry. In Rails, this is the Rails logger, otherwise
102
135
  # Sentry provides its own Sentry::Logger.
136
+ # @return [Logger]
103
137
  attr_accessor :logger
104
138
 
105
139
  # Project directory root for in_app detection. Could be Rails root, etc.
106
140
  # Set automatically for Rails.
107
- attr_reader :project_root
141
+ # @return [String]
142
+ attr_accessor :project_root
108
143
 
109
144
  # Insert sentry-trace to outgoing requests' headers
145
+ # @return [Boolean]
110
146
  attr_accessor :propagate_traces
111
147
 
112
148
  # Array of rack env parameters to be included in the event sent to sentry.
149
+ # @return [Array<String>]
113
150
  attr_accessor :rack_env_whitelist
114
151
 
115
152
  # Release tag to be passed with every event sent to Sentry.
116
153
  # We automatically try to set this to a git SHA or Capistrano release.
154
+ # @return [String]
117
155
  attr_accessor :release
118
156
 
119
157
  # The sampling factor to apply to events. A value of 0.0 will not send
120
158
  # any events, and a value of 1.0 will send 100% of events.
159
+ # @return [Float]
121
160
  attr_accessor :sample_rate
122
161
 
123
162
  # Include module versions in reports - boolean.
163
+ # @return [Boolean]
124
164
  attr_accessor :send_modules
125
165
 
126
166
  # When send_default_pii's value is false (default), sensitive information like
127
167
  # - user ip
128
168
  # - user cookie
129
169
  # - request body
170
+ # - query string
130
171
  # will not be sent to Sentry.
172
+ # @return [Boolean]
131
173
  attr_accessor :send_default_pii
132
174
 
133
175
  # Allow to skip Sentry emails within rake tasks
176
+ # @return [Boolean]
134
177
  attr_accessor :skip_rake_integration
135
178
 
136
179
  # IP ranges for trusted proxies that will be skipped when calculating IP address.
137
180
  attr_accessor :trusted_proxies
138
181
 
182
+ # @return [String]
139
183
  attr_accessor :server_name
140
184
 
141
185
  # Return a Transport::Configuration object for transport-related configurations.
186
+ # @return [Transport]
142
187
  attr_reader :transport
143
188
 
144
189
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
190
+ # @return [Float]
145
191
  attr_accessor :traces_sample_rate
146
192
 
147
193
  # Take a Proc that controls the sample rate for every tracing event, e.g.
148
- # ```
149
- # lambda do |tracing_context|
150
- # # tracing_context[:transaction_context] contains the information about the transaction
151
- # # tracing_context[:parent_sampled] contains the transaction's parent's sample decision
152
- # true # return value can be a boolean or a float between 0.0 and 1.0
153
- # end
154
- # ```
194
+ # @example
195
+ # config.traces_sampler = lambda do |tracing_context|
196
+ # # tracing_context[:transaction_context] contains the information about the transaction
197
+ # # tracing_context[:parent_sampled] contains the transaction's parent's sample decision
198
+ # true # return value can be a boolean or a float between 0.0 and 1.0
199
+ # end
200
+ # @return [Proc]
155
201
  attr_accessor :traces_sampler
156
202
 
203
+ # Send diagnostic client reports about dropped events, true by default
204
+ # tries to attach to an existing envelope max once every 30s
205
+ # @return [Boolean]
206
+ attr_accessor :send_client_reports
207
+
157
208
  # these are not config options
209
+ # @!visibility private
158
210
  attr_reader :errors, :gem_specs
159
211
 
160
212
  # Most of these errors generate 4XX responses. In general, Sentry clients
@@ -177,17 +229,21 @@ module Sentry
177
229
 
178
230
  LOG_PREFIX = "** [Sentry] ".freeze
179
231
  MODULE_SEPARATOR = "::".freeze
232
+ SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
180
233
 
181
234
  # Post initialization callbacks are called at the end of initialization process
182
235
  # allowing extending the configuration of sentry-ruby by multiple extensions
183
236
  @@post_initialization_callbacks = []
184
237
 
185
238
  def initialize
239
+ self.app_dirs_pattern = nil
186
240
  self.debug = false
187
241
  self.background_worker_threads = Concurrent.processor_count
242
+ self.backtrace_cleanup_callback = nil
188
243
  self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
189
244
  self.breadcrumbs_logger = []
190
245
  self.context_lines = 3
246
+ self.capture_exception_frame_locals = false
191
247
  self.environment = environment_from_env
192
248
  self.enabled_environments = []
193
249
  self.exclude_loggers = []
@@ -202,12 +258,15 @@ module Sentry
202
258
  self.send_modules = true
203
259
  self.send_default_pii = false
204
260
  self.skip_rake_integration = false
261
+ self.send_client_reports = true
205
262
  self.trusted_proxies = []
206
263
  self.dsn = ENV['SENTRY_DSN']
207
264
  self.server_name = server_name_from_env
208
265
 
209
- self.before_send = false
266
+ self.before_send = nil
210
267
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
268
+ self.traces_sample_rate = nil
269
+ self.traces_sampler = nil
211
270
 
212
271
  @transport = Transport::Configuration.new
213
272
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
@@ -216,18 +275,13 @@ module Sentry
216
275
  end
217
276
 
218
277
  def dsn=(value)
219
- return if value.nil? || value.empty?
220
-
221
- @dsn = DSN.new(value)
278
+ @dsn = init_dsn(value)
222
279
  end
223
280
 
224
281
  alias server= dsn=
225
282
 
226
-
227
283
  def async=(value)
228
- if value && !value.respond_to?(:call)
229
- raise(ArgumentError, "async must be callable")
230
- end
284
+ check_callable!("async", value)
231
285
 
232
286
  @async = value
233
287
  end
@@ -246,17 +300,13 @@ module Sentry
246
300
  end
247
301
 
248
302
  def before_send=(value)
249
- unless value == false || value.respond_to?(:call)
250
- raise ArgumentError, "before_send must be callable (or false to disable)"
251
- end
303
+ check_callable!("before_send", value)
252
304
 
253
305
  @before_send = value
254
306
  end
255
307
 
256
308
  def before_breadcrumb=(value)
257
- unless value.nil? || value.respond_to?(:call)
258
- raise ArgumentError, "before_breadcrumb must be callable (or nil to disable)"
259
- end
309
+ check_callable!("before_breadcrumb", value)
260
310
 
261
311
  @before_breadcrumb = value
262
312
  end
@@ -268,18 +318,13 @@ module Sentry
268
318
  def sending_allowed?
269
319
  @errors = []
270
320
 
271
- valid? &&
272
- capture_in_environment? &&
273
- sample_allowed?
321
+ valid? && capture_in_environment?
274
322
  end
275
323
 
276
- def error_messages
277
- @errors = [@errors[0]] + @errors[1..-1].map(&:downcase) # fix case of all but first
278
- @errors.join(", ")
279
- end
324
+ def sample_allowed?
325
+ return true if sample_rate == 1.0
280
326
 
281
- def project_root=(root_dir)
282
- @project_root = root_dir
327
+ Random.rand < sample_rate
283
328
  end
284
329
 
285
330
  def exception_class_allowed?(exc)
@@ -303,6 +348,17 @@ module Sentry
303
348
  !!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
304
349
  end
305
350
 
351
+ # @return [String, nil]
352
+ def csp_report_uri
353
+ if dsn && dsn.valid?
354
+ uri = dsn.csp_report_uri
355
+ uri += "&sentry_release=#{CGI.escape(release)}" if release && !release.empty?
356
+ uri += "&sentry_environment=#{CGI.escape(environment)}" if environment && !environment.empty?
357
+ uri
358
+ end
359
+ end
360
+
361
+ # @api private
306
362
  def stacktrace_builder
307
363
  @stacktrace_builder ||= StacktraceBuilder.new(
308
364
  project_root: @project_root.to_s,
@@ -313,28 +369,39 @@ module Sentry
313
369
  )
314
370
  end
315
371
 
372
+ # @api private
316
373
  def detect_release
317
374
  return unless sending_allowed?
318
375
 
319
- self.release ||= detect_release_from_env ||
320
- detect_release_from_git ||
321
- detect_release_from_capistrano ||
322
- detect_release_from_heroku
376
+ self.release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?)
377
+
378
+ if running_on_heroku? && release.nil?
379
+ log_warn(HEROKU_DYNO_METADATA_MESSAGE)
380
+ end
323
381
  rescue => e
324
382
  log_error("Error detecting release", e, debug: debug)
325
383
  end
326
384
 
327
- def csp_report_uri
328
- if dsn && dsn.valid?
329
- uri = dsn.csp_report_uri
330
- uri += "&sentry_release=#{CGI.escape(release)}" if release && !release.empty?
331
- uri += "&sentry_environment=#{CGI.escape(environment)}" if environment && !environment.empty?
332
- uri
333
- end
385
+ # @api private
386
+ def error_messages
387
+ @errors = [@errors[0]] + @errors[1..-1].map(&:downcase) # fix case of all but first
388
+ @errors.join(", ")
334
389
  end
335
390
 
336
391
  private
337
392
 
393
+ def check_callable!(name, value)
394
+ unless value == nil || value.respond_to?(:call)
395
+ raise ArgumentError, "#{name} must be callable (or nil to disable)"
396
+ end
397
+ end
398
+
399
+ def init_dsn(dsn_string)
400
+ return if dsn_string.nil? || dsn_string.empty?
401
+
402
+ DSN.new(dsn_string)
403
+ end
404
+
338
405
  def excluded_exception?(incoming_exception)
339
406
  excluded_exception_classes.any? do |excluded_exception|
340
407
  matches_exception?(excluded_exception, incoming_exception)
@@ -364,37 +431,6 @@ module Sentry
364
431
  nil
365
432
  end
366
433
 
367
- def detect_release_from_heroku
368
- return unless running_on_heroku?
369
- return if ENV['CI']
370
- log_warn(HEROKU_DYNO_METADATA_MESSAGE) && return unless ENV['HEROKU_SLUG_COMMIT']
371
-
372
- ENV['HEROKU_SLUG_COMMIT']
373
- end
374
-
375
- def running_on_heroku?
376
- File.directory?("/etc/heroku")
377
- end
378
-
379
- def detect_release_from_capistrano
380
- revision_file = File.join(project_root, 'REVISION')
381
- revision_log = File.join(project_root, '..', 'revisions.log')
382
-
383
- if File.exist?(revision_file)
384
- File.read(revision_file).strip
385
- elsif File.exist?(revision_log)
386
- File.open(revision_log).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
387
- end
388
- end
389
-
390
- def detect_release_from_git
391
- Sentry.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
392
- end
393
-
394
- def detect_release_from_env
395
- ENV['SENTRY_RELEASE']
396
- end
397
-
398
434
  def capture_in_environment?
399
435
  return true if enabled_in_current_env?
400
436
 
@@ -411,24 +447,6 @@ module Sentry
411
447
  end
412
448
  end
413
449
 
414
- def sample_allowed?
415
- return true if sample_rate == 1.0
416
-
417
- if Random.rand >= sample_rate
418
- @errors << "Excluded by random sample"
419
- false
420
- else
421
- true
422
- end
423
- end
424
-
425
- # Try to resolve the hostname to an FQDN, but fall back to whatever
426
- # the load name is.
427
- def resolve_hostname
428
- Socket.gethostname ||
429
- Socket.gethostbyname(hostname).first rescue server_name
430
- end
431
-
432
450
  def environment_from_env
433
451
  ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
434
452
  end
@@ -437,10 +455,16 @@ module Sentry
437
455
  if running_on_heroku?
438
456
  ENV['DYNO']
439
457
  else
440
- resolve_hostname
458
+ # Try to resolve the hostname to an FQDN, but fall back to whatever
459
+ # the load name is.
460
+ Socket.gethostname || Socket.gethostbyname(hostname).first rescue server_name
441
461
  end
442
462
  end
443
463
 
464
+ def running_on_heroku?
465
+ File.directory?("/etc/heroku") && !ENV["CI"]
466
+ end
467
+
444
468
  def run_post_initialization_callbacks
445
469
  self.class.post_initialization_callbacks.each do |hook|
446
470
  instance_eval(&hook)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  return if Object.method_defined?(:deep_dup)
2
4
 
3
5
  require 'sentry/core_ext/object/duplicable'
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  return if Object.method_defined?(:duplicable?)
3
4
 
4
5
  #########################################
data/lib/sentry/dsn.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "uri"
2
4
 
3
5
  module Sentry
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Envelope
6
+ def initialize(headers)
7
+ @headers = headers
8
+ @items = []
9
+ end
10
+
11
+ def add_item(headers, payload)
12
+ @items << [headers, payload]
13
+ end
14
+
15
+ def to_s
16
+ payload = @items.map do |item_headers, item_payload|
17
+ <<~ENVELOPE
18
+ #{JSON.generate(item_headers)}
19
+ #{JSON.generate(item_payload)}
20
+ ENVELOPE
21
+ end.join("\n")
22
+
23
+ "#{JSON.generate(@headers)}\n#{payload}"
24
+ end
25
+ end
26
+ end
data/lib/sentry/event.rb CHANGED
@@ -6,6 +6,7 @@ require 'sentry/interface'
6
6
  require 'sentry/backtrace'
7
7
  require 'sentry/utils/real_ip'
8
8
  require 'sentry/utils/request_id'
9
+ require 'sentry/utils/custom_inspection'
9
10
 
10
11
  module Sentry
11
12
  class Event
@@ -21,6 +22,10 @@ module Sentry
21
22
 
22
23
  MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
23
24
 
25
+ SKIP_INSPECTION_ATTRIBUTES = [:@configuration, :@modules, :@backtrace]
26
+
27
+ include CustomInspection
28
+
24
29
  attr_writer(*WRITER_ATTRIBUTES)
25
30
  attr_reader(*SERIALIZEABLE_ATTRIBUTES)
26
31
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Error < StandardError
3
5
  end
data/lib/sentry/hub.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/scope"
2
4
  require "sentry/client"
3
5
 
@@ -133,6 +135,11 @@ module Sentry
133
135
 
134
136
  event = current_client.capture_event(event, scope, hint)
135
137
 
138
+
139
+ if event && configuration.debug
140
+ configuration.log_debug(event.to_json_compatible)
141
+ end
142
+
136
143
  @last_event_id = event&.event_id
137
144
  event
138
145
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Integrable
3
5
  def register_integration(name:, version:)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Interface
3
5
  def self.inherited(klass)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class ExceptionInterface < Interface
3
5
  def initialize(values:)
@@ -1,5 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/utils/exception_cause_chain"
4
+
1
5
  module Sentry
2
6
  class SingleExceptionInterface < Interface
7
+ include CustomInspection
8
+
9
+ SKIP_INSPECTION_ATTRIBUTES = [:@stacktrace]
10
+ PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]".freeze
11
+ OMISSION_MARK = "...".freeze
12
+ MAX_LOCAL_BYTES = 1024
13
+
3
14
  attr_reader :type, :value, :module, :thread_id, :stacktrace
4
15
 
5
16
  def initialize(exception:, stacktrace: nil)
@@ -20,6 +31,26 @@ module Sentry
20
31
  # also see `StacktraceBuilder.build`.
21
32
  def self.build_with_stacktrace(exception:, stacktrace_builder:)
22
33
  stacktrace = stacktrace_builder.build(backtrace: exception.backtrace)
34
+
35
+ if locals = exception.instance_variable_get(:@sentry_locals)
36
+ locals.each do |k, v|
37
+ locals[k] =
38
+ begin
39
+ v = v.inspect unless v.is_a?(String)
40
+
41
+ if v.length >= MAX_LOCAL_BYTES
42
+ v = v.byteslice(0..MAX_LOCAL_BYTES - 1) + OMISSION_MARK
43
+ end
44
+
45
+ v
46
+ rescue StandardError
47
+ PROBLEMATIC_LOCAL_VALUE_REPLACEMENT
48
+ end
49
+ end
50
+
51
+ stacktrace.frames.last.vars = locals
52
+ end
53
+
23
54
  new(exception: exception, stacktrace: stacktrace)
24
55
  end
25
56
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class StacktraceInterface
3
5
  attr_reader :frames
@@ -10,6 +12,10 @@ module Sentry
10
12
  { frames: @frames.map(&:to_hash) }
11
13
  end
12
14
 
15
+ def inspect
16
+ @frames.map(&:to_s)
17
+ end
18
+
13
19
  private
14
20
 
15
21
  # Not actually an interface, but I want to use the same style
@@ -28,6 +34,10 @@ module Sentry
28
34
  @filename = compute_filename
29
35
  end
30
36
 
37
+ def to_s
38
+ "#{@filename}:#{@lineno}"
39
+ end
40
+
31
41
  def compute_filename
32
42
  return if abs_path.nil?
33
43
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class StacktraceBuilder
3
5
  attr_reader :project_root, :app_dirs_pattern, :linecache, :context_lines, :backtrace_cleanup_callback
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class ThreadsInterface
3
5
  def initialize(crashed: false, stacktrace: nil)