bugsnag 6.21.0 → 6.24.1

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -0
  3. data/VERSION +1 -1
  4. data/lib/bugsnag/breadcrumb_type.rb +14 -0
  5. data/lib/bugsnag/breadcrumbs/breadcrumb.rb +34 -1
  6. data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +1 -0
  7. data/lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb +50 -0
  8. data/lib/bugsnag/cleaner.rb +31 -18
  9. data/lib/bugsnag/configuration.rb +240 -22
  10. data/lib/bugsnag/delivery/synchronous.rb +2 -2
  11. data/lib/bugsnag/delivery/thread_queue.rb +2 -2
  12. data/lib/bugsnag/endpoint_configuration.rb +11 -0
  13. data/lib/bugsnag/endpoint_validator.rb +80 -0
  14. data/lib/bugsnag/error.rb +25 -0
  15. data/lib/bugsnag/event.rb +7 -0
  16. data/lib/bugsnag/integrations/rack.rb +3 -3
  17. data/lib/bugsnag/integrations/rails/active_job.rb +102 -0
  18. data/lib/bugsnag/integrations/railtie.rb +9 -1
  19. data/lib/bugsnag/integrations/resque.rb +17 -3
  20. data/lib/bugsnag/middleware/active_job.rb +18 -0
  21. data/lib/bugsnag/middleware/delayed_job.rb +21 -2
  22. data/lib/bugsnag/middleware/exception_meta_data.rb +2 -0
  23. data/lib/bugsnag/middleware/rack_request.rb +84 -19
  24. data/lib/bugsnag/middleware/rails3_request.rb +2 -2
  25. data/lib/bugsnag/middleware/rake.rb +1 -1
  26. data/lib/bugsnag/middleware/session_data.rb +3 -1
  27. data/lib/bugsnag/middleware/sidekiq.rb +1 -1
  28. data/lib/bugsnag/report.rb +166 -6
  29. data/lib/bugsnag/session_tracker.rb +52 -12
  30. data/lib/bugsnag/stacktrace.rb +10 -1
  31. data/lib/bugsnag/tasks/bugsnag.rake +1 -1
  32. data/lib/bugsnag/utility/duplicator.rb +124 -0
  33. data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
  34. data/lib/bugsnag.rb +126 -4
  35. metadata +16 -6
@@ -44,9 +44,23 @@ module Bugsnag
44
44
  :attributes => FRAMEWORK_ATTRIBUTES
45
45
  }
46
46
 
47
- context = "#{payload['class']}@#{queue}"
48
- report.meta_data.merge!({:context => context, :payload => payload})
49
- report.context = context
47
+ metadata = payload
48
+ class_name = metadata['class']
49
+
50
+ # when using Active Job the payload "class" will always be the Resque
51
+ # "JobWrapper", so we need to unwrap the actual class name
52
+ if class_name == "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper"
53
+ unwrapped_class_name = metadata['args'][0]['job_class'] rescue nil
54
+
55
+ if unwrapped_class_name
56
+ class_name = unwrapped_class_name
57
+ metadata['wrapped'] ||= unwrapped_class_name
58
+ end
59
+ end
60
+
61
+ context = "#{class_name}@#{queue}"
62
+ report.meta_data.merge!({ context: context, payload: metadata })
63
+ report.automatic_context = context
50
64
  end
51
65
  end
52
66
  end
@@ -0,0 +1,18 @@
1
+ module Bugsnag::Middleware
2
+ class ActiveJob
3
+ def initialize(bugsnag)
4
+ @bugsnag = bugsnag
5
+ end
6
+
7
+ def call(report)
8
+ data = report.request_data[:active_job]
9
+
10
+ if data
11
+ report.add_tab(:active_job, data)
12
+ report.automatic_context = "#{data[:job_name]}@#{data[:queue]}"
13
+ end
14
+
15
+ @bugsnag.call(report)
16
+ end
17
+ end
18
+ end
@@ -2,6 +2,11 @@ module Bugsnag::Middleware
2
2
  ##
