sentry-ruby 5.4.2 → 5.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 310e7ac901b3da90fb400710f95be183627b1e35587f059810669469deb27763
4
- data.tar.gz: 75cff84d0f7238ac444b5f6ce0a67f0150daefa38d9bac9e2b083a6f98ecff6e
3
+ metadata.gz: 7a114a391fe058601b40369376152bee8ba9b1b96ab94710344283cfe1a4490b
4
+ data.tar.gz: 7a17648c7a5d06f22d2d643f6ff1cb25d3fed2348609e372fc5762043ebd538a
5
5
  SHA512:
6
- metadata.gz: 76251bc9b7499bc40b30c1e20c3b638a3cc5bf0a3c5df9f5210fa2b2293a3ee7e6c8c7a1d3073b1055c8788855531c36ece0517ea38e203367c0c8bf6842e130
7
- data.tar.gz: cc3b7a551b8d8c798c5843b895710244857bcf110e57cc4dd1de345e7fe6e09201bf3adf3e4645d9101df201cab8ce4cda0b547b88d137ef1faab8e7a6f1693f
6
+ metadata.gz: dab19469a8e68201380e2be405332bb7c8caef7bf46fd4dae70f4dc3beaac2902ce51b2b2c902b39ca048e1bc5ff47f85b1e7d3a824b5f20ff002dcbdca2c354
7
+ data.tar.gz: 6bf0cba7b42dd9bfd5078e4353ad7f92d932b959a87c748cb30e505ffd8d205899c819ea4f72e5f3099f0b861139864062b870219678abdeda0a0629604963d3
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --format documentation
2
2
  --color
3
- --require spec_helper
data/Gemfile CHANGED
@@ -3,17 +3,23 @@ git_source(:github) { |name| "https://github.com/#{name}.git" }
3
3
 
4
4
  gem "sentry-ruby", path: "./"
5
5
 
6
- gem "rack" unless ENV["WITHOUT_RACK"] == "1"
6
+ rack_version = ENV["RACK_VERSION"]
7
+ rack_version = "3.0.0" if rack_version.nil?
8
+ gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
9
+
10
+ redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
11
+ gem "redis", "~> #{redis_rb_version}"
12
+
13
+ gem "puma"
7
14
 
8
15
  gem "rake", "~> 12.0"
9
16
  gem "rspec", "~> 3.0"
10
17
  gem "rspec-retry"
11
- gem "webmock"
12
- gem "fakeredis"
13
18
  gem "timecop"
14
- gem 'simplecov'
19
+ gem "simplecov"
15
20
  gem "simplecov-cobertura", "~> 1.4"
16
21
  gem "rexml"
22
+ gem "stackprof" unless RUBY_PLATFORM == "java"
17
23
 
18
24
  gem "object_tracer"
19
25
  gem "debug", github: "ruby/debug", platform: :ruby if RUBY_VERSION.to_f >= 2.6
@@ -24,4 +30,5 @@ gem "benchmark_driver"
24
30
  gem "benchmark-ipsa"
25
31
  gem "benchmark-memory"
26
32
 
27
- gem "yard", "~> 0.9.27"
33
+ gem "yard", github: "lsegal/yard"
34
+ 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
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
5
+ module Sentry
6
+ # A {https://www.w3.org/TR/baggage W3C Baggage Header} implementation.
7
+ class Baggage
8
+ SENTRY_PREFIX = 'sentry-'
9
+ SENTRY_PREFIX_REGEX = /^sentry-/.freeze
10
+
11
+ # @return [Hash]
12
+ attr_reader :items
13
+
14
+ # @return [Boolean]
15
+ attr_reader :mutable
16
+
17
+ def initialize(items, mutable: true)
18
+ @items = items
19
+ @mutable = mutable
20
+ end
21
+
22
+ # Creates a Baggage object from an incoming W3C Baggage header string.
23
+ #
24
+ # Sentry items are identified with the 'sentry-' prefix and stored in a hash.
25
+ # The presence of a Sentry item makes the baggage object immutable.
26
+ #
27
+ # @param header [String] The incoming Baggage header string.
28
+ # @return [Baggage, nil]
29
+ def self.from_incoming_header(header)
30
+ items = {}
31
+ mutable = true
32
+
33
+ header.split(',').each do |item|
34
+ item = item.strip
35
+ key, val = item.split('=')
36
+
37
+ next unless key && val
38
+ next unless key =~ SENTRY_PREFIX_REGEX
39
+
40
+ baggage_key = key.split('-')[1]
41
+ next unless baggage_key
42
+
43
+ items[CGI.unescape(baggage_key)] = CGI.unescape(val)
44
+ mutable = false
45
+ end
46
+
47
+ new(items, mutable: mutable)
48
+ end
49
+
50
+ # Make the Baggage immutable.
51
+ # @return [void]
52
+ def freeze!
53
+ @mutable = false
54
+ end
55
+
56
+ # A {https://develop.sentry.dev/sdk/performance/dynamic-sampling-context/#envelope-header Dynamic Sampling Context}
57
+ # hash to be used in the trace envelope header.
58
+ # @return [Hash]
59
+ def dynamic_sampling_context
60
+ @items
61
+ end
62
+
63
+ # Serialize the Baggage object back to a string.
64
+ # @return [String]
65
+ def serialize
66
+ items = @items.map { |k, v| "#{SENTRY_PREFIX}#{CGI.escape(k)}=#{CGI.escape(v)}" }
67
+ items.join(',')
68
+ end
69
+ end
70
+ end
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
 
