sentry-ruby-core 4.4.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +2 -0
  4. data/Gemfile +8 -5
  5. data/LICENSE.txt +1 -1
  6. data/README.md +29 -175
  7. data/bin/console +5 -1
  8. data/lib/sentry/background_worker.rb +33 -3
  9. data/lib/sentry/backtrace.rb +1 -3
  10. data/lib/sentry/breadcrumb/sentry_logger.rb +3 -1
  11. data/lib/sentry/breadcrumb.rb +28 -2
  12. data/lib/sentry/breadcrumb_buffer.rb +16 -0
  13. data/lib/sentry/client.rb +65 -7
  14. data/lib/sentry/configuration.rb +155 -112
  15. data/lib/sentry/core_ext/object/deep_dup.rb +4 -0
  16. data/lib/sentry/core_ext/object/duplicable.rb +2 -0
  17. data/lib/sentry/dsn.rb +6 -1
  18. data/lib/sentry/envelope.rb +26 -0
  19. data/lib/sentry/event.rb +65 -23
  20. data/lib/sentry/exceptions.rb +2 -0
  21. data/lib/sentry/hub.rb +31 -5
  22. data/lib/sentry/integrable.rb +2 -0
  23. data/lib/sentry/interface.rb +3 -10
  24. data/lib/sentry/interfaces/exception.rb +13 -3
  25. data/lib/sentry/interfaces/request.rb +49 -19
  26. data/lib/sentry/interfaces/single_exception.rb +31 -0
  27. data/lib/sentry/interfaces/stacktrace.rb +14 -0
  28. data/lib/sentry/interfaces/stacktrace_builder.rb +39 -10
  29. data/lib/sentry/interfaces/threads.rb +12 -2
  30. data/lib/sentry/linecache.rb +3 -0
  31. data/lib/sentry/net/http.rb +71 -47
  32. data/lib/sentry/rack/capture_exceptions.rb +2 -0
  33. data/lib/sentry/rack.rb +2 -1
  34. data/lib/sentry/rake.rb +33 -9
  35. data/lib/sentry/release_detector.rb +39 -0
  36. data/lib/sentry/scope.rb +76 -6
  37. data/lib/sentry/span.rb +84 -8
  38. data/lib/sentry/transaction.rb +48 -10
  39. data/lib/sentry/transaction_event.rb +19 -6
  40. data/lib/sentry/transport/configuration.rb +4 -2
  41. data/lib/sentry/transport/dummy_transport.rb +2 -0
  42. data/lib/sentry/transport/http_transport.rb +57 -38
  43. data/lib/sentry/transport.rb +80 -19
  44. data/lib/sentry/utils/argument_checking_helper.rb +2 -0
  45. data/lib/sentry/utils/custom_inspection.rb +14 -0
  46. data/lib/sentry/utils/exception_cause_chain.rb +10 -10
  47. data/lib/sentry/utils/logging_helper.rb +6 -4
  48. data/lib/sentry/utils/real_ip.rb +9 -1
  49. data/lib/sentry/utils/request_id.rb +2 -0
  50. data/lib/sentry/version.rb +3 -1
  51. data/lib/sentry-ruby.rb +184 -49
  52. data/sentry-ruby-core.gemspec +2 -3
  53. data/sentry-ruby.gemspec +2 -3
  54. metadata +9 -22
  55. data/.craft.yml +0 -28
  56. data/lib/sentry/benchmarks/benchmark_transport.rb +0 -14
  57. data/lib/sentry/rack/deprecations.rb +0 -19
data/lib/sentry/client.rb CHANGED
@@ -1,11 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/transport"
2
4
 
3
5
  module Sentry
4
6
  class Client
5
7
  include LoggingHelper
6
8
 
7
- attr_reader :transport, :configuration, :logger
9
+ # The Transport object that'll send events for the client.
10
+ # @return [Transport]
11
+ attr_reader :transport
12
+
13
+ # @!macro configuration
14
+ attr_reader :configuration
8
15
 
16
+ # @deprecated Use Sentry.logger to retrieve the current logger instead.
17
+ attr_reader :logger
18
+
19
+ # @param configuration [Configuration]
9
20
  def initialize(configuration)
10
21
  @configuration = configuration
11
22
  @logger = configuration.logger
@@ -23,15 +34,33 @@ module Sentry
23
34
  end
24
35
  end
25
36
 
37
+ # Applies the given scope's data to the event and sends it to Sentry.
38
+ # @param event [Event] the event to be sent.
39
+ # @param scope [Scope] the scope with contextual data that'll be applied to the event before it's sent.
40
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
41
+ # @return [Event, nil]
26
42
  def capture_event(event, scope, hint = {})