3
3
  # Attaches delayed_job information to an error report
4
4
  class DelayedJob
5
+ # Active Job's queue adapter sets the "display_name" to this format. This
6
+ # breaks the event context as the ID and arguments are included, which will
7
+ # differ between executions of the same job
8
+ ACTIVE_JOB_DISPLAY_NAME = /^.* \[[0-9a-f-]+\] from DelayedJob\(.*\) with arguments: \[.*\]$/
9
+
5
10
  def initialize(bugsnag)
6
11
  @bugsnag = bugsnag
7
12
  end
@@ -23,8 +28,10 @@ module Bugsnag::Middleware
23
28
  if job.respond_to?(:payload_object)
24
29
  job_data[:active_job] = job.payload_object.job_data if job.payload_object.respond_to?(:job_data)
25
30
  payload_data = construct_job_payload(job.payload_object)
26
- report.context = payload_data[:display_name] if payload_data.include?(:display_name)
27
- report.context ||= payload_data[:class] if payload_data.include?(:class)
31
+
32
+ context = get_context(payload_data, job_data[:active_job])
33
+ report.automatic_context = context unless context.nil?
34
+
28
35
  job_data[:payload] = payload_data
29
36
  end
30
37
 
@@ -70,5 +77,17 @@ module Bugsnag::Middleware
70
77
  end
71
78
  data
72
79
  end
80
+
81
+ private
82
+
83
+ def get_context(payload_data, active_job_data)
84
+ if payload_data.include?(:display_name) && !ACTIVE_JOB_DISPLAY_NAME.match?(payload_data[:display_name])
85
+ payload_data[:display_name]
86
+ elsif active_job_data && active_job_data['job_class'] && active_job_data['queue_name']
87
+ "#{active_job_data['job_class']}@#{active_job_data['queue_name']}"
88
+ elsif payload_data.include?(:class)
89
+ payload_data[:class]
90
+ end
91
+ end
73
92
  end
74
93
  end
@@ -16,6 +16,8 @@ module Bugsnag::Middleware
16
16
 
17
17
  if exception.respond_to?(:bugsnag_context)
18
18
  context = exception.bugsnag_context
19
+ # note: this should set 'context' not 'automatic_context' as it's a
20
+ # user-supplied value
19
21
  report.context = context if context.is_a?(String)
20
22
  end
21
23
 
@@ -1,8 +1,11 @@
1
+ require "json"
2
+
1
3
  module Bugsnag::Middleware
2
4
  ##
3
5
  # Extracts and attaches rack data to an error report
4
6
  class RackRequest
5
7
  SPOOF = "[SPOOF]".freeze
8
+ COOKIE_HEADER = "Cookie".freeze
6
9
 
7
10
  def initialize(bugsnag)
8
11
  @bugsnag = bugsnag
@@ -18,8 +21,8 @@ module Bugsnag::Middleware
18
21
  client_ip = request.ip.to_s rescue SPOOF
19
22
  session = env["rack.session"]
20
23
 
21
- # Set the context
22
- report.context = "#{request.request_method} #{request.path}"
24
+ # Set the automatic context
25
+ report.automatic_context = "#{request.request_method} #{request.path}"
23
26
 
24
27
  # Set a sensible default for user_id
25
28
  report.user["id"] = request.ip
@@ -42,22 +45,6 @@ module Bugsnag::Middleware
42
45
  Bugsnag.configuration.warn "RackRequest - Rescued error while cleaning request.referer: #{stde}"
43
46
  end
44
47
 
45
- headers = {}
46
-
47
- env.each_pair do |key, value|
48
- if key.to_s.start_with?("HTTP_")
49
- header_key = key[5..-1]
50
- elsif ["CONTENT_TYPE", "CONTENT_LENGTH"].include?(key)
51
- header_key = key
52
- else
53
- next
54
- end
55
-
56
- headers[header_key.split("_").map {|s| s.capitalize}.join("-")] = value
57
- end
58
-
59
- headers["Referer"] = referer if headers["Referer"]
60
-
61
48
  # Add a request tab