@@ -105,16 +108,7 @@ module Sentry
105
108
  # @param transaction [Transaction] the transaction to be recorded.
106
109
  # @return [TransactionEvent]
107
110
  def event_from_transaction(transaction)
108
- TransactionEvent.new(configuration: configuration).tap do |event|
109
- event.transaction = transaction.name
110
- event.contexts.merge!(trace: transaction.get_trace_context)
111
- event.timestamp = transaction.timestamp
112
- event.start_timestamp = transaction.start_timestamp
113
- event.tags = transaction.tags
114
-
115
- finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
116
- event.spans = finished_spans.map(&:to_hash)
117
- end
111
+ TransactionEvent.new(configuration: configuration, transaction: transaction)
118
112
  end
119
113
 
120
114
  # @!macro send_event
@@ -131,6 +125,16 @@ module Sentry
131
125
  end
132
126
  end
133
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
+
134
138
  transport.send_event(event)
135
139
 
136
140
  event
@@ -156,6 +160,22 @@ module Sentry
156
160
  trace
157
161
  end
158
162
 
163
+ # Generates a W3C Baggage header for distribted tracing from the given Span.
164
+ # Returns `nil` if `config.propagate_traces` is `false`.
165
+ # @param span [Span] the span to generate trace from.
166
+ # @return [String, nil]
167
+ def generate_baggage(span)
168
+ return unless configuration.propagate_traces
169
+
170
+ baggage = span.to_baggage
171
+
172
+ if baggage && !baggage.empty?
173
+ log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
174
+ end
175
+
176
+ baggage
177
+ end
178
+
159
179
  private
160
180
 
161
181
  def dispatch_background_event(event, hint)
@@ -72,6 +72,19 @@ module Sentry
72
72
  # @return [Proc]
73
73
  attr_reader :before_send
74
74
 
75
+ # Optional Proc, called before sending an event to the server
76
+ # @example
77
+ # config.before_send_transaction = lambda do |event, hint|
78
+ # # skip unimportant transactions or strip sensitive data
79
+ # if event.transaction == "/healthcheck/route"
80
+ # nil
81
+ # else
82
+ # event
83
+ # end
84
+ # end
85
+ # @return [Proc]
86
+ attr_reader :before_send_transaction
87
+
75
88
  # An array of breadcrumbs loggers to be used. Available options are:
76
89
  # - :sentry_logger
77
90
  # - :http_logger
@@ -84,10 +97,6 @@ module Sentry
84
97
  # @return [Array<Symbol>]
85
98
  attr_reader :breadcrumbs_logger
86
99
 
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
100
  # Max number of breadcrumbs a breadcrumb buffer can hold
92
101
  # @return [Integer]
93
102
  attr_accessor :max_breadcrumbs
@@ -127,6 +136,22 @@ module Sentry
127
136
  attr_accessor :inspect_exception_causes_for_exclusion
128
137
  alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion
129
138
 
139
+ # Whether to capture local variables from the raised exception's frame. Default is false.
140
+ # @return [Boolean]
141
+ attr_accessor :include_local_variables
142
+
143
+ # @deprecated Use {#include_local_variables} instead.
144
+ alias_method :capture_exception_frame_locals, :include_local_variables
145
+
146
+ # @deprecated Use {#include_local_variables=} instead.
147
+ def capture_exception_frame_locals=(value)
148
+ log_warn <<~MSG
149
+ `capture_exception_frame_locals` is now deprecated in favor of `include_local_variables`.
150
+ MSG
151
+
152
+ self.include_local_variables = value
153
+ end
154
+
130
155
  # You may provide your own LineCache for matching paths with source files.
131
156
  # This may be useful if you need to get source code from places other than the disk.
132
157
  # @see LineCache
@@ -202,6 +227,11 @@ module Sentry
202
227
  # @return [Proc]
203
228
  attr_accessor :traces_sampler