27
43
  return unless configuration.sending_allowed?
28
44
 
29
- scope.apply_to_event(event, hint)
45
+ unless event.is_a?(TransactionEvent) || configuration.sample_allowed?
46
+ transport.record_lost_event(:sample_rate, 'event')
47
+ return
48
+ end
49
+
50
+ event_type = event.is_a?(Event) ? event.type : event["type"]
51
+ event = scope.apply_to_event(event, hint)
52
+
53
+ if event.nil?
54
+ log_info("Discarded event because one of the event processors returned nil")
55
+ transport.record_lost_event(:event_processor, event_type)
56
+ return
57
+ end
30
58
 
31
59
  if async_block = configuration.async
32
60
  dispatch_async_event(async_block, event, hint)
33
- elsif hint.fetch(:background, true)
34
- dispatch_background_event(event, hint)
61
+ elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
62
+ queued = dispatch_background_event(event, hint)
63
+ transport.record_lost_event(:queue_overflow, event_type) unless queued
35
64
  else
36
65
  send_event(event, hint)
37
66
  end
@@ -42,9 +71,14 @@ module Sentry
42
71
  nil
43
72
  end
44
73
 
74
+ # Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
75
+ # @param exception [Exception] the exception to be reported.
76
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
77
+ # @return [Event, nil]
45
78
  def event_from_exception(exception, hint = {})
79
+ return unless @configuration.sending_allowed? && @configuration.exception_class_allowed?(exception)
80
+
46
81
  integration_meta = Sentry.integrations[hint[:integration]]
47
- return unless @configuration.exception_class_allowed?(exception)
48
82
 
49
83
  Event.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
50
84
  event.add_exception_interface(exception)
@@ -52,13 +86,22 @@ module Sentry
52
86
  end
53
87
  end
54
88
 
55
- def event_from_message(message, hint = {})
89
+ # Initializes an Event object with the given message.
90
+ # @param message [String] the message to be reported.
91
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
92
+ # @return [Event]
93
+ def event_from_message(message, hint = {}, backtrace: nil)
94
+ return unless @configuration.sending_allowed?
95
+
56
96
  integration_meta = Sentry.integrations[hint[:integration]]
57
97
  event = Event.new(configuration: configuration, integration_meta: integration_meta, message: message)
58
- event.add_threads_interface(backtrace: caller)
98
+ event.add_threads_interface(backtrace: backtrace || caller)
59
99
  event
60
100
  end
61
101
 
102
+ # Initializes an Event object with the given Transaction object.
103
+ # @param transaction [Transaction] the transaction to be recorded.
104
+ # @return [TransactionEvent]
62
105
  def event_from_transaction(transaction)
63
106
  TransactionEvent.new(configuration: configuration).tap do |event|
64
107
  event.transaction = transaction.name
@@ -71,6 +114,7 @@ module Sentry
71
114
  end
72
115
  end
73
116
 
117
+ # @!macro send_event
74
118
  def send_event(event, hint = nil)
75
119
  event_type = event.is_a?(Event) ? event.type : event["type"]
76
120
 
@@ -79,6 +123,7 @@ module Sentry
79
123
 
80
124
  if event.nil?
81
125
  log_info("Discarded event because before_send returned nil")
126
+ transport.record_lost_event(:before_send, 'event')
82
127
  return
83
128
  end
84
129
  end
@@ -92,9 +137,22 @@ module Sentry
92
137
 
93
138
  event_info = Event.get_log_message(event.to_hash)
94
139
  log_info("Unreported #{loggable_event_type}: #{event_info}")
140
+ transport.record_lost_event(:network_error, event_type)
95
141
  raise
96
142
  end
97
143
 
144
+ # Generates a Sentry trace for distribted tracing from the given Span.
145
+ # Returns `nil` if `config.propagate_traces` is `false`.
146
+ # @param span [Span] the span to generate trace from.
147
+ # @return [String, nil]
148
+ def generate_sentry_trace(span)
149
+ return unless configuration.propagate_traces
150
+
151
+ trace = span.to_sentry_trace
152
+ log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
153
+ trace
154
+ end
155
+
98
156
  private
99
157
 
100
158
  def dispatch_background_event(event, hint)
@@ -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,130 +35,179 @@ 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, nil]
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
- # Boolean to check nested exceptions when deciding if to exclude. Defaults to false
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
 
96
129
  # You may provide your own LineCache for matching paths with source files.
