sentry-raven 2.1.3 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.craft.yml +19 -0
  3. data/.scripts/bump-version.rb +5 -0
  4. data/CHANGELOG.md +703 -0
  5. data/Gemfile +37 -0
  6. data/Makefile +3 -0
  7. data/README.md +116 -18
  8. data/Rakefile +30 -0
  9. data/exe/raven +32 -0
  10. data/lib/raven/backtrace.rb +16 -6
  11. data/lib/raven/base.rb +17 -4
  12. data/lib/raven/breadcrumbs/{activesupport.rb → active_support_logger.rb} +9 -3
  13. data/lib/raven/breadcrumbs/logger.rb +2 -92
  14. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  15. data/lib/raven/breadcrumbs.rb +3 -1
  16. data/lib/raven/cli.rb +31 -43
  17. data/lib/raven/client.rb +39 -17
  18. data/lib/raven/configuration.rb +277 -37
  19. data/lib/raven/context.rb +17 -11
  20. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  21. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  22. data/lib/raven/event.rb +172 -233
  23. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  24. data/lib/raven/instance.rb +51 -25
  25. data/lib/raven/integrations/delayed_job.rb +18 -18
  26. data/lib/raven/integrations/rack-timeout.rb +11 -5
  27. data/lib/raven/integrations/rack.rb +36 -19
  28. data/lib/raven/integrations/rails/active_job.rb +52 -20
  29. data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
  30. data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
  31. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  32. data/lib/raven/integrations/rails.rb +24 -8
  33. data/lib/raven/integrations/rake.rb +6 -1
  34. data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
  35. data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
  36. data/lib/raven/integrations/sidekiq.rb +6 -57
  37. data/lib/raven/interface.rb +2 -2
  38. data/lib/raven/interfaces/exception.rb +0 -2
  39. data/lib/raven/interfaces/http.rb +0 -2
  40. data/lib/raven/interfaces/message.rb +1 -1
  41. data/lib/raven/interfaces/single_exception.rb +0 -2
  42. data/lib/raven/interfaces/stack_trace.rb +19 -27
  43. data/lib/raven/linecache.rb +34 -17
  44. data/lib/raven/logger.rb +11 -18
  45. data/lib/raven/processor/cookies.rb +27 -7
  46. data/lib/raven/processor/http_headers.rb +18 -5
  47. data/lib/raven/processor/post_data.rb +16 -3
  48. data/lib/raven/processor/removecircularreferences.rb +12 -8
  49. data/lib/raven/processor/removestacktrace.rb +17 -6
  50. data/lib/raven/processor/sanitizedata.rb +88 -29
  51. data/lib/raven/processor/utf8conversion.rb +39 -14
  52. data/lib/raven/processor.rb +1 -1
  53. data/lib/raven/transports/http.rb +29 -21
  54. data/lib/raven/transports/stdout.rb +20 -0
  55. data/lib/raven/transports.rb +4 -8
  56. data/lib/raven/utils/context_filter.rb +42 -0
  57. data/lib/raven/utils/deep_merge.rb +6 -12
  58. data/lib/raven/utils/exception_cause_chain.rb +20 -0
  59. data/lib/raven/utils/real_ip.rb +1 -1
  60. data/lib/raven/utils/request_id.rb +16 -0
  61. data/lib/raven/version.rb +2 -2
  62. data/lib/sentry-raven-without-integrations.rb +6 -1
  63. data/lib/sentry_raven_without_integrations.rb +1 -0
  64. data/sentry-raven.gemspec +28 -0
  65. metadata +37 -103
  66. data/lib/raven/error.rb +0 -4
@@ -1,4 +1,3 @@
1
- require 'logger'
2
1
  require 'uri'
3
2
 
4
3
  module Raven
@@ -13,6 +12,11 @@ module Raven
13
12
  attr_reader :async
14
13
  alias async? async
15
14
 
