bugsnag 6.6.3 → 6.6.4

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +27 -0
  3. data/.rubocop.yml +2 -0
  4. data/.rubocop_todo.yml +672 -0
  5. data/.travis.yml +46 -11
  6. data/CHANGELOG.md +12 -0
  7. data/Gemfile +8 -0
  8. data/Rakefile +5 -4
  9. data/VERSION +1 -1
  10. data/lib/bugsnag.rb +19 -4
  11. data/lib/bugsnag/configuration.rb +20 -2
  12. data/lib/bugsnag/delivery.rb +4 -0
  13. data/lib/bugsnag/delivery/synchronous.rb +2 -0
  14. data/lib/bugsnag/delivery/thread_queue.rb +2 -0
  15. data/lib/bugsnag/helpers.rb +10 -0
  16. data/lib/bugsnag/integrations/mailman.rb +4 -0
  17. data/lib/bugsnag/integrations/rack.rb +4 -0
  18. data/lib/bugsnag/integrations/rake.rb +2 -0
  19. data/lib/bugsnag/integrations/resque.rb +6 -0
  20. data/lib/bugsnag/integrations/shoryuken.rb +2 -0
  21. data/lib/bugsnag/integrations/sidekiq.rb +2 -0
  22. data/lib/bugsnag/middleware/callbacks.rb +2 -0
  23. data/lib/bugsnag/middleware/classify_error.rb +2 -0
  24. data/lib/bugsnag/middleware/clearance_user.rb +2 -0
  25. data/lib/bugsnag/middleware/exception_meta_data.rb +14 -14
  26. data/lib/bugsnag/middleware/ignore_error_class.rb +3 -0
  27. data/lib/bugsnag/middleware/mailman.rb +2 -0
  28. data/lib/bugsnag/middleware/rack_request.rb +2 -0
  29. data/lib/bugsnag/middleware/rails3_request.rb +2 -0
  30. data/lib/bugsnag/middleware/rake.rb +2 -0
  31. data/lib/bugsnag/middleware/session_data.rb +2 -0
  32. data/lib/bugsnag/middleware/sidekiq.rb +2 -0
  33. data/lib/bugsnag/middleware/suggestion_data.rb +2 -0
  34. data/lib/bugsnag/middleware/warden_user.rb +2 -0
  35. data/lib/bugsnag/middleware_stack.rb +20 -2
  36. data/lib/bugsnag/report.rb +16 -3
  37. data/lib/bugsnag/session_tracker.rb +13 -1
  38. data/lib/bugsnag/stacktrace.rb +6 -2
  39. data/spec/middleware/exception_meta_data_spec.rb +104 -0
  40. data/spec/report_spec.rb +9 -1
  41. metadata +7 -3
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and appends clearance user information
2
4
  class ClearanceUser
3
5
  COMMON_USER_FIELDS = [:email, :name, :first_name, :last_name, :created_at, :id]
4
6
 
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts data from the exception.
2
4
  class ExceptionMetaData
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
@@ -7,23 +9,21 @@ module Bugsnag::Middleware
7
9
  def call(report)
8
10
  # Apply the user's information attached to the exceptions
9
11
  report.raw_exceptions.each do |exception|
10
- if exception.class.include?(Bugsnag::MetaData)
11
- if exception.bugsnag_user_id.is_a?(String)
12
- report.user = {id: exception.bugsnag_user_id}
13
- end
12
+ if exception.respond_to?(:bugsnag_user_id) && exception.bugsnag_user_id.is_a?(String)
13
+ report.user = {id: exception.bugsnag_user_id}
14
+ end
14
15
 
15
- if exception.bugsnag_context.is_a?(String)
16
- report.context = exception.bugsnag_context
17
- end
16
+ if exception.respond_to?(:bugsnag_context) && exception.bugsnag_context.is_a?(String)
17
+ report.context = exception.bugsnag_context
18
+ end
18
19
 
19
- if exception.bugsnag_grouping_hash.is_a?(String)
20
- report.grouping_hash = exception.bugsnag_grouping_hash
21
- end
20
+ if exception.respond_to?(:bugsnag_grouping_hash) && exception.bugsnag_grouping_hash.is_a?(String)
21
+ report.grouping_hash = exception.bugsnag_grouping_hash
22
+ end
22
23
 
23
- if exception.respond_to?(:bugsnag_meta_data) && exception.bugsnag_meta_data
24
- exception.bugsnag_meta_data.each do |key, value|
25
- report.add_tab key, value
26
- end
24
+ if exception.respond_to?(:bugsnag_meta_data) && exception.bugsnag_meta_data.is_a?(Hash)
25
+ exception.bugsnag_meta_data.each do |key, value|
26
+ report.add_tab key, value
27
27
  end
