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.
- checksums.yaml +4 -4
- data/.rdoc_options +27 -0
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +672 -0
- data/.travis.yml +46 -11
- data/CHANGELOG.md +12 -0
- data/Gemfile +8 -0
- data/Rakefile +5 -4
- data/VERSION +1 -1
- data/lib/bugsnag.rb +19 -4
- data/lib/bugsnag/configuration.rb +20 -2
- data/lib/bugsnag/delivery.rb +4 -0
- data/lib/bugsnag/delivery/synchronous.rb +2 -0
- data/lib/bugsnag/delivery/thread_queue.rb +2 -0
- data/lib/bugsnag/helpers.rb +10 -0
- data/lib/bugsnag/integrations/mailman.rb +4 -0
- data/lib/bugsnag/integrations/rack.rb +4 -0
- data/lib/bugsnag/integrations/rake.rb +2 -0
- data/lib/bugsnag/integrations/resque.rb +6 -0
- data/lib/bugsnag/integrations/shoryuken.rb +2 -0
- data/lib/bugsnag/integrations/sidekiq.rb +2 -0
- data/lib/bugsnag/middleware/callbacks.rb +2 -0
- data/lib/bugsnag/middleware/classify_error.rb +2 -0
- data/lib/bugsnag/middleware/clearance_user.rb +2 -0
- data/lib/bugsnag/middleware/exception_meta_data.rb +14 -14
- data/lib/bugsnag/middleware/ignore_error_class.rb +3 -0
- data/lib/bugsnag/middleware/mailman.rb +2 -0
- data/lib/bugsnag/middleware/rack_request.rb +2 -0
- data/lib/bugsnag/middleware/rails3_request.rb +2 -0
- data/lib/bugsnag/middleware/rake.rb +2 -0
- data/lib/bugsnag/middleware/session_data.rb +2 -0
- data/lib/bugsnag/middleware/sidekiq.rb +2 -0
- data/lib/bugsnag/middleware/suggestion_data.rb +2 -0
- data/lib/bugsnag/middleware/warden_user.rb +2 -0
- data/lib/bugsnag/middleware_stack.rb +20 -2
- data/lib/bugsnag/report.rb +16 -3
- data/lib/bugsnag/session_tracker.rb +13 -1
- data/lib/bugsnag/stacktrace.rb +6 -2
- data/spec/middleware/exception_meta_data_spec.rb +104 -0
- data/spec/report_spec.rb +9 -1
- metadata +7 -3
@@ -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.
|
11
|
-
|
12
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
16
|
+
if exception.respond_to?(:bugsnag_context) && exception.bugsnag_context.is_a?(String)
|
17
|
+
report.context = exception.bugsnag_context
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
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,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
|
-
|
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
|
-
|
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
|
data/lib/bugsnag/report.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/bugsnag/stacktrace.rb
CHANGED
@@ -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 =
|
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.
|
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
|
data/spec/report_spec.rb
CHANGED
@@ -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
|
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
|
|