97
- # This may be useful if you need to get source code from places other than
98
- # the disk. See Sentry::LineCache for the required interface you must implement.
130
+ # This may be useful if you need to get source code from places other than the disk.
131
+ # @see LineCache
132
+ # @return [LineCache]
99
133
  attr_accessor :linecache
100
134
 
101
135
  # Logger used by Sentry. In Rails, this is the Rails logger, otherwise
102
136
  # Sentry provides its own Sentry::Logger.
137
+ # @return [Logger]
103
138
  attr_accessor :logger
104
139
 
105
140
  # Project directory root for in_app detection. Could be Rails root, etc.
106
141
  # Set automatically for Rails.
107
- attr_reader :project_root
142
+ # @return [String]
143
+ attr_accessor :project_root
144
+
145
+ # Insert sentry-trace to outgoing requests' headers
146
+ # @return [Boolean]
147
+ attr_accessor :propagate_traces
108
148
 
109
149
  # Array of rack env parameters to be included in the event sent to sentry.
150
+ # @return [Array<String>]
110
151
  attr_accessor :rack_env_whitelist
111
152
 
112
153
  # Release tag to be passed with every event sent to Sentry.
113
154
  # We automatically try to set this to a git SHA or Capistrano release.
155
+ # @return [String]
114
156
  attr_accessor :release
115
157
 
116
158
  # The sampling factor to apply to events. A value of 0.0 will not send
117
159
  # any events, and a value of 1.0 will send 100% of events.
160
+ # @return [Float]
118
161
  attr_accessor :sample_rate
119
162
 
120
163
  # Include module versions in reports - boolean.
164
+ # @return [Boolean]
121
165
  attr_accessor :send_modules
122
166
 
123
167
  # When send_default_pii's value is false (default), sensitive information like
124
168
  # - user ip
125
169
  # - user cookie
126
170
  # - request body
171
+ # - query string
127
172
  # will not be sent to Sentry.
173
+ # @return [Boolean]
128
174
  attr_accessor :send_default_pii
129
175
 
176
+ # Allow to skip Sentry emails within rake tasks
177
+ # @return [Boolean]
178
+ attr_accessor :skip_rake_integration
179
+
130
180
  # IP ranges for trusted proxies that will be skipped when calculating IP address.
131
181
  attr_accessor :trusted_proxies
132
182
 
183
+ # @return [String]
133
184
  attr_accessor :server_name
134
185
 
135
186
  # Return a Transport::Configuration object for transport-related configurations.
187
+ # @return [Transport]
136
188
  attr_reader :transport
137
189
 
138
190
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
191
+ # @return [Float]
139
192
  attr_accessor :traces_sample_rate
140
193
 
141
194
  # Take a Proc that controls the sample rate for every tracing event, e.g.
142
- # ```
143
- # lambda do |tracing_context|
144
- # # tracing_context[:transaction_context] contains the information about the transaction
145
- # # tracing_context[:parent_sampled] contains the transaction's parent's sample decision
146
- # true # return value can be a boolean or a float between 0.0 and 1.0
147
- # end
148
- # ```
195
+ # @example
196
+ # config.traces_sampler = lambda do |tracing_context|
197
+ # # tracing_context[:transaction_context] contains the information about the transaction
198
+ # # tracing_context[:parent_sampled] contains the transaction's parent's sample decision
199
+ # true # return value can be a boolean or a float between 0.0 and 1.0
200
+ # end
201
+ # @return [Proc]
149
202
  attr_accessor :traces_sampler
150
203
 
204
+ # Send diagnostic client reports about dropped events, true by default
205
+ # tries to attach to an existing envelope max once every 30s
206
+ # @return [Boolean]
207
+ attr_accessor :send_client_reports
208
+
151
209
  # these are not config options
210
+ # @!visibility private
152
211
  attr_reader :errors, :gem_specs
153
212
 
154
213
  # Most of these errors generate 4XX responses. In general, Sentry clients
@@ -171,17 +230,21 @@ module Sentry
171
230
 
172
231
  LOG_PREFIX = "** [Sentry] ".freeze
173
232
  MODULE_SEPARATOR = "::".freeze
233
+ SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
174
234
 
175
235
  # Post initialization callbacks are called at the end of initialization process
176
236
  # allowing extending the configuration of sentry-ruby by multiple extensions
177
237
  @@post_initialization_callbacks = []
178
238
 
179
239
  def initialize
240
+ self.app_dirs_pattern = nil
180
241
  self.debug = false
181
242
  self.background_worker_threads = Concurrent.processor_count