28
28
  end
29
29
  end
@@ -1,4 +1,7 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Determines if the exception should be ignored based on the configured
4
+ # `ignore_classes`
2
5
  class IgnoreErrorClass
3
6
  def initialize(bugsnag)
4
7
  @bugsnag = bugsnag
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and attaches mailman data to an error report
2
4
  class Mailman
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and attaches rack data to an error report
2
4
  class RackRequest
3
5
  SPOOF = "[SPOOF]".freeze
4
6
 
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and attaches rails and rack environment data to an error report
2
4
  class Rails3Request
3
5
  SPOOF = "[SPOOF]".freeze
4
6
 
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and attaches rake task information to an error report
2
4
  class Rake
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Attaches information about current session to an error report
2
4
  class SessionData
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Attaches Sidekiq job information to an error report
2
4
  class Sidekiq
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Attaches any "Did you mean?" suggestions to the report
2
4
  class SuggestionData
3
5
 
4
6
  CAPTURE_REGEX = /Did you mean\?([\s\S]+)$/
@@ -1,4 +1,6 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and attaches user information from Warden to an error report
2
4
  class WardenUser
3
5
  SCOPE_PATTERN = /^warden\.user\.([^.]+)\.key$/
4
6
  COMMON_USER_FIELDS = [:email, :name, :first_name, :last_name, :created_at, :id]
@@ -1,11 +1,17 @@
1
1
  module Bugsnag
2
2
  class MiddlewareStack
3
+ ##
4
+ # Creates the middleware stack.
3
5
  def initialize
4
6
  @middlewares = []
5
7
  @disabled_middleware = []
6
8
  @mutex = Mutex.new
7
9
  end
8
10
 
11
+ ##
12
+ # Defines a new middleware to use in the middleware call sequence.
13
+ #
14
+ # Will return early if given middleware is disabled or already included.
9
15
  def use(new_middleware)
10
16
  @mutex.synchronize do
11
17
  return if @disabled_middleware.include?(new_middleware)
@@ -15,6 +21,11 @@ module Bugsnag
15
21
  end
16
22
  end
17
23
 
24
+ ##
25
+ # Inserts a new middleware to use after a given middleware already added.
26
+ #
27
+ # Will return early if given middleware is disabled or already added.
28
+ # New middleware will be inserted last if the existing middleware is not already included.
18
29
  def insert_after(after, new_middleware)
19
30
  @mutex.synchronize do
20
31
  return if @disabled_middleware.include?(new_middleware)
@@ -34,6 +45,11 @@ module Bugsnag
34
45
  end
35
46
  end
36
47
 
48
+ ##
49
+ # Inserts a new middleware to use before a given middleware already added.
50
+ #
51
+ # Will return early if given middleware is disabled or already added.
52
+ # New middleware will be inserted last if the existing middleware is not already included.
37
53
  def insert_before(before, new_middleware)
38
54
  @mutex.synchronize do
39
55
  return if @disabled_middleware.include?(new_middleware)
@@ -57,12 +73,14 @@ module Bugsnag
57
73
  end
58
74
  end
59
75
 
60
- # This allows people to proxy methods to the array if they want to do more complex stuff
76
+ ##
77
+ # Allows the user to proxy methods for more complex functionality.
61
78
  def method_missing(method, *args, &block)
62
79
  @middlewares.send(method, *args, &block)
63
80
  end
64
81
 
65
- # Runs the middleware stack and calls
82
+ ##
83
+ # Runs the middleware stack.
66
84
  def run(report)
67
85
  # The final lambda is the termination of the middleware stack. It calls deliver on the notification
68
86
  lambda_has_run = false
@@ -37,6 +37,8 @@ module Bugsnag
37
37
  attr_accessor :severity_reason
38
38
  attr_accessor :user
39
39
 
40
+ ##
41
+ # Initializes a new report from an exception.
40
42
  def initialize(exception, passed_configuration, auto_notify=false)
41
43
  @should_ignore = false
42
44
  @unhandled = auto_notify
@@ -58,7 +60,8 @@ module Bugsnag
58
60
  self.user = {}
59
61
  end
60
62
 
61
- # Add a new tab to this notification
63
+ ##
64
+ # Add a new metadata tab to this notification.
62
65
  def add_tab(name, value)
63
66
  return if name.nil?
64
67
 
@@ -72,14 +75,16 @@ module Bugsnag
72
75
  end
73
76
  end
74
77
 
75
- # Remove a tab from this notification
78
+ ##
79
+ # Removes a metadata tab from this notification.
76
80
  def remove_tab(name)
77
81
  return if name.nil?
78
82
 
79
83
  meta_data.delete(name)
80
84
  end
