sentry-ruby 5.5.0 → 5.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ad98e145a972608f9b15a669f771074f1fac592b8819fe1e1e2ac70c6b919a0
4
- data.tar.gz: 5058a18d24be77f5879c4455ab0a437010ad526cb5fa0ec869161c7fa5174d4f
3
+ metadata.gz: 2373a9b8990fd62763c4d1b26001f953f95efdd8b51f803e5d59bdb1ba984e3e
4
+ data.tar.gz: d09371eb4bf3aaff6e7735f74cd5887b2e99f636a60f1dca3cf653bbb8ba9bb4
5
5
  SHA512:
6
- metadata.gz: 69c776cddf21fb89754a1750632f2dc3a28d3dc4cd5e20a0c2a175985533b2f1d6774b4196c3437186776b2e60683356b141675606762338bfbb4e4d2fc9f764
7
- data.tar.gz: 3684c877d6370e5f72fb09151e5f01803661e177dfdce326b789e26ed59c06671bde2db641957f6bdc423f576cfe9e97657d5ac7e8fb81a61e8760ac31039ab8
6
+ metadata.gz: 4799b727ddaab0622be00e73b58a2e3f723779e1b46c7d5007d5711a8897022190ff9f87cc90df7411f32fd6b59d4f4637bcf26cc7eba8484cb04429871e9242
7
+ data.tar.gz: d97d9272ee815447ebdcbb72adb4e2255584475547e7878ed6c7af4f312d170bd37f3029acaaa6866eb04f2d89c1581c4854b849c2d57a1a51eb4b67e4ebeb0e
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --format documentation
2
2
  --color
3
- --require spec_helper
data/Gemfile CHANGED
@@ -7,17 +7,25 @@ rack_version = ENV["RACK_VERSION"]
7
7
  rack_version = "3.0.0" if rack_version.nil?
8
8
  gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
9
9
 
10
+ redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
11
+ gem "redis", "~> #{redis_rb_version}"
12
+
13
+ gem "puma"
14
+
10
15
  gem "rake", "~> 12.0"
11
16
  gem "rspec", "~> 3.0"
12
17
  gem "rspec-retry"
13
- gem "fakeredis"
14
18
  gem "timecop"
15
- gem 'simplecov'
19
+ gem "simplecov"
16
20
  gem "simplecov-cobertura", "~> 1.4"
17
21
  gem "rexml"
22
+ gem "stackprof" unless RUBY_PLATFORM == "java"
23
+
24
+ if RUBY_VERSION.to_f >= 2.6
25
+ gem "debug", github: "ruby/debug", platform: :ruby
26
+ gem "irb"
27
+ end
18
28
 
19
- gem "object_tracer"
20
- gem "debug", github: "ruby/debug", platform: :ruby if RUBY_VERSION.to_f >= 2.6
21
29
  gem "pry"
22
30
 
23
31
  gem "benchmark-ips"
@@ -25,4 +33,5 @@ gem "benchmark_driver"
25
33
  gem "benchmark-ipsa"
26
34
  gem "benchmark-memory"
27
35
 