204
229
 
230
+ # Easier way to use performance tracing
231
+ # If set to true, will set traces_sample_rate to 1.0
232
+ # @return [Boolean, nil]
233
+ attr_reader :enable_tracing
234
+
205
235
  # Send diagnostic client reports about dropped events, true by default
206
236
  # tries to attach to an existing envelope max once every 30s
207
237
  # @return [Boolean]
@@ -211,6 +241,16 @@ module Sentry
211
241
  # @return [Boolean]
212
242
  attr_accessor :auto_session_tracking
213
243
 
244
+ # The instrumenter to use, :sentry or :otel
245
+ # @return [Symbol]
246
+ attr_reader :instrumenter
247
+
248
+ # Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
249
+ # Note that this rate is relative to traces_sample_rate / traces_sampler,
250
+ # i.e. the profile is sampled by this rate after the transaction is sampled.
251
+ # @return [Float, nil]
252
+ attr_reader :profiles_sample_rate
253
+
214
254
  # these are not config options
215
255
  # @!visibility private
216
256
  attr_reader :errors, :gem_specs
@@ -237,9 +277,20 @@ module Sentry
237
277
  MODULE_SEPARATOR = "::".freeze
238
278
  SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
239
279
 
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 = []
280
+ INSTRUMENTERS = [:sentry, :otel]
281
+
282
+ class << self
283
+ # Post initialization callbacks are called at the end of initialization process
284
+ # allowing extending the configuration of sentry-ruby by multiple extensions
285
+ def post_initialization_callbacks
286
+ @post_initialization_callbacks ||= []
287
+ end
288
+
289
+ # allow extensions to add their hooks to the Configuration class
290
+ def add_post_initialization_callback(&block)
291
+ post_initialization_callbacks << block
292
+ end
293
+ end
243
294
 
244
295
  def initialize
245
296
  self.app_dirs_pattern = nil
@@ -249,7 +300,7 @@ module Sentry
249
300
  self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
250
301
  self.breadcrumbs_logger = []
251
302
  self.context_lines = 3
252
- self.capture_exception_frame_locals = false
303
+ self.include_local_variables = false
253
304
  self.environment = environment_from_env
254
305
  self.enabled_environments = []
255
306
  self.exclude_loggers = []
@@ -269,11 +320,14 @@ module Sentry
269
320
  self.trusted_proxies = []
270
321
  self.dsn = ENV['SENTRY_DSN']
271
322
  self.server_name = server_name_from_env
323
+ self.instrumenter = :sentry
272
324
 
273
325
  self.before_send = nil
326
+ self.before_send_transaction = nil
274
327
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
275
328
  self.traces_sample_rate = nil
276
329
  self.traces_sampler = nil
330
+ self.enable_tracing = nil
277
331
 
278
332
  @transport = Transport::Configuration.new
279
333
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
@@ -322,6 +376,12 @@ module Sentry
322
376
  @before_send = value
323
377
  end
324
378
 
379
+ def before_send_transaction=(value)
380
+ check_callable!("before_send_transaction", value)
381
+
382
+ @before_send_transaction = value
383
+ end
384
+
325
385
  def before_breadcrumb=(value)
326
386
  check_callable!("before_breadcrumb", value)
327
387
 
@@ -332,6 +392,20 @@ module Sentry
332
392
  @environment = environment.to_s
333
393
  end
334
394
 
395
+ def instrumenter=(instrumenter)
396
+ @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
397
+ end
398
+
399
+ def enable_tracing=(enable_tracing)
400
+ @enable_tracing = enable_tracing
401
+ @traces_sample_rate ||= 1.0 if enable_tracing
402
+ end
403
+
404
+ def profiles_sample_rate=(profiles_sample_rate)
405
+ log_info("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
406
+ @profiles_sample_rate = profiles_sample_rate
407
+ end
408
+
335
409
  def sending_allowed?
336
410
  @errors = []
337
411
 
@@ -362,7 +436,20 @@ module Sentry
362
436
  end
363
437
 
364
438
  def tracing_enabled?
365
- !!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
439
+ valid_sampler = !!((@traces_sample_rate &&
440
+ @traces_sample_rate >= 0.0 &&
441
+ @traces_sample_rate <= 1.0) ||
442
+ @traces_sampler)
443
+
444
+ (@enable_tracing != false) && valid_sampler && sending_allowed?
445
+ end
446
+
447
+ def profiling_enabled?
448
+ valid_sampler = !!(@profiles_sample_rate &&
449
+ @profiles_sample_rate >= 0.0 &&
450
+ @profiles_sample_rate <= 1.0)
451
+
452
+ tracing_enabled? && valid_sampler && sending_allowed?
366
453
  end
367
454
 
368
455
  # @return [String, nil]
@@ -487,16 +574,5 @@ module Sentry
487
574
  instance_eval(&hook)
488
575
  end
489
576
  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
577
  end
502
578
  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/event.rb CHANGED
@@ -18,7 +18,7 @@ module Sentry
18
18
  event_id level timestamp
19
19
  release environment server_name modules
20
20
  message user tags contexts extra
21
- fingerprint breadcrumbs transaction
21
+ fingerprint breadcrumbs transaction transaction_info
22
22
  platform sdk type
23
23
  )