62
49
  report.add_tab(:request, {
63
50
  :url => url,
@@ -65,9 +52,17 @@ module Bugsnag::Middleware
65
52
  :params => params.to_hash,
66
53
  :referer => referer,
67
54
  :clientIp => client_ip,
68
- :headers => headers
55
+ :headers => format_headers(env, referer)
69
56
  })
70
57
 
58
+ # add the HTTP version if present
59
+ if env["SERVER_PROTOCOL"]
60
+ report.add_metadata(:request, :httpVersion, env["SERVER_PROTOCOL"])
61
+ end
62
+
63
+ add_request_body(report, request, env)
64
+ add_cookies(report, request)
65
+
71
66
  # Add an environment tab
72
67
  if report.configuration.send_environment
73
68
  report.add_tab(:environment, env)
@@ -87,5 +82,75 @@ module Bugsnag::Middleware
87
82
 
88
83
  @bugsnag.call(report)
89
84
  end
85
+
86
+ private
87
+
88
+ def format_headers(env, referer)
89
+ headers = {}
90
+
91
+ env.each_pair do |key, value|
92
+ if key.to_s.start_with?("HTTP_")
93
+ header_key = key[5..-1]
94
+ elsif ["CONTENT_TYPE", "CONTENT_LENGTH"].include?(key)
95
+ header_key = key
96
+ else
97
+ next
98
+ end
99
+
100
+ headers[header_key.split("_").map {|s| s.capitalize}.join("-")] = value
101
+ end
102
+
103
+ headers["Referer"] = referer if headers["Referer"]
104
+
105
+ headers
106
+ end
107
+
108
+ def add_request_body(report, request, env)
109
+ body = parsed_request_body(request, env)
110
+
111
+ # this request may not have a body
112
+ return unless body.is_a?(Hash) && !body.empty?
113
+
114
+ report.add_metadata(:request, :body, body)
115
+ end
116
+
117
+ def parsed_request_body(request, env)
118
+ return request.POST rescue nil if request.form_data?
119
+
120
+ content_type = env["CONTENT_TYPE"]
121
+
122
+ return nil if content_type.nil?
123
+
124
+ if content_type.include?('/json') || content_type.include?('+json')
125
+ begin
126
+ body = request.body
127
+
128
+ return JSON.parse(body.read)
129
+ rescue StandardError
130
+ return nil
131
+ ensure
132
+ # the body must be rewound so other things can read it after we do
133
+ body.rewind
134
+ end
135
+ end
136
+
137
+ nil
138
+ end
139
+
140
+ def add_cookies(report, request)
141
+ return unless record_cookies?
142
+
143
+ cookies = request.cookies rescue nil
144
+
145
+ return unless cookies.is_a?(Hash) && !cookies.empty?
146
+
147
+ report.add_metadata(:request, :cookies, cookies)
148
+ end
149
+
150
+ def record_cookies?
151
+ # only record cookies in the request if none of the filters match "Cookie"
152
+ # the "Cookie" header will be filtered as normal
153
+ !Bugsnag.cleaner.filters_match?(COOKIE_HEADER)
154
+ end
90
155
  end
91
156
  end
@@ -15,8 +15,8 @@ module Bugsnag::Middleware
15
15
  client_ip = env["action_dispatch.remote_ip"].to_s rescue SPOOF
16
16
 
17
17
  if params
18
- # Set the context
19
- report.context = "#{params[:controller]}##{params[:action]}"
18
+ # Set the automatic context
19
+ report.automatic_context = "#{params[:controller]}##{params[:action]}"
20
20
 
21
21
  # Augment the request tab
22
22
  report.add_tab(:request, {
@@ -16,7 +16,7 @@ module Bugsnag::Middleware
16
16
  :arguments => task.arg_description
17
17
  })