15
+ # An array of breadcrumbs loggers to be used. Available options are:
16
+ # - :sentry_logger
17
+ # - :active_support_logger
18
+ attr_reader :breadcrumbs_logger
19
+
16
20
  # Number of lines of code context to capture, or nil for none
17
21
  attr_accessor :context_lines
18
22
 
@@ -32,12 +36,25 @@ module Raven
32
36
  # You should probably append to this rather than overwrite it.
33
37
  attr_accessor :excluded_exceptions
34
38
 
39
+ # Boolean to check nested exceptions when deciding if to exclude. Defaults to false
40
+ attr_accessor :inspect_exception_causes_for_exclusion
41
+ alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion
42
+
35
43
  # DSN component - set automatically if DSN provided
36
44
  attr_accessor :host
37
45
 
38
46
  # The Faraday adapter to be used. Will default to Net::HTTP when not set.
39
47
  attr_accessor :http_adapter
40
48
 
49
+ # A Proc yeilding the faraday builder allowing for further configuration
50
+ # of the faraday adapter
51
+ attr_accessor :faraday_builder
52
+
53
+ # You may provide your own LineCache for matching paths with source files.
54
+ # This may be useful if you need to get source code from places other than
55
+ # the disk. See Raven::LineCache for the required interface you must implement.
56
+ attr_accessor :linecache
57
+
41
58
  # Logger used by Raven. In Rails, this is the Rails logger, otherwise
42
59
  # Raven provides its own Raven::Logger.
43
60
  attr_accessor :logger
@@ -71,7 +88,7 @@ module Raven
71
88
  attr_accessor :public_key
72
89
 
73
90
  # Turns on ActiveSupport breadcrumbs integration
74
- attr_accessor :rails_activesupport_breadcrumbs
91
+ attr_reader :rails_activesupport_breadcrumbs
75
92
 
76
93
  # Rails catches exceptions in the ActionDispatch::ShowExceptions or
77
94
  # ActionDispatch::DebugExceptions middlewares, depending on the environment.
@@ -83,6 +100,10 @@ module Raven
83
100
  # We automatically try to set this to a git SHA or Capistrano release.
84
101
  attr_accessor :release
85
102
 
103
+ # The sampling factor to apply to events. A value of 0.0 will not send
104
+ # any events, and a value of 1.0 will send 100% of events.
105
+ attr_accessor :sample_rate
106
+
86
107
  # Boolean - sanitize values that look like credit card numbers
87
108
  attr_accessor :sanitize_credit_cards
88
109
 
@@ -91,6 +112,10 @@ module Raven
91
112
  # a hash key, will be censored and not sent to Sentry.
92
113
  attr_accessor :sanitize_fields
93
114
 
115
+ # If you're sure you want to override the default sanitization values, you can
116
+ # add to them to an array of Strings here, e.g. %w(authorization password)
117
+ attr_accessor :sanitize_fields_excluded
118
+
94
119
  # Sanitize additional HTTP headers - only Authorization is removed by default.
95
120
  attr_accessor :sanitize_http_headers
96
121
 
@@ -98,8 +123,23 @@ module Raven
98
123
  # Otherwise, can be one of "http", "https", or "dummy"
99
124
  attr_accessor :scheme
100
125
 
126
+ # a proc/lambda that takes an array of stack traces
127
+ # it'll be used to silence (reduce) backtrace of the exception
128
+ #
129
+ # for example:
130
+ #
131
+ # ```ruby
132
+ # Raven.configuration.backtrace_cleanup_callback = lambda do |backtrace|
133
+ # Rails.backtrace_cleaner.clean(backtrace)
134
+ # end
135
+ # ```
136
+ #
137
+ attr_accessor :backtrace_cleanup_callback
138
+
101
139
  # Secret key for authentication with the Sentry server
102
140
  # If you provide a DSN, this will be set automatically.
141
+ #
142
+ # This is deprecated and not necessary for newer Sentry installations any more.
103
143
  attr_accessor :secret_key
104
144
 
105
145
  # Include module versions in reports - boolean.