24
24
 
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,11 +88,33 @@ 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
119
  check_argument_type!(exception, ::Exception)
97
120
 
@@ -101,6 +124,7 @@ module Sentry
101
124
 
102
125
  options[:hint] ||= {}
103
126
  options[:hint][:exception] = exception
127
+
104
128
  event = current_client.event_from_exception(exception, options[:hint])
105
129
 
106
130
  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,31 +105,21 @@ 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
123
111
  !(key.start_with?('HTTP_') || CONTENT_HEADERS.include?(key))
124
112
  end
125
113
 
126
- # Rack adds in an incorrect HTTP_VERSION key, which causes downstream
114
+ # In versions < 3, Rack adds in an incorrect HTTP_VERSION key, which causes downstream
127
115
  # to think this is a Version header. Instead, this is mapped to
128
116
  # env['SERVER_PROTOCOL']. But we don't want to ignore a valid header
129
117
  # if the request has legitimately sent a Version header themselves.
130
118
  # See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
131
- # NOTE: This will be removed in version 3.0+
132
119
  def is_server_protocol?(key, value, protocol_version)
120
+ rack_version = Gem::Version.new(::Rack.release)
121
+ return false if rack_version >= Gem::Version.new("3.0")
122
+
133
123
  key == 'HTTP_VERSION' && value == protocol_version
134
124
  end
135
125
 
@@ -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,21 @@ 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?
29
+ return super unless started? && Sentry.initialized?
30
+ return super if from_sentry_sdk?
30
31
 
31
- sentry_span = start_sentry_span
32
- set_sentry_trace_header(req, sentry_span)
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)
33
34
 
34
- super.tap do |res|
35
- record_sentry_breadcrumb(req, res)
36
- record_sentry_span(req, res, sentry_span)
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(:status, res.code.to_i)
42
+ end
43
+ end
37
44
  end
38
45
  end
39
46
 
@@ -42,13 +49,17 @@ module Sentry
42
49
  def set_sentry_trace_header(req, sentry_span)
43
50
  return unless sentry_span
44
51
 
45
- trace = Sentry.get_current_client.generate_sentry_trace(sentry_span)
52
+ client = Sentry.get_current_client
53
+
54
+ trace = client.generate_sentry_trace(sentry_span)
46
55
  req[SENTRY_TRACE_HEADER_NAME] = trace if trace
56
+
57
+ baggage = client.generate_baggage(sentry_span)
58
+ req[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
47
59
  end
48
60
 
49
61
  def record_sentry_breadcrumb(req, res)
50
62
  return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
51
- return if from_sentry_sdk?
52
63
 
53
64
  request_info = extract_request_info(req)
54
65
 
@@ -64,29 +75,6 @@ module Sentry
64
75
  Sentry.add_breadcrumb(crumb)
65
76
  end
66
77
 
67
- def record_sentry_span(req, res, sentry_span)
68
- return unless Sentry.initialized? && sentry_span
69
-
70
- request_info = extract_request_info(req)
71
- sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
72
- sentry_span.set_data(:status, res.code.to_i)
73
- finish_sentry_span(sentry_span)
74
- end
75
-
76
- def start_sentry_span
77
- return unless Sentry.initialized? && span = Sentry.get_current_scope.get_span
78
- return if from_sentry_sdk?
79
- return if span.sampled == false
80
-
81
- span.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
82
- end
83
-
84
- def finish_sentry_span(sentry_span)
85
- return unless Sentry.initialized? && sentry_span
86
-
87
- sentry_span.set_timestamp(Sentry.utc_now.to_f)
88
- end
89
-
90
78
  def from_sentry_sdk?
91
79
  dsn = Sentry.configuration.dsn
92
80
  dsn && dsn.host == self.address
@@ -109,7 +97,4 @@ module Sentry
109
97
  end
110
98
  end
111
99
 
112
- Sentry.register_patch do
113
- patch = Sentry::Net::HTTP
114
- Net::HTTP.send(:prepend, patch) unless Net::HTTP.ancestors.include?(patch)
115
- end
100
+ Sentry.register_patch(Sentry::Net::HTTP, Net::HTTP)