18
18
 
19
- report.context ||= task.name
19
+ report.automatic_context ||= task.name
20
20
  end
21
21
 
22
22
  @bugsnag.call(report)
@@ -8,12 +8,14 @@ module Bugsnag::Middleware
8
8
 
9
9
  def call(report)
10
10
  session = Bugsnag::SessionTracker.get_current_session
11
- unless session.nil?
11
+
12
+ if session && !session[:paused?]
12
13
  if report.unhandled
13
14
  session[:events][:unhandled] += 1
14
15
  else
15
16
  session[:events][:handled] += 1
16
17
  end
18
+
17
19
  report.session = session
18
20
  end
19
21
 
@@ -10,7 +10,7 @@ module Bugsnag::Middleware
10
10
  sidekiq = report.request_data[:sidekiq]
11
11
  if sidekiq
12
12
  report.add_tab(:sidekiq, sidekiq)
13
- report.context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
13
+ report.automatic_context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
14
14
  end
15
15
  @bugsnag.call(report)
16
16
  end
@@ -1,8 +1,10 @@
1
1
  require "json"
2
2
  require "pathname"
3
+ require "bugsnag/error"
3
4
  require "bugsnag/stacktrace"
4
5
 
5
6
  module Bugsnag
7
+ # rubocop:todo Metrics/ClassLength
6
8
  class Report
7
9
  NOTIFIER_NAME = "Ruby Bugsnag Notifier"
8
10
  NOTIFIER_VERSION = Bugsnag::VERSION
@@ -45,16 +47,13 @@ module Bugsnag
45
47
  # @return [Configuration]
46
48
  attr_accessor :configuration
47
49
 
48
- # Additional context for this report
49
- # @return [String, nil]
50
- attr_accessor :context
51
-
52
50
  # The delivery method that will be used for this report
53
51
  # @see Configuration#delivery_method
54
52
  # @return [Symbol]
55
53
  attr_accessor :delivery_method
56
54
 
57
55
  # The list of exceptions in this report
56
+ # @deprecated Use {#errors} instead
58
57
  # @return [Array<Hash>]
59
58
  attr_accessor :exceptions
60
59
 
@@ -72,10 +71,12 @@ module Bugsnag
72
71
  attr_accessor :grouping_hash
73
72
 
74
73
  # Arbitrary metadata attached to this report
74
+ # @deprecated Use {#metadata} instead
75
75
  # @return [Hash]
76
76
  attr_accessor :meta_data
77
77
 
78
78
  # The raw Exception instances for this report
79
+ # @deprecated Use {#original_error} instead
79
80
  # @see #exceptions
80
81
  # @return [Array<Exception>]
81
82
  attr_accessor :raw_exceptions
@@ -102,31 +103,67 @@ module Bugsnag
102
103
  # @return [Hash]
103
104
  attr_accessor :user
104
105
 
106
+ # A list of errors in this report
107
+ # @return [Array<Error>]
108
+ attr_reader :errors
109
+
110
+ # The Exception instance this report was created for
111
+ # @return [Exception]
112
+ attr_reader :original_error
113
+
105
114
  ##
106
115
  # Initializes a new report from an exception.
107
116
  def initialize(exception, passed_configuration, auto_notify=false)
117
+ # store the creation time for use as device.time
118
+ @created_at = Time.now.utc.iso8601(3)
119
+
108
120
  @should_ignore = false
109
121
  @unhandled = auto_notify
122
+ @initial_unhandled = @unhandled
110
123
 
111
124
  self.configuration = passed_configuration
112
125
 
126
+ @original_error = exception
113
127
  self.raw_exceptions = generate_raw_exceptions(exception)
114
128
  self.exceptions = generate_exception_list
129
+ @errors = generate_error_list
115
130
 
116
131
  self.api_key = configuration.api_key
117
132
  self.app_type = configuration.app_type
118
133
  self.app_version = configuration.app_version