28
- gem "yard", "~> 0.9.27"
36
+ gem "yard", github: "lsegal/yard"
37
+ gem "webrick"
data/README.md CHANGED
@@ -20,6 +20,7 @@ Sentry SDK for Ruby
20
20
  | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-sidekiq%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-sidekiq.svg)](https://rubygems.org/gems/sentry-sidekiq/) |
21
21
  | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-delayed_job%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-delayed_job.svg)](https://rubygems.org/gems/sentry-delayed_job/) |
22
22
  | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-resque%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-resque.svg)](https://rubygems.org/gems/sentry-resque/) |
23
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-opentelemetry%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-opentelemetry.svg)](https://rubygems.org/gems/sentry-opentelemetry/) |
23
24
 
24
25
 
25
26
 
@@ -51,6 +52,7 @@ gem "sentry-rails"
51
52
  gem "sentry-sidekiq"
52
53
  gem "sentry-delayed_job"
53
54
  gem "sentry-resque"
55
+ gem "sentry-opentelemetry"
54
56
  ```
55
57
 
56
58
  ### Configuration
@@ -88,6 +90,7 @@ To learn more about sampling transactions, please visit the [official documentat
88
90
  - [Sidekiq](https://docs.sentry.io/platforms/ruby/guides/sidekiq/)
89
91
  - [DelayedJob](https://docs.sentry.io/platforms/ruby/guides/delayed_job/)
90
92
  - [Resque](https://docs.sentry.io/platforms/ruby/guides/resque/)
93
+ - [OpenTemeletry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
91
94
 
92
95
  ### Enriching Events
93
96
 
data/Rakefile CHANGED
@@ -8,6 +8,13 @@ require "rspec/core/rake_task"
8
8
 
9
9
  RSpec::Core::RakeTask.new(:spec).tap do |task|
10
10
  task.rspec_opts = "--order rand"
11
+ task.exclude_pattern = "spec/isolated/**/*_spec.rb"
11
12
  end
12
13
 
13
- task :default => :spec
14
+ task :isolated_specs do
15
+ Dir["spec/isolated/*"].each do |file|
16
+ sh "bundle exec rspec #{file}"
17
+ end
18
+ end
19
+
20
+ task :default => [:spec, :isolated_specs]
@@ -76,7 +76,7 @@ module Sentry
76
76
  end
77
77
  end
78
78
 
79
- APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test)/.freeze
79
+ APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/.freeze
80
80
 
81
81
  # holder for an Array of Backtrace::Line instances
82
82
  attr_reader :lines
@@ -8,17 +8,6 @@ module Sentry
8
8
  SENTRY_PREFIX = 'sentry-'
9
9
  SENTRY_PREFIX_REGEX = /^sentry-/.freeze
10
10
 
11
- DSC_KEYS = %w(
12
- trace_id
13
- public_key
14
- sample_rate
15
- release
16
- environment
17
- transaction
18
- user_id
19
- user_segment
20
- ).freeze
21
-
22
11
  # @return [Hash]
23
12
  attr_reader :items
24
13
 
@@ -68,7 +57,7 @@ module Sentry
68
57
  # hash to be used in the trace envelope header.
69
58
  # @return [Hash]
70
59
  def dynamic_sampling_context
71
- @items.select { |k, _v| DSC_KEYS.include?(k) }
60
+ @items
72
61
  end
73
62
 
74
63
  # Serialize the Baggage object back to a string.
data/lib/sentry/client.rb CHANGED
@@ -76,7 +76,10 @@ module Sentry
76
76
  # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
77
77
  # @return [Event, nil]
78
78
  def event_from_exception(exception, hint = {})
79
- return unless @configuration.sending_allowed? && @configuration.exception_class_allowed?(exception)
79
+ return unless @configuration.sending_allowed?
80
+
81
+ ignore_exclusions = hint.delete(:ignore_exclusions) { false }
82
+ return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
80
83
 
81
84
  integration_meta = Sentry.integrations[hint[:integration]]
82
85
 
@@ -122,6 +125,16 @@ module Sentry
122
125
  end
123
126
  end
124
127
 
128
+ if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
129
+ event = configuration.before_send_transaction.call(event, hint)
130
+
131
+ if event.nil?
132
+ log_info("Discarded event because before_send_transaction returned nil")
133
+ transport.record_lost_event(:before_send, 'transaction')
134
+ return
135
+ end
136
+ end
137
+
125
138
  transport.send_event(event)
126
139
 
127
140
  event
@@ -14,6 +14,8 @@ module Sentry
14
14
  class Configuration
15
15
  include CustomInspection
16
16
  include LoggingHelper
17
+ include ArgumentCheckingHelper
18
+
17
19
  # Directories to be recognized as part of your app. e.g. if you
18
20
  # have an `engines` dir at the root of your project, you may want
19
21
  # to set this to something like /(app|config|engines|lib)/
@@ -72,6 +74,19 @@ module Sentry
72
74
  # @return [Proc]
73
75
  attr_reader :before_send
74
76
 
77
+ # Optional Proc, called before sending an event to the server
78
+ # @example
79
+ # config.before_send_transaction = lambda do |event, hint|
80
+ # # skip unimportant transactions or strip sensitive data
81
+ # if event.transaction == "/healthcheck/route"
82
+ # nil
83
+ # else
84
+ # event
85
+ # end
86
+ # end
87
+ # @return [Proc]
88
+ attr_reader :before_send_transaction
89
+
75
90
  # An array of breadcrumbs loggers to be used. Available options are:
76
91
  # - :sentry_logger
77
92
  # - :http_logger
@@ -84,10 +99,6 @@ module Sentry
84
99
  # @return [Array<Symbol>]
85
100
  attr_reader :breadcrumbs_logger
86
101
 
87
- # Whether to capture local variables from the raised exception's frame. Default is false.
88
- # @return [Boolean]
89
- attr_accessor :capture_exception_frame_locals
90
-
91
102
  # Max number of breadcrumbs a breadcrumb buffer can hold
92
103
  # @return [Integer]
93
104
  attr_accessor :max_breadcrumbs
@@ -127,6 +138,22 @@ module Sentry
127
138
  attr_accessor :inspect_exception_causes_for_exclusion
128
139
  alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion
129
140
 
141
+ # Whether to capture local variables from the raised exception's frame. Default is false.
142
+ # @return [Boolean]
143
+ attr_accessor :include_local_variables
144
+
145
+ # @deprecated Use {#include_local_variables} instead.
146
+ alias_method :capture_exception_frame_locals, :include_local_variables
147
+
148
+ # @deprecated Use {#include_local_variables=} instead.
149
+ def capture_exception_frame_locals=(value)
150
+ log_warn <<~MSG
151
+ `capture_exception_frame_locals` is now deprecated in favor of `include_local_variables`.
152
+ MSG
153
+
154
+ self.include_local_variables = value
155
+ end
156
+
130
157
  # You may provide your own LineCache for matching paths with source files.
131
158
  # This may be useful if you need to get source code from places other than the disk.
132
159
  # @see LineCache
@@ -154,7 +181,7 @@ module Sentry
154
181
  # Release tag to be passed with every event sent to Sentry.
155
182
  # We automatically try to set this to a git SHA or Capistrano release.
156
183
  # @return [String]
157
- attr_accessor :release
184
+ attr_reader :release
158
185
 
159
186
  # The sampling factor to apply to events. A value of 0.0 will not send
160
187
  # any events, and a value of 1.0 will send 100% of events.
@@ -189,8 +216,8 @@ module Sentry
189
216
  attr_reader :transport
190
217
 
191
218
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
192
- # @return [Float]
193
- attr_accessor :traces_sample_rate
219
+ # @return [Float, nil]
220
+ attr_reader :traces_sample_rate
194
221
 
195
222
  # Take a Proc that controls the sample rate for every tracing event, e.g.
196
223
  # @example
@@ -202,6 +229,11 @@ module Sentry
202
229
  # @return [Proc]
203
230
  attr_accessor :traces_sampler
204
231
 
232
+ # Easier way to use performance tracing
233
+ # If set to true, will set traces_sample_rate to 1.0
234
+ # @return [Boolean, nil]
235
+ attr_reader :enable_tracing
236
+
205
237
  # Send diagnostic client reports about dropped events, true by default
206
238
  # tries to attach to an existing envelope max once every 30s
207
239
  # @return [Boolean]
@@ -211,10 +243,29 @@ module Sentry
211
243
  # @return [Boolean]
212
244
  attr_accessor :auto_session_tracking
213
245
 
246
+ # The instrumenter to use, :sentry or :otel
247
+ # @return [Symbol]
248
+ attr_reader :instrumenter
249
+
250
+ # Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
251
+ # Note that this rate is relative to traces_sample_rate / traces_sampler,
252
+ # i.e. the profile is sampled by this rate after the transaction is sampled.
253
+ # @return [Float, nil]
254
+ attr_reader :profiles_sample_rate
255
+
214
256
  # these are not config options
215
257
  # @!visibility private
216
258
  attr_reader :errors, :gem_specs
217
259
 
260
+ # These exceptions could enter Puma's `lowlevel_error_handler` callback and the SDK's Puma integration
261
+ # But they are mostly considered as noise and should be ignored by default
262
+ # Please see https://github.com/getsentry/sentry-ruby/pull/2026 for more information
263
+ PUMA_IGNORE_DEFAULT = [
264
+ 'Puma::MiniSSL::SSLError',
265
+ 'Puma::HttpParserError',
266
+ 'Puma::HttpParserError501'
267
+ ].freeze
268
+
218
269
  # Most of these errors generate 4XX responses. In general, Sentry clients
219
270
  # only automatically report 5xx responses.
220
271
  IGNORE_DEFAULT = [
@@ -237,9 +288,20 @@ module Sentry
237
288
  MODULE_SEPARATOR = "::".freeze
238
289
  SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
239
290
 
240
- # Post initialization callbacks are called at the end of initialization process
241
- # allowing extending the configuration of sentry-ruby by multiple extensions
242
- @@post_initialization_callbacks = []
291
+ INSTRUMENTERS = [:sentry, :otel]
292
+
293
+ class << self
294
+ # Post initialization callbacks are called at the end of initialization process
295
+ # allowing extending the configuration of sentry-ruby by multiple extensions
296
+ def post_initialization_callbacks
297
+ @post_initialization_callbacks ||= []
298
+ end
299
+
300
+ # allow extensions to add their hooks to the Configuration class
301
+ def add_post_initialization_callback(&block)
302
+ post_initialization_callbacks << block
303
+ end
304
+ end
243
305
 
244
306
  def initialize
245
307
  self.app_dirs_pattern = nil
@@ -249,11 +311,11 @@ module Sentry
249
311
  self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
250
312
  self.breadcrumbs_logger = []
251
313
  self.context_lines = 3
252
- self.capture_exception_frame_locals = false
314
+ self.include_local_variables = false
253
315
  self.environment = environment_from_env
254
316
  self.enabled_environments = []
255
317
  self.exclude_loggers = []
256
- self.excluded_exceptions = IGNORE_DEFAULT.dup
318
+ self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
257
319
  self.inspect_exception_causes_for_exclusion = true
258
320
  self.linecache = ::Sentry::LineCache.new
259
321
  self.logger = ::Sentry::Logger.new(STDOUT)
@@ -269,11 +331,13 @@ module Sentry
269
331
  self.trusted_proxies = []
270
332
  self.dsn = ENV['SENTRY_DSN']
271
333
  self.server_name = server_name_from_env
334
+ self.instrumenter = :sentry
272
335
 
273
336
  self.before_send = nil
337
+ self.before_send_transaction = nil
274
338
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
275
- self.traces_sample_rate = nil
276
339
  self.traces_sampler = nil
340
+ self.enable_tracing = nil
277
341
 
278
342
  @transport = Transport::Configuration.new
279
343
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
@@ -287,6 +351,12 @@ module Sentry
287
351
 
288
352
  alias server= dsn=
289
353
 
354
+ def release=(value)
355
+ check_argument_type!(value, String, NilClass)
356
+
357
+ @release = value
358
+ end
359
+
290
360
  def async=(value)
291
361
  check_callable!("async", value)
292
362
 
@@ -322,6 +392,12 @@ module Sentry
322
392
  @before_send = value
323
393
  end
324
394
 
395
+ def before_send_transaction=(value)
396
+ check_callable!("before_send_transaction", value)
397
+
398
+ @before_send_transaction = value
399
+ end
400
+
325
401
  def before_breadcrumb=(value)
326
402
  check_callable!("before_breadcrumb", value)
327
403
 
@@ -332,6 +408,30 @@ module Sentry
332
408
  @environment = environment.to_s
333
409
  end
334
410
 
411
+ def instrumenter=(instrumenter)
412
+ @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
413
+ end
414
+
415
+ def enable_tracing=(enable_tracing)
416
+ @enable_tracing = enable_tracing
417
+ @traces_sample_rate ||= 1.0 if enable_tracing
418
+ end
419
+
420
+ def is_numeric_or_nil?(value)
421
+ value.is_a?(Numeric) || value.nil?
422
+ end
423
+
424
+ def traces_sample_rate=(traces_sample_rate)
425
+ raise ArgumentError, "traces_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(traces_sample_rate)
426
+ @traces_sample_rate = traces_sample_rate
427
+ end
428
+
429
+ def profiles_sample_rate=(profiles_sample_rate)
430
+ raise ArgumentError, "profiles_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(profiles_sample_rate)
431
+ log_info("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
432
+ @profiles_sample_rate = profiles_sample_rate
433
+ end
434
+
335
435
  def sending_allowed?
336
436
  @errors = []
337
437
 
@@ -361,8 +461,21 @@ module Sentry
361
461
  enabled_environments.empty? || enabled_environments.include?(environment)
362
462
  end
363
463
 
464
+ def valid_sample_rate?(sample_rate)
465
+ return false unless sample_rate.is_a?(Numeric)
466
+ sample_rate >= 0.0 && sample_rate <= 1.0
467
+ end
468
+
364
469
  def tracing_enabled?
365
- !!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
470
+ valid_sampler = !!((valid_sample_rate?(@traces_sample_rate)) || @traces_sampler)
471
+
472
+ (@enable_tracing != false) && valid_sampler && sending_allowed?
473
+ end
474
+
475
+ def profiling_enabled?
476
+ valid_sampler = !!(valid_sample_rate?(@profiles_sample_rate))
477
+
478
+ tracing_enabled? && valid_sampler && sending_allowed?
366
479
  end
367
480
 
368
481
  # @return [String, nil]
@@ -390,7 +503,7 @@ module Sentry
390
503
  def detect_release
391
504
  return unless sending_allowed?
392
505
 
393
- self.release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?)
506
+ @release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?)
394
507
 
395
508
  if running_on_heroku? && release.nil?
396
509
  log_warn(HEROKU_DYNO_METADATA_MESSAGE)
@@ -487,16 +600,5 @@ module Sentry
487
600
  instance_eval(&hook)
488
601
  end
489
602
  end
490
-
491
- # allow extensions to add their hooks to the Configuration class
492
- def self.add_post_initialization_callback(&block)
493
- self.post_initialization_callbacks << block
494
- end
495
-
496
- protected
497
-
498
- def self.post_initialization_callbacks
499
- @@post_initialization_callbacks
500
- end
501
603
  end
502
604
  end
@@ -19,10 +19,7 @@ module Sentry
19
19
  end
20
20
 
21
21
  def to_s
22
- <<~ITEM
23
- #{JSON.generate(@headers)}
24
- #{JSON.generate(@payload)}
25
- ITEM
22
+ [JSON.generate(@headers), JSON.generate(@payload)].join("\n")
26
23
  end
27
24
 
28
25
  def serialize
data/lib/sentry/hub.rb CHANGED
@@ -76,8 +76,9 @@ module Sentry
76
76
  @stack.pop
77
77
  end
78
78
 
79
- def start_transaction(transaction: nil, custom_sampling_context: {}, **options)
79
+ def start_transaction(transaction: nil, custom_sampling_context: {}, instrumenter: :sentry, **options)
80
80
  return unless configuration.tracing_enabled?
81
+ return unless instrumenter == configuration.instrumenter
81
82
 
82
83
  transaction ||= Transaction.new(**options.merge(hub: self))
83
84
 
@@ -87,13 +88,39 @@ module Sentry
87
88
  }
88
89
 
89
90
  sampling_context.merge!(custom_sampling_context)
90
-
91
91
  transaction.set_initial_sample_decision(sampling_context: sampling_context)
92
+
93
+ transaction.start_profiler!
94
+
92
95
  transaction
93
96
  end
94
97
 
98
+ def with_child_span(instrumenter: :sentry, **attributes, &block)
99
+ return yield(nil) unless instrumenter == configuration.instrumenter
100
+
101
+ current_span = current_scope.get_span
102
+ return yield(nil) unless current_span
103
+
104
+ result = nil
105
+
106
+ begin
107
+ current_span.with_child_span(**attributes) do |child_span|
108
+ current_scope.set_span(child_span)
109
+ result = yield(child_span)
110
+ end
111
+ ensure
112
+ current_scope.set_span(current_span)
113
+ end
114
+
115
+ result
116
+ end
117
+
95
118
  def capture_exception(exception, **options, &block)
96
- check_argument_type!(exception, ::Exception)
119
+ if RUBY_PLATFORM == "java"
120
+ check_argument_type!(exception, ::Exception, ::Java::JavaLang::Throwable)
121
+ else
122
+ check_argument_type!(exception, ::Exception)
123
+ end
97
124
 
98
125
  return if Sentry.exception_captured?(exception)
99
126
 
@@ -101,6 +128,7 @@ module Sentry
101
128
 
102
129
  options[:hint] ||= {}
103
130
  options[:hint][:exception] = exception
131
+
104
132
  event = current_client.event_from_exception(exception, options[:hint])
105
133
 
106
134
  return unless event
@@ -73,7 +73,7 @@ module Sentry
73
73
  request.POST
74
74
  elsif request.body # JSON requests, etc
75
75
  data = request.body.read(MAX_BODY_LIMIT)
76
- data = encode_to_utf_8(data.to_s)
76
+ data = Utils::EncodingHelper.encode_to_utf_8(data.to_s)
77
77
  request.body.rewind
78
78
  data
79
79
  end
@@ -94,7 +94,7 @@ module Sentry
94
94
  key = key.sub(/^HTTP_/, "")
95
95
  key = key.split('_').map(&:capitalize).join('-')
96
96
 
97
- memo[key] = encode_to_utf_8(value.to_s)
97
+ memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
98
98
  rescue StandardError => e
99
99
  # Rails adds objects to the Rack env that can sometimes raise exceptions
100
100
  # when `to_s` is called.
@@ -105,18 +105,6 @@ module Sentry
105
105
  end
106
106
  end
107
107
 
108
- def encode_to_utf_8(value)
109
- if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
110
- value = value.dup.force_encoding(Encoding::UTF_8)
111
- end
112
-
113
- if !value.valid_encoding?
114
- value = value.scrub
115
- end
116
-
117
- value
118
- end
119
-
120
108
  def is_skippable_header?(key)
121
109
  key.upcase != key || # lower-case envs aren't real http headers
122
110
  key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
@@ -15,7 +15,15 @@ module Sentry
15
15
 
16
16
  def initialize(exception:, stacktrace: nil)
17
17
  @type = exception.class.to_s
18
- @value = (exception.message || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
18
+ exception_message =
19
+ if exception.respond_to?(:detailed_message)
20
+ exception.detailed_message(highlight: false)
21
+ else
22
+ exception.message || ""
23
+ end
24
+
25
+ @value = exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
26
+
19
27
  @module = exception.class.to_s.split('::')[0...-1].join('::')
20
28
  @thread_id = Thread.current.object_id
21
29
  @stacktrace = stacktrace
@@ -26,14 +26,24 @@ module Sentry
26
26
  #
27
27
  # So we're only instrumenting request when `Net::HTTP` is already started
28
28
  def request(req, body = nil, &block)
29
- return super unless started?
30
-
31
- sentry_span = start_sentry_span
32
- set_sentry_trace_header(req, sentry_span)
33
-
34
- super.tap do |res|
35
- record_sentry_breadcrumb(req, res)
36
- record_sentry_span(req, res, sentry_span)
29
+ return super unless started? && Sentry.initialized?
30
+ return super if from_sentry_sdk?
31
+
32
+ Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span|
33
+ set_sentry_trace_header(req, sentry_span)
34
+
35
+ super.tap do |res|
36
+ record_sentry_breadcrumb(req, res)
37
+
38
+ if sentry_span
39
+ request_info = extract_request_info(req)
40
+ sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
41
+ sentry_span.set_data('url', request_info[:url])
42
+ sentry_span.set_data('http.method', request_info[:method])
43
+ sentry_span.set_data('http.query', request_info[:query]) if request_info[:query]
44
+ sentry_span.set_data('status', res.code.to_i)
45
+ end
46
+ end
37
47
  end
38
48
  end
39
49
 
@@ -53,7 +63,6 @@ module Sentry
53
63
 
54
64
  def record_sentry_breadcrumb(req, res)
55
65
  return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
56
- return if from_sentry_sdk?
57
66
 
58
67
  request_info = extract_request_info(req)
59
68
 
@@ -69,29 +78,6 @@ module Sentry
69
78
  Sentry.add_breadcrumb(crumb)
70
79
  end
71
80
 
72
- def record_sentry_span(req, res, sentry_span)
73
- return unless Sentry.initialized? && sentry_span
74
-
75
- request_info = extract_request_info(req)
76
- sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
77
- sentry_span.set_data(:status, res.code.to_i)
78
- finish_sentry_span(sentry_span)
79
- end
80
-
81
- def start_sentry_span
82
- return unless Sentry.initialized? && span = Sentry.get_current_scope.get_span
83
- return if from_sentry_sdk?
84
- return if span.sampled == false
85
-
86
- span.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
87
- end
88
-
89
- def finish_sentry_span(sentry_span)
90
- return unless Sentry.initialized? && sentry_span
91
-
92
- sentry_span.set_timestamp(Sentry.utc_now.to_f)
93
- end
94
-
95
81
  def from_sentry_sdk?
96
82
  dsn = Sentry.configuration.dsn
97
83
  dsn && dsn.host == self.address
@@ -104,7 +90,7 @@ module Sentry
104
90
  result = { method: req.method, url: url }
105
91
 
106
92
  if Sentry.configuration.send_default_pii
107
- result[:url] = result[:url] + "?#{uri.query}"
93
+ result[:query] = uri.query
108
94
  result[:body] = req.body
109
95
  end
110
96
 
@@ -114,7 +100,4 @@ module Sentry
114
100
  end
115
101
  end
116
102
 
117
- Sentry.register_patch do
118
- patch = Sentry::Net::HTTP
119
- Net::HTTP.send(:prepend, patch) unless Net::HTTP.ancestors.include?(patch)
120
- end
103
+ Sentry.register_patch(Sentry::Net::HTTP, Net::HTTP)