243
+ self.backtrace_cleanup_callback = nil
182
244
  self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
183
245
  self.breadcrumbs_logger = []
184
246
  self.context_lines = 3
247
+ self.capture_exception_frame_locals = false
185
248
  self.environment = environment_from_env
186
249
  self.enabled_environments = []
187
250
  self.exclude_loggers = []
@@ -190,17 +253,21 @@ module Sentry
190
253
  self.linecache = ::Sentry::LineCache.new
191
254
  self.logger = ::Sentry::Logger.new(STDOUT)
192
255
  self.project_root = Dir.pwd
256
+ self.propagate_traces = true
193
257
 
194
- self.release = detect_release
195
258
  self.sample_rate = 1.0
196
259
  self.send_modules = true
197
260
  self.send_default_pii = false
261
+ self.skip_rake_integration = false
262
+ self.send_client_reports = true
198
263
  self.trusted_proxies = []
199
264
  self.dsn = ENV['SENTRY_DSN']
200
265
  self.server_name = server_name_from_env
201
266
 
202
- self.before_send = false
267
+ self.before_send = nil
203
268
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
269
+ self.traces_sample_rate = nil
270
+ self.traces_sampler = nil
204
271
 
205
272
  @transport = Transport::Configuration.new
206
273
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
@@ -209,18 +276,13 @@ module Sentry
209
276
  end
210
277
 
211
278
  def dsn=(value)
212
- return if value.nil? || value.empty?
213
-
214
- @dsn = DSN.new(value)
279
+ @dsn = init_dsn(value)
215
280
  end
216
281
 
217
282
  alias server= dsn=
218
283
 
219
-
220
284
  def async=(value)
221
- if value && !value.respond_to?(:call)
222
- raise(ArgumentError, "async must be callable")
223
- end
285
+ check_callable!("async", value)
224
286
 
225
287
  @async = value
226
288
  end
@@ -239,17 +301,13 @@ module Sentry
239
301
  end
240
302
 
241
303
  def before_send=(value)
242
- unless value == false || value.respond_to?(:call)
243
- raise ArgumentError, "before_send must be callable (or false to disable)"
244
- end
304
+ check_callable!("before_send", value)
245
305
 
246
306
  @before_send = value
247
307
  end
248
308
 
249
309
  def before_breadcrumb=(value)
250
- unless value.nil? || value.respond_to?(:call)
251
- raise ArgumentError, "before_breadcrumb must be callable (or nil to disable)"
252
- end
310
+ check_callable!("before_breadcrumb", value)
253
311
 
254
312
  @before_breadcrumb = value
255
313
  end
@@ -261,18 +319,13 @@ module Sentry
261
319
  def sending_allowed?
262
320
  @errors = []
263
321
 
264
- valid? &&
265
- capture_in_environment? &&
266
- sample_allowed?
322
+ valid? && capture_in_environment?
267
323
  end
268
324
 
269
- def error_messages
270
- @errors = [@errors[0]] + @errors[1..-1].map(&:downcase) # fix case of all but first
271
- @errors.join(", ")
272
- end
325
+ def sample_allowed?
326
+ return true if sample_rate == 1.0
273
327
 
274
- def project_root=(root_dir)
275
- @project_root = root_dir
328
+ Random.rand < sample_rate
276
329
  end
277
330
 
278
331
  def exception_class_allowed?(exc)
@@ -296,6 +349,17 @@ module Sentry
296
349
  !!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
297
350
  end
298
351
 
352
+ # @return [String, nil]
353
+ def csp_report_uri
354
+ if dsn && dsn.valid?
355
+ uri = dsn.csp_report_uri
356
+ uri += "&sentry_release=#{CGI.escape(release)}" if release && !release.empty?
357
+ uri += "&sentry_environment=#{CGI.escape(environment)}" if environment && !environment.empty?
358
+ uri
359
+ end
360
+ end
361
+
362
+ # @api private
299
363
  def stacktrace_builder
300
364
  @stacktrace_builder ||= StacktraceBuilder.new(
301
365
  project_root: @project_root.to_s,
@@ -306,17 +370,39 @@ module Sentry
306
370
  )
307
371
  end
308
372
 
309
- private
310
-
373
+ # @api private
311
374
  def detect_release
312
- detect_release_from_env ||
313
- detect_release_from_git ||
314
- detect_release_from_capistrano ||
315
- detect_release_from_heroku
375
+ return unless sending_allowed?
376
+
377
+ self.release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?)
378
+
379
+ if running_on_heroku? && release.nil?
380
+ log_warn(HEROKU_DYNO_METADATA_MESSAGE)
381
+ end
316
382
  rescue => e