81
85
 
82
- # Build an exception payload
86
+ ##
87
+ # Builds and returns the exception payload for this notification.
83
88
  def as_json
84
89
  # Build the payload's exception event
85
90
  payload_event = {
@@ -120,6 +125,8 @@ module Bugsnag
120
125
  }
121
126
  end
122
127
 
128
+ ##
129
+ # Returns the headers required for the notification.
123
130
  def headers
124
131
  {
125
132
  "Bugsnag-Api-Key" => api_key,
@@ -128,14 +135,20 @@ module Bugsnag
128
135
  }
129
136
  end
130
137
 
138
+ ##
139
+ # Whether this report should be ignored and not sent.
131
140
  def ignore?
132
141
  @should_ignore
133
142
  end
134
143
 
144
+ ##
145
+ # Data set on the configuration to be attached to every error notification.
135
146
  def request_data
136
147
  configuration.request_data
137
148
  end
138
149
 
150
+ ##
151
+ # Tells the client this report should not be sent.
139
152
  def ignore!
140
153
  @should_ignore = true
141
154
  end
@@ -12,18 +12,28 @@ module Bugsnag
12
12
 
13
13
  attr_reader :session_counts
14
14
 
15
+ ##
16
+ # Sets the session information for this thread.
15
17
  def self.set_current_session(session)
16
18
  Thread.current[THREAD_SESSION] = session
17
19
  end
18
20
 
21
+ ##
22
+ # Returns the session information for this thread.
19
23
  def self.get_current_session
20
24
  Thread.current[THREAD_SESSION]
21
25
  end
22
26
 
27
+ ##
28
+ # Initializes the session tracker.
23
29
  def initialize
24
30
  @session_counts = Concurrent::Hash.new(0)
25
31
  end
26
32
 
33
+ ##
34
+ # Starts a new session, storing it on the current thread.
35
+ #
36
+ # This allows Bugsnag to track error rates for a release.
27
37
  def start_session
28
38
  start_delivery_thread
29
39
  start_time = Time.now().utc().strftime('%Y-%m-%dT%H:%M:00')
@@ -41,6 +51,8 @@ module Bugsnag
41
51
 
42
52
  alias_method :create_session, :start_session
43
53
 
54
+ ##
55
+ # Delivers the current session_counts lists to the session endpoint.
44
56
  def send_sessions
45
57
  sessions = []
46
58
  counts = @session_counts
@@ -54,6 +66,7 @@ module Bugsnag
54
66
  deliver(sessions)
55
67
  end
56
68
 
69
+ private
57
70
  def start_delivery_thread
58
71
  MUTEX.synchronize do
59
72
  @started = nil unless defined?(@started)
@@ -77,7 +90,6 @@ module Bugsnag
77
90
  end
78
91
  end
79
92
 
80
- private
81
93
  def add_session(min)
82
94
  @session_counts[min] += 1
83
95
  end
@@ -8,8 +8,10 @@ module Bugsnag
8
8
  JAVA_BACKTRACE_REGEX = /^(.*)\((.*)(?::([0-9]+))?\)$/
9
9
 
10
10
  # Path to vendored code. Used to mark file paths as out of project.
11
- VENDOR_PATH = 'vendor/'
11
+ VENDOR_PATH = /^(vendor\/|\.bundle\/)/
12
12
 
13
+ ##
14
+ # Process a backtrace and the configuration into a parsed stacktrace.
13
15
  def initialize(backtrace, configuration)
14
16
  @configuration = configuration
15
17
 
@@ -44,7 +46,7 @@ module Bugsnag
44
46
  if defined?(@configuration.project_root) && @configuration.project_root.to_s != ''
45
47
  trace_hash[:inProject] = true if file.start_with?(@configuration.project_root.to_s)