@@ -114,7 +154,7 @@ module Raven
114
154
  # Note that the object passed into the block will be a String (messages) or
115
155
  # an exception.
116
156
  # e.g. lambda { |exc_or_msg| exc_or_msg.some_attr == false }
117
- attr_accessor :should_capture
157
+ attr_reader :should_capture
118
158
 
119
159
  # Silences ready message when true.
120
160
  attr_accessor :silence_ready
@@ -138,14 +178,45 @@ module Raven
138
178
  # E.g. lambda { |event| Thread.new { MyJobProcessor.send_email(event) } }
139
179
  attr_reader :transport_failure_callback
140
180
 
181
+ # Optional Proc, called before sending an event to the server/
182
+ # E.g.: lambda { |event, hint| event }
183
+ # E.g.: lambda { |event, hint| nil }
184
+ # E.g.: lambda { |event, hint|
185
+ # event[:message] = 'a'
186
+ # event
187
+ # }
188
+ attr_reader :before_send
189
+
190
+ # Errors object - an Array that contains error messages. See #
191
+ attr_reader :errors
192
+
193
+ # the dsn value, whether it's set via `config.dsn=` or `ENV["SENTRY_DSN"]`
194
+ attr_reader :dsn
195
+
196
+ # Array of rack env parameters to be included in the event sent to sentry.
197
+ attr_accessor :rack_env_whitelist
198
+
199
+ # Most of these errors generate 4XX responses. In general, Sentry clients
200
+ # only automatically report 5xx responses.
141
201
  IGNORE_DEFAULT = [
142
202
  'AbstractController::ActionNotFound',
203
+ 'ActionController::BadRequest',
143
204
  'ActionController::InvalidAuthenticityToken',
205
+ 'ActionController::InvalidCrossOriginRequest',
206
+ 'ActionController::MethodNotAllowed',
207
+ 'ActionController::NotImplemented',
208
+ 'ActionController::ParameterMissing',
144
209
  'ActionController::RoutingError',
145
210
  'ActionController::UnknownAction',
211
+ 'ActionController::UnknownFormat',
212
+ 'ActionController::UnknownHttpMethod',
213
+ 'ActionDispatch::Http::Parameters::ParseError',
214
+ 'ActiveJob::DeserializationError', # Can cause infinite loops
146
215
  'ActiveRecord::RecordNotFound',
147
216
  'CGI::Session::CookieStore::TamperedWithCookie',
148
217
  'Mongoid::Errors::DocumentNotFound',
218
+ 'Rack::QueryParser::InvalidParameterError',
219
+ 'Rack::QueryParser::ParameterTypeError',
149
220
  'Sinatra::NotFound'
150
221
  ].freeze
151
222
 
@@ -160,34 +231,61 @@ module Raven
160
231
  Raven::Processor::HTTPHeaders
161
232
  ].freeze
162
233
 
234
+ HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
235
+ "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`".freeze
236
+
237
+ RACK_ENV_WHITELIST_DEFAULT = %w(
238
+ REMOTE_ADDR
239
+ SERVER_NAME
240
+ SERVER_PORT
241
+ ).freeze
242
+
243
+ LOG_PREFIX = "** [Raven] ".freeze
244
+ MODULE_SEPARATOR = "::".freeze
245
+
246
+ AVAILABLE_BREADCRUMBS_LOGGERS = [:sentry_logger, :active_support_logger].freeze
247
+
163
248
  def initialize
164
249
  self.async = false
250
+ self.breadcrumbs_logger = []
165
251
  self.context_lines = 3
166
- self.current_environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
252
+ self.current_environment = current_environment_from_env
167
253
  self.encoding = 'gzip'
168
254
  self.environments = []
169
255
  self.exclude_loggers = []
170
256
  self.excluded_exceptions = IGNORE_DEFAULT.dup
257
+ self.inspect_exception_causes_for_exclusion = false
258
+ self.linecache = ::Raven::LineCache.new
259
+ self.logger = ::Raven::Logger.new(STDOUT)
171
260
  self.open_timeout = 1