317
383
  log_error("Error detecting release", e, debug: debug)
318
384
  end
319
385
 
386
+ # @api private
387
+ def error_messages
388
+ @errors = [@errors[0]] + @errors[1..-1].map(&:downcase) # fix case of all but first
389
+ @errors.join(", ")
390
+ end
391
+
392
+ private
393
+
394
+ def check_callable!(name, value)
395
+ unless value == nil || value.respond_to?(:call)
396
+ raise ArgumentError, "#{name} must be callable (or nil to disable)"
397
+ end
398
+ end
399
+
400
+ def init_dsn(dsn_string)
401
+ return if dsn_string.nil? || dsn_string.empty?
402
+
403
+ DSN.new(dsn_string)
404
+ end
405
+
320
406
  def excluded_exception?(incoming_exception)
321
407
  excluded_exception_classes.any? do |excluded_exception|
322
408
  matches_exception?(excluded_exception, incoming_exception)
@@ -346,37 +432,6 @@ module Sentry
346
432
  nil
347
433
  end
348
434
 
349
- def detect_release_from_heroku
350
- return unless running_on_heroku?
351
- return if ENV['CI']
352
- log_warn(HEROKU_DYNO_METADATA_MESSAGE) && return unless ENV['HEROKU_SLUG_COMMIT']
353
-
354
- ENV['HEROKU_SLUG_COMMIT']
355
- end
356
-
357
- def running_on_heroku?
358
- File.directory?("/etc/heroku")
359
- end
360
-
361
- def detect_release_from_capistrano
362
- revision_file = File.join(project_root, 'REVISION')
363
- revision_log = File.join(project_root, '..', 'revisions.log')
364
-
365
- if File.exist?(revision_file)
366
- File.read(revision_file).strip
367
- elsif File.exist?(revision_log)
368
- File.open(revision_log).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
369
- end
370
- end
371
-
372
- def detect_release_from_git
373
- Sentry.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
374
- end
375
-
376
- def detect_release_from_env
377
- ENV['SENTRY_RELEASE']
378
- end
379
-
380
435
  def capture_in_environment?
381
436
  return true if enabled_in_current_env?
382
437
 
@@ -393,36 +448,24 @@ module Sentry
393
448
  end
394
449
  end
395
450
 
396
- def sample_allowed?
397
- return true if sample_rate == 1.0
398
-
399
- if Random::DEFAULT.rand >= sample_rate
400
- @errors << "Excluded by random sample"
401
- false
402
- else
403
- true
404
- end
405
- end
406
-
407
- # Try to resolve the hostname to an FQDN, but fall back to whatever
408
- # the load name is.
409
- def resolve_hostname
410
- Socket.gethostname ||
411
- Socket.gethostbyname(hostname).first rescue server_name
412
- end
413
-
414
451
  def environment_from_env
415
- ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
452
+ ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
416
453
  end
417
454
 
418
455
  def server_name_from_env
419
456
  if running_on_heroku?
420
457
  ENV['DYNO']
421
458
  else
422
- resolve_hostname
459
+ # Try to resolve the hostname to an FQDN, but fall back to whatever
460
+ # the load name is.
461
+ Socket.gethostname || Socket.gethostbyname(hostname).first rescue server_name
423
462
  end
424
463
  end
425
464
 
465
+ def running_on_heroku?
466
+ File.directory?("/etc/heroku") && !ENV["CI"]
467
+ end
468
+
426
469
  def run_post_initialization_callbacks
427
470
  self.class.post_initialization_callbacks.each do |hook|
428
471
  instance_eval(&hook)
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ return if Object.method_defined?(:deep_dup)
4
+
1
5
  require 'sentry/core_ext/object/duplicable'
2
6
 
3
7
  #########################################
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ return if Object.method_defined?(:duplicable?)
4
+
3
5
  #########################################
4
6
  # This file was copied from Rails 5.2 #
5
7
  #########################################
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
@@ -37,10 +39,13 @@ module Sentry
37
39
  def server
38
40
  server = "#{scheme}://#{host}"
39
41
  server += ":#{port}" unless port == PORT_MAP[scheme]
40
- server += path
41
42
  server
42
43
  end
43
44
 
45
+ def csp_report_uri
46
+ "#{server}/api/#{project_id}/security/?sentry_key=#{public_key}"
47
+ end
48
+
44
49
  def envelope_endpoint
45
50
  "#{path}/api/#{project_id}/envelope/"
46
51
  end