46
48
  file.sub!(/#{@configuration.project_root}\//, "")
47
- trace_hash.delete(:inProject) if file.start_with?(VENDOR_PATH)
49
+ trace_hash.delete(:inProject) if file.match(VENDOR_PATH)
48
50
  end
49
51
 
50
52
 
@@ -66,6 +68,8 @@ module Bugsnag
66
68
  end.compact
67
69
  end
68
70
 
71
+ ##
72
+ # Returns the processed backtrace
69
73
  def to_a
70
74
  @processed_backtrace
71
75
  end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Bugsnag::Middleware::ExceptionMetaData do
5
+ let(:report_class) do
6
+ Class.new do
7
+ attr_accessor :raw_exceptions, :tabs, :user, :context, :grouping_hash
8
+
9
+ def initialize(errors)
10
+ self.raw_exceptions = Array(errors)
11
+ end
12
+
13
+ def add_tab(key, value)
14
+ self.tabs ||= {}
15
+ tabs[key] = value
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:middleware) { Bugsnag::Middleware::ExceptionMetaData.new(lambda {|_|}) }
21
+ let(:bugsnag_error_class) { Class.new(StandardError) { include Bugsnag::MetaData } }
22
+
23
+ it "adds metadata when exception singleton class extended with Bugsnag::MetaData" do
24
+ error = RuntimeError.new
25
+ error.extend(Bugsnag::MetaData)
26
+ error.bugsnag_meta_data = {"foo" => "bar"}
27
+
28
+ report = report_class.new(error)
29
+
30
+ middleware.call(report)
31
+
32
+ expect(report.tabs).to eq({"foo" => "bar"})
33
+ end
34
+
35
+ it "adds metadata when exception class includes Bugsnag::MetaData" do
36
+ error = bugsnag_error_class.new
37
+ error.bugsnag_meta_data = {"foo" => "bar"}
38
+
39
+ report = report_class.new(error)
40
+
41
+ middleware.call(report)
42
+
43
+ expect(report.tabs).to eq({"foo" => "bar"})
44
+ end
45
+
46
+ it "does nothing when metadata not a Hash" do
47
+ error = bugsnag_error_class.new
48
+ error.bugsnag_meta_data = 4
49
+
50
+ report = report_class.new(error)
51
+
52
+ middleware.call(report)
53
+
54
+ expect(report.user).to eq(nil)
55
+ expect(report.tabs).to eq(nil)
56
+ expect(report.grouping_hash).to eq(nil)
57
+ expect(report.context).to eq(nil)
58
+ end
59
+
60
+ it "sets user ID when a string" do
61
+ error = bugsnag_error_class.new
62
+ error.bugsnag_user_id = "1234"
63
+
64
+ report = report_class.new(error)
65
+
66
+ middleware.call(report)
67
+
68
+ expect(report.user).to eq({id: "1234"})
69
+ end
70
+
71
+ it "sets context when a string" do
72
+ error = bugsnag_error_class.new
73
+ error.bugsnag_context = "Foo#bar"
74
+
75
+ report = report_class.new(error)
76
+
77
+ middleware.call(report)
78
+
79
+ expect(report.context).to eq("Foo#bar")
80
+ end
81
+
82
+ it "sets grouping_hash when a string" do
83
+ error = bugsnag_error_class.new
84
+ error.bugsnag_grouping_hash = "abcdef"
85
+
86
+ report = report_class.new(error)
87
+
88
+ middleware.call(report)
89
+
90
+ expect(report.grouping_hash).to eq("abcdef")
91
+ end
92
+
93
+ it "does nothing when no bugsnag attributes are set" do
94
+ error = bugsnag_error_class.new
95
+ report = report_class.new(error)
96
+
97
+ middleware.call(report)
98
+
99
+ expect(report.user).to eq(nil)
100
+ expect(report.tabs).to eq(nil)
101
+ expect(report.grouping_hash).to eq(nil)
102
+ expect(report.context).to eq(nil)
103
+ end
104
+ end
@@ -536,6 +536,10 @@ describe Bugsnag::Report do
536
536
  File.join(project_root, "lib/helpers/string.rb:32:in `splice'"),
537
537
  File.join(project_root, "lib/vendor/lib/article.rb:158:in `initialize'"),
538
538
  File.join(project_root, "lib/prog.rb:158:in `read_articles'"),
539
+ File.join(project_root, ".bundle/strutils/lib.string.rb:508:in `splice'"),
540
+ File.join(project_root, "abundle/article.rb:158:in `initialize'"),
541
+ File.join(project_root, ".bundles/strutils/lib.string.rb:508:in `splice'"),
542
+ File.join(project_root, "lib/.bundle/article.rb:158:in `initialize'"),
539
543
  "app.rb:10:in `main'",
540
544
  "(pry):3:in `__pry__'"
541
545
  ]}
@@ -549,7 +553,11 @@ describe Bugsnag::Report do
549
553
  expect(exception["stacktrace"][3]["inProject"]).to be true
550
554
  expect(exception["stacktrace"][4]["inProject"]).to be true
551
555
  expect(exception["stacktrace"][5]["inProject"]).to be_nil
552
- expect(exception["stacktrace"][6]["inProject"]).to be_nil
556
+ expect(exception["stacktrace"][6]["inProject"]).to be true
557
+ expect(exception["stacktrace"][7]["inProject"]).to be true
558
+ expect(exception["stacktrace"][8]["inProject"]).to be true
559
+ expect(exception["stacktrace"][9]["inProject"]).to be_nil
560
+ expect(exception["stacktrace"][10]["inProject"]).to be_nil
553
561
  }
554
562
  end
555
563