172
261
  self.processors = DEFAULT_PROCESSORS.dup
173
- self.proxy = nil
174
- self.rails_activesupport_breadcrumbs = false
262
+ self.project_root = detect_project_root
263
+ @rails_activesupport_breadcrumbs = false
264
+
175
265
  self.rails_report_rescued_exceptions = true
176
266
  self.release = detect_release
267
+ self.sample_rate = 1.0
177
268
  self.sanitize_credit_cards = true
178
269
  self.sanitize_fields = []
270
+ self.sanitize_fields_excluded = []
179
271
  self.sanitize_http_headers = []
180
272
  self.send_modules = true
181
- self.server = ENV['SENTRY_DSN'] if ENV['SENTRY_DSN']
182
- self.server_name = resolve_hostname
273
+ self.server = ENV['SENTRY_DSN']
274
+ self.server_name = server_name_from_env
183
275
  self.should_capture = false
184
276
  self.ssl_verification = true
185
277
  self.tags = {}
186
278
  self.timeout = 2
187
279
  self.transport_failure_callback = false
280
+ self.before_send = false
281
+ self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
188
282
  end
189
283
 
190
284
  def server=(value)
285
+ return if value.nil?
286
+
287
+ @dsn = value
288
+
191
289
  uri = URI.parse(value)
192
290
  uri_path = uri.path.split('/')
193
291
 
@@ -195,7 +293,7 @@ module Raven
195
293
  # DSN-style string
196
294
  self.project_id = uri_path.pop
197
295
  self.public_key = uri.user
198
- self.secret_key = uri.password
296
+ self.secret_key = !(uri.password.nil? || uri.password.empty?) ? uri.password : nil
199
297
  end
200
298
 
201
299
  self.scheme = uri.scheme
@@ -205,13 +303,14 @@ module Raven
205
303
 
206
304
  # For anyone who wants to read the base server string
207
305
  @server = "#{scheme}://#{host}"
208
- @server << ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
209
- @server << path
306
+ @server += ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
307
+ @server += path
210
308
  end
211
309
  alias dsn= server=
212
310
 
213
311
  def encoding=(encoding)
214
312
  raise(Error, 'Unsupported encoding') unless %w(gzip json).include? encoding
313
+
215
314
  @encoding = encoding
216
315
  end
217
316
 
@@ -219,13 +318,32 @@ module Raven
219
318
  unless value == false || value.respond_to?(:call)
220
319
  raise(ArgumentError, "async must be callable (or false to disable)")
221
320
  end
321
+
222
322
  @async = value
223
323
  end
224
324
 
325
+ def breadcrumbs_logger=(logger)
326
+ loggers =
327
+ if logger.is_a?(Array)
328
+ logger
329
+ else
330
+ unless AVAILABLE_BREADCRUMBS_LOGGERS.include?(logger)
331
+ raise Raven::Error, "Unsupported breadcrumbs logger. Supported loggers: #{AVAILABLE_BREADCRUMBS_LOGGERS}"
332
+ end
333
+
334
+ Array(logger)
335
+ end
336
+
337
+ require "raven/breadcrumbs/sentry_logger" if loggers.include?(:sentry_logger)
338
+
339
+ @breadcrumbs_logger = logger
340
+ end
341
+
225
342
  def transport_failure_callback=(value)
226
343
  unless value == false || value.respond_to?(:call)
227
344
  raise(ArgumentError, "transport_failure_callback must be callable (or false to disable)")
228
345
  end
346
+
229
347
  @transport_failure_callback = value
230
348
  end
231
349
 
@@ -233,9 +351,18 @@ module Raven
233
351
  unless value == false || value.respond_to?(:call)
234
352
  raise ArgumentError, "should_capture must be callable (or false to disable)"
235
353
  end
354
+
236
355
  @should_capture = value
237
356
  end
238
357
 