119
134
  self.breadcrumbs = []
135
+ self.context = configuration.context if configuration.context_set?
120
136
  self.delivery_method = configuration.delivery_method
121
137
  self.hostname = configuration.hostname
122
138
  self.runtime_versions = configuration.runtime_versions.dup
123
- self.meta_data = {}
139
+ self.meta_data = Utility::Duplicator.duplicate(configuration.metadata)
124
140
  self.release_stage = configuration.release_stage
125
141
  self.severity = auto_notify ? "error" : "warning"
126
142
  self.severity_reason = auto_notify ? {:type => UNHANDLED_EXCEPTION} : {:type => HANDLED_EXCEPTION}
127
143
  self.user = {}
144
+
145
+ @metadata_delegate = Utility::MetadataDelegate.new
146
+ end
147
+
148
+ ##
149
+ # Additional context for this report
150
+ # @!attribute context
151
+ # @return [String, nil]
152
+ def context
153
+ return @context if defined?(@context)
154
+
155
+ @automatic_context
128
156
  end
129
157
 
158
+ attr_writer :context
159
+
160
+ ##
161
+ # Context set automatically by Bugsnag uses this attribute, which prevents
162
+ # it from overwriting the user-supplied context
163
+ # @api private
164
+ # @return [String, nil]
165
+ attr_accessor :automatic_context
166
+
130
167
  ##
131
168
  # Add a new metadata tab to this notification.
132
169
  #
@@ -135,6 +172,8 @@ module Bugsnag
135
172
  # exists, this will be merged with the existing values. If a Hash is not
136
173
  # given, the value will be placed into the 'custom' tab
137
174
  # @return [void]
175
+ #
176
+ # @deprecated Use {#add_metadata} instead
138
177
  def add_tab(name, value)
139
178
  return if name.nil?
140
179
 
@@ -153,6 +192,8 @@ module Bugsnag
153
192
  #
154
193
  # @param name [String]
155
194
  # @return [void]
195
+ #
196
+ # @deprecated Use {#clear_metadata} instead
156
197
  def remove_tab(name)
157
198
  return if name.nil?
158
199
 
@@ -175,7 +216,8 @@ module Bugsnag
175
216
  context: context,
176
217
  device: {
177
218
  hostname: hostname,
178
- runtimeVersions: runtime_versions
219
+ runtimeVersions: runtime_versions,
220
+ time: @created_at
179
221
  },
180
222
  exceptions: exceptions,
181
223
  groupingHash: grouping_hash,
@@ -257,8 +299,119 @@ module Bugsnag
257
299
  end
258
300
  end
259
301
 
