bugsnag 6.6.3 → 6.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|