358
+ def before_send=(value)
359
+ unless value == false || value.respond_to?(:call)
360
+ raise ArgumentError, "before_send must be callable (or false to disable)"
361
+ end
362
+
363
+ @before_send = value
364
+ end
365
+
239
366
  # Allows config options to be read like a hash
240
367
  #
241
368
  # @param [Symbol] option Key for a given attribute
@@ -248,17 +375,19 @@ module Raven
248
375
  end
249
376
 
250
377
  def capture_allowed?(message_or_exc = nil)
251
- capture_in_current_environment? &&
252
- capture_allowed_by_callback?(message_or_exc)
253
- end
378
+ @errors = []
254
379
 
380
+ valid? &&
381
+ capture_in_current_environment? &&
382
+ capture_allowed_by_callback?(message_or_exc) &&
383
+ sample_allowed?
384
+ end
255
385
  # If we cannot capture, we cannot send.
256
386
  alias sending_allowed? capture_allowed?
257
387
 
258
- def verify!
259
- %w(server public_key secret_key project_id).each do |key|
260
- raise(Error, "No #{key} specified") unless public_send key
261
- end
388
+ def error_messages
389
+ @errors = [errors[0]] + errors[1..-1].map(&:downcase) # fix case of all but first
390
+ errors.join(", ")
262
391
  end
263
392
 
264
393
  def project_root=(root_dir)
@@ -266,45 +395,144 @@ module Raven
266
395
  Backtrace::Line.instance_variable_set(:@in_app_pattern, nil) # blow away cache
267
396
  end
268
397
 
398
+ def rails_activesupport_breadcrumbs=(val)
399
+ DeprecationHelper.deprecate_old_breadcrumbs_configuration(:active_support_logger)
400
+ @rails_activesupport_breadcrumbs = val
401
+ end
402
+
403
+ def exception_class_allowed?(exc)
404
+ if exc.is_a?(Raven::Error)
405
+ # Try to prevent error reporting loops
406
+ logger.debug "Refusing to capture Raven error: #{exc.inspect}"
407
+ false
408
+ elsif excluded_exception?(exc)
409
+ logger.debug "User excluded error: #{exc.inspect}"
410
+ false
411
+ else
412
+ true
413
+ end
414
+ end
415
+
416
+ def enabled_in_current_env?
417
+ environments.empty? || environments.include?(current_environment)
418
+ end
419
+
420
+ private
421
+
422
+ def detect_project_root
423
+ if defined? Rails.root # we are in a Rails application
424
+ Rails.root.to_s
425
+ else
426
+ Dir.pwd
427
+ end
428
+ end
429
+
269
430
  def detect_release
270
- detect_release_from_git ||
431
+ detect_release_from_env ||
432
+ detect_release_from_git ||
271
433
  detect_release_from_capistrano ||
272
434
  detect_release_from_heroku
435
+ rescue => e
436
+ logger.error "Error detecting release: #{e.message}"
273
437
  end
274
438
 
275
- private
439
+ def excluded_exception?(incoming_exception)
440
+ excluded_exceptions.any? do |excluded_exception|
441
+ matches_exception?(get_exception_class(excluded_exception), incoming_exception)
442
+ end
443
+ end
276
444
 
277
- def detect_release_from_heroku
278
- sys_dyno_info = File.read("/etc/heroku/dyno").strip if File.directory?("/etc/heroku") rescue nil
279
- return unless sys_dyno_info
280
-
281
- # being overly cautious, because if we raise an error Raven won't start
282
- begin
283
- hash = JSON.parse(sys_dyno_info)
284
- hash && hash["release"] && hash["release"]["commit"]
285
- rescue JSON::JSONError
286
- Raven.logger.error "Cannot parse Heroku JSON: #{sys_dyno_info}"
445
+ def get_exception_class(x)
446
+ x.is_a?(Module) ? x : qualified_const_get(x)
447
+ end
448
+
449
+ def matches_exception?(excluded_exception_class, incoming_exception)
450
+ if inspect_exception_causes_for_exclusion?
451
+ Raven::Utils::ExceptionCauseChain.exception_to_array(incoming_exception).any? { |cause| excluded_exception_class === cause }
452
+ else
453
+ excluded_exception_class === incoming_exception
454
+ end
455
+ end
456
+
457
+ # In Ruby <2.0 const_get can't lookup "SomeModule::SomeClass" in one go
458
+ def qualified_const_get(x)
459
+ x = x.to_s
460
+ if !x.match(/::/)
461
+ Object.const_get(x)
462
+ else
463
+ x.split(MODULE_SEPARATOR).reject(&:empty?).inject(Object) { |a, e| a.const_get(e) }
287
464
  end