302
+ # A Hash containing arbitrary metadata
303
+ # @!attribute metadata
304
+ # @return [Hash]
305
+ def metadata
306
+ @meta_data
307
+ end
308
+
309
+ # @param metadata [Hash]
310
+ # @return [void]
311
+ def metadata=(metadata)
312
+ @meta_data = metadata
313
+ end
314
+
315
+ ##
316
+ # Data from the current HTTP request. May be nil if no data has been recorded
317
+ #
318
+ # @return [Hash, nil]
319
+ def request
320
+ @meta_data[:request]
321
+ end
322
+
323
+ ##
324
+ # Add values to metadata
325
+ #
326
+ # @overload add_metadata(section, data)
327
+ # Merges data into the given section of metadata
328
+ # @param section [String, Symbol]
329
+ # @param data [Hash]
330
+ #
331
+ # @overload add_metadata(section, key, value)
332
+ # Sets key to value in the given section of metadata. If the value is nil
333
+ # the key will be deleted
334
+ # @param section [String, Symbol]
335
+ # @param key [String, Symbol]
336
+ # @param value
337
+ #
338
+ # @return [void]
339
+ def add_metadata(section, key_or_data, *args)
340
+ @metadata_delegate.add_metadata(@meta_data, section, key_or_data, *args)
341
+ end
342
+
343
+ ##
344
+ # Clear values from metadata
345
+ #
346
+ # @overload clear_metadata(section)
347
+ # Clears the given section of metadata
348
+ # @param section [String, Symbol]
349
+ #
350
+ # @overload clear_metadata(section, key)
351
+ # Clears the key in the given section of metadata
352
+ # @param section [String, Symbol]
353
+ # @param key [String, Symbol]
354
+ #
355
+ # @return [void]
356
+ def clear_metadata(section, *args)
357
+ @metadata_delegate.clear_metadata(@meta_data, section, *args)
358
+ end
359
+
360
+ ##
361
+ # Set information about the current user
362
+ #
363
+ # Additional user fields can be added as metadata in a "user" section
364
+ #
365
+ # Setting a field to 'nil' will remove it from the user data
366
+ #
367
+ # @param id [String, nil]
368
+ # @param email [String, nil]
369
+ # @param name [String, nil]
370
+ # @return [void]
371
+ def set_user(id = nil, email = nil, name = nil)
372
+ new_user = { id: id, email: email, name: name }
373
+ new_user.reject! { |key, value| value.nil? }
374
+
375
+ @user = new_user
376
+ end
377
+
378
+ def unhandled=(new_unhandled)
379
+ # fix the handled/unhandled counts in the current session
380
+ update_handled_counts(new_unhandled, @unhandled)
381
+
382
+ @unhandled = new_unhandled
383
+ end
384
+
385
+ ##
386
+ # Returns true if the unhandled flag has been changed from its initial value
387
+ #
388
+ # @api private
389
+ # @return [Boolean]
390
+ def unhandled_overridden?
391
+ @unhandled != @initial_unhandled
392
+ end
393
+
260
394
  private
261
395
 
396
+ def update_handled_counts(is_unhandled, was_unhandled)
397
+ # do nothing if there is no session to update
398
+ return if @session.nil?
399
+
400
+ # increment the counts for the current unhandled value
401
+ if is_unhandled
402
+ @session[:events][:unhandled] += 1
403
+ else
404
+ @session[:events][:handled] += 1
405
+ end
406
+
407
+ # decrement the counts for the previous unhandled value
408
+ if was_unhandled
409
+ @session[:events][:unhandled] -= 1
410
+ else
411
+ @session[:events][:handled] -= 1
412
+ end
413
+ end
414
+
262
415
  def generate_exception_list
263
416
  raw_exceptions.map do |exception|
264
417
  {
@@ -269,6 +422,12 @@ module Bugsnag
269
422
  end
270
423
  end
271
424
 
425
+ def generate_error_list
426
+ exceptions.map do |exception|
427
+ Error.new(exception[:errorClass], exception[:message], exception[:stacktrace])
428
+ end
429
+ end
430
+
272
431
  def error_class(exception)
273
432
  # The "Class" check is for some strange exceptions like Timeout::Error
274
433
  # which throw the error class instead of an instance
@@ -311,4 +470,5 @@ module Bugsnag
311
470
  exceptions
312
471
  end
313
472
  end
473
+ # rubocop:enable Metrics/ClassLength
314
474
  end
@@ -34,16 +34,20 @@ module Bugsnag
34
34
  # Starts a new session, storing it on the current thread.
35
35
  #
36
36
  # This allows Bugsnag to track error rates for a release.
37
+ #
38
+ # @return [void]
37
39
  def start_session
38
- return unless Bugsnag.configuration.enable_sessions
40
+ return unless Bugsnag.configuration.enable_sessions && Bugsnag.configuration.should_notify_release_stage?
41
+
39
42
  start_delivery_thread
40
43
  start_time = Time.now().utc().strftime('%Y-%m-%dT%H:%M:00')
41
44
  new_session = {
42
- :id => SecureRandom.uuid,
43
- :startedAt => start_time,
44
- :events => {
45
- :handled => 0,
46
- :unhandled => 0
45
+ id: SecureRandom.uuid,
46
+ startedAt: start_time,
47
+ paused?: false,
48
+ events: {
49
+ handled: 0,
50
+ unhandled: 0
47
51
  }
48
52
  }
49
53
  SessionTracker.set_current_session(new_session)
@@ -52,6 +56,47 @@ module Bugsnag
52
56
 
53
57
  alias_method :create_session, :start_session
54
58
 
59
+ ##
60
+ # Stop any events being attributed to the current session until it is
61
+ # resumed or a new session is started
62
+ #
63
+ # @see resume_session
64
+ #
65
+ # @return [void]
66
+ def pause_session
67
+ current_session = SessionTracker.get_current_session
68
+
69
+ return unless current_session
70
+
71
+ current_session[:paused?] = true
72
+ end
73
+
74
+ ##
75
+ # Resume the current session if it was previously paused. If there is no
76
+ # current session, a new session will be started
77
+ #
78
+ # @see pause_session
79
+ #
80
+ # @return [Boolean] true if a paused session was resumed
81
+ def resume_session
82
+ current_session = SessionTracker.get_current_session
83
+
84
+ if current_session
85
+ # if the session is paused then resume it, otherwise we don't need to
86
+ # do anything
87
+ if current_session[:paused?]
88
+ current_session[:paused?] = false
89
+
90
+ return true
91
+ end
92
+ else
93
+ # if there's no current session, start a new one
94
+ start_session
95
+ end
96
+
97
+ false
98
+ end
99
+
55
100
  ##
56
101
  # Delivers the current session_counts lists to the session endpoint.
57
102
  def send_sessions
@@ -83,7 +128,7 @@ module Bugsnag
83
128
  end
84
129
  end
85
130
  end
86
- @delivery_thread = Concurrent::TimerTask.execute(execution_interval: 30) do
131
+ @delivery_thread = Concurrent::TimerTask.execute(execution_interval: 10) do
87
132
  if @session_counts.size > 0
88
133
  send_sessions
89
134
  end
@@ -106,11 +151,6 @@ module Bugsnag
106
151
  return
107
152
  end
108
153
 
109
- if !Bugsnag.configuration.should_notify_release_stage?
110
- Bugsnag.configuration.debug("Not delivering sessions due to notify_release_stages :#{Bugsnag.configuration.notify_release_stages.inspect}")
111
- return
112
- end
113
-
114
154
  body = {
115
155
  :notifier => {
116
156
  :name => Bugsnag::Report::NOTIFIER_NAME,
@@ -43,7 +43,7 @@ module Bugsnag
43
43
  if defined?(configuration.project_root) && configuration.project_root.to_s != ''
44
44
  trace_hash[:inProject] = true if file.start_with?(configuration.project_root.to_s)
45
45
  file.sub!(/#{configuration.project_root}\//, "")
46
- trace_hash.delete(:inProject) if file.match(configuration.vendor_path)
46
+ trace_hash.delete(:inProject) if vendor_path?(configuration, file)
47
47
  end
48
48
 
49
49
  # Strip common gem path prefixes
@@ -67,5 +67,14 @@ module Bugsnag
67
67
 
68
68
  processed_backtrace
69
69
  end
70
+
71
+ # @api private
72
+ def self.vendor_path?(configuration, file_path)
73
+ return true if configuration.vendor_path && file_path.match(configuration.vendor_path)
74
+
75
+ configuration.vendor_paths.any? do |vendor_path|
76
+ file_path.start_with?("#{vendor_path.sub(/\/$/, '')}/")
77
+ end
78
+ end
70
79
  end
71
80
  end
@@ -7,7 +7,7 @@ namespace :bugsnag do
7
7
  raise RuntimeError.new("Bugsnag test exception")
8
8
  rescue => e
9
9
  Bugsnag.notify(e) do |report|
10
- report.context = "rake#test_exception"
10
+ report.automatic_context = "rake#test_exception"
11
11
  end
12
12
  end
13
13
  end