465
+ rescue NameError # There's no way to safely ask if a constant exist for an unknown string
466
+ nil
467
+ end
468
+
469
+ def detect_release_from_heroku
470
+ return unless running_on_heroku?
471
+ return if ENV['CI']
472
+ logger.warn(HEROKU_DYNO_METADATA_MESSAGE) && return unless ENV['HEROKU_SLUG_COMMIT']
473
+
474
+ ENV['HEROKU_SLUG_COMMIT']
475
+ end
476
+
477
+ def running_on_heroku?
478
+ File.directory?("/etc/heroku")
288
479
  end
289
480
 
290
481
  def detect_release_from_capistrano
291
- version = File.read(File.join(project_root, 'REVISION')).strip rescue nil
482
+ revision_file = File.join(project_root, 'REVISION')
483
+ revision_log = File.join(project_root, '..', 'revisions.log')
292
484
 
293
- # Capistrano 3.0 - 3.1.x
294
- version || File.open(File.join(project_root, '..', 'revisions.log')).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1') rescue nil
485
+ if File.exist?(revision_file)
486
+ File.read(revision_file).strip
487
+ elsif File.exist?(revision_log)
488
+ File.open(revision_log).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
489
+ end
295
490
  end
296
491
 
297
492
  def detect_release_from_git
298
- `git rev-parse --short HEAD`.strip if File.directory?(".git") rescue nil
493
+ Raven.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
494
+ end
495
+
496
+ def detect_release_from_env
497
+ ENV['SENTRY_RELEASE']
299
498
  end
300
499
 
301
500
  def capture_in_current_environment?
302
- !!server && (environments.empty? || environments.include?(current_environment))
501
+ return true if enabled_in_current_env?
502
+
503
+ @errors << "Not configured to send/capture in environment '#{current_environment}'"
504
+ false
303
505
  end
304
506
 
305
507
  def capture_allowed_by_callback?(message_or_exc)
306
- return true if !should_capture || message_or_exc.nil?
307
- should_capture.call(*[message_or_exc])
508
+ return true if !should_capture || message_or_exc.nil? || should_capture.call(message_or_exc)
509
+
510
+ @errors << "should_capture returned false"
511
+ false
512
+ end
513
+
514
+ def valid?
515
+ return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
516
+
517
+ if server
518
+ %w(server host path public_key project_id).map do |key|
519
+ @errors << "No #{key} specified" unless public_send(key)
520
+ end
521
+ else
522
+ @errors << "DSN not set"
523
+ end
524
+ false
525
+ end
526
+
527
+ def sample_allowed?
528
+ return true if sample_rate == 1.0
529
+
530
+ if Random::DEFAULT.rand >= sample_rate
531
+ @errors << "Excluded by random sample"
532
+ false
533
+ else
534
+ true
535
+ end
308
536
  end
309
537
 
310
538
  # Try to resolve the hostname to an FQDN, but fall back to whatever
@@ -313,5 +541,17 @@ module Raven
313
541
  Socket.gethostname ||
314
542
  Socket.gethostbyname(hostname).first rescue server_name
315
543
  end
544
+
545
+ def current_environment_from_env
546
+ ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
547
+ end
548
+
549
+ def server_name_from_env
550
+ if running_on_heroku?
551
+ ENV['DYNO']
552
+ else
553
+ resolve_hostname
554
+ end
555
+ end
316
556
  end
317
557
  end
data/lib/raven/context.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rbconfig'
2
+ require 'etc'
2
3
 
3
4
  module Raven
4
5
  class Context
@@ -10,31 +11,36 @@ module Raven
10
11
  Thread.current[:sentry_context] = nil
11
12
  end
12
13
 
13
- attr_accessor :extra, :server_os, :rack_env, :runtime, :tags, :user
14
+ attr_accessor :transaction, :extra, :server_os, :rack_env, :runtime, :tags, :user
14
15
 
15
16
  def initialize
16
- self.extra = {}
17
17
  self.server_os = self.class.os_context
18
- self.rack_env = nil
19
18
  self.runtime = self.class.runtime_context
19
+ self.extra = { :server => { :os => server_os, :runtime => runtime } }
20
+ self.rack_env = nil
20
21
  self.tags = {}
21
22
  self.user = {}
23
+ self.transaction = []
22
24
  end
23
25
 
24
26
  class << self
25
27
  def os_context
26
- @os_context ||= {
27
- "name" => Raven.sys_command("uname -s") || RbConfig::CONFIG["host_os"],
28
- "version" => Raven.sys_command("uname -v"),
29
- "build" => Raven.sys_command("uname -r"),
30
- "kernel_version" => Raven.sys_command("uname -a") || Raven.sys_command("ver") # windows
31
- }
28
+ @os_context ||=
29
+ begin
30
+ uname = Etc.uname
31
+ {
32
+ name: uname[:sysname] || RbConfig::CONFIG["host_os"],
33
+ version: uname[:version],
34
+ build: uname[:release],
35
+ kernel_version: uname[:version]
36
+ }
37
+ end
32
38
  end
33
39
 
34
40
  def runtime_context
35
41
  @runtime_context ||= {
36
- "name" => RbConfig::CONFIG["ruby_install_name"],
37
- "version" => Raven.sys_command("ruby -v")
42
+ name: RbConfig::CONFIG["ruby_install_name"],
43
+ version: RUBY_DESCRIPTION || Raven.sys_command("ruby -v")
38
44
  }
39
45
  end
40
46
  end
@@ -0,0 +1,57 @@
1
+ require 'raven/core_ext/object/duplicable'
2
+
3
+ #########################################
4
+ # This file was copied from Rails 5.2 #
5
+ #########################################
6
+
7
+ class Object
8
+ # Returns a deep copy of object if it's duplicable. If it's
9
+ # not duplicable, returns +self+.
10
+ #
11
+ # object = Object.new
12
+ # dup = object.deep_dup
13
+ # dup.instance_variable_set(:@a, 1)
14
+ #
15
+ # object.instance_variable_defined?(:@a) # => false
16
+ # dup.instance_variable_defined?(:@a) # => true
17
+ def deep_dup
18
+ duplicable? ? dup : self
19
+ end
20
+ end
21
+
22
+ class Array
23
+ # Returns a deep copy of array.
24
+ #
25
+ # array = [1, [2, 3]]
26
+ # dup = array.deep_dup
27
+ # dup[1][2] = 4
28
+ #
29
+ # array[1][2] # => nil
30
+ # dup[1][2] # => 4
31
+ def deep_dup
32
+ map(&:deep_dup)
33
+ end
34
+ end
35
+
36
+ class Hash
37
+ # Returns a deep copy of hash.
38
+ #
39
+ # hash = { a: { b: 'b' } }
40
+ # dup = hash.deep_dup
41
+ # dup[:a][:c] = 'c'
42
+ #
43
+ # hash[:a][:c] # => nil
44
+ # dup[:a][:c] # => "c"
45
+ def deep_dup
46
+ hash = dup
47
+ each_pair do |key, value|
48
+ if key.frozen? && ::String === key
49
+ hash[key] = value.deep_dup
50
+ else
51
+ hash.delete(key)
52
+ hash[key.deep_dup] = value.deep_dup
53
+ end
54
+ end
55
+ hash
56
+ end
57
+ end