bugsnag 1.1.5 → 1.2.0.beta
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.
- data/Gemfile +1 -1
- data/Gemfile.lock +5 -5
- data/README.md +132 -22
- data/VERSION +1 -1
- data/bugsnag.gemspec +13 -8
- data/lib/bugsnag.rb +39 -18
- data/lib/bugsnag/configuration.rb +54 -43
- data/lib/bugsnag/helpers.rb +35 -21
- data/lib/bugsnag/middleware/callbacks.rb +19 -0
- data/lib/bugsnag/middleware/rack_request.rb +48 -0
- data/lib/bugsnag/middleware/rails2_request.rb +45 -0
- data/lib/bugsnag/middleware/rails3_request.rb +27 -0
- data/lib/bugsnag/middleware/warden_user.rb +47 -0
- data/lib/bugsnag/middleware_stack.rb +64 -0
- data/lib/bugsnag/notification.rb +146 -57
- data/lib/bugsnag/rack.rb +34 -49
- data/lib/bugsnag/rails.rb +12 -3
- data/lib/bugsnag/rails/action_controller_rescue.rb +33 -21
- data/lib/bugsnag/rails/controller_methods.rb +34 -50
- data/lib/bugsnag/railtie.rb +26 -14
- data/lib/bugsnag/tasks.rb +1 -1
- data/lib/{tasks → bugsnag/tasks}/bugsnag.rake +5 -0
- metadata +31 -17
- data/lib/bugsnag/delay/resque.rb +0 -21
data/lib/bugsnag/helpers.rb
CHANGED
@@ -1,34 +1,48 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class Parser
|
3
|
+
def json
|
4
|
+
Bugsnag::Helpers.load_json(body)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
1
9
|
module Bugsnag
|
2
10
|
module Helpers
|
3
11
|
MAX_STRING_LENGTH = 4096
|
4
12
|
|
5
|
-
def self.
|
6
|
-
return nil unless
|
7
|
-
hash.inject({}) do |h, (k, v)|
|
8
|
-
h[k.to_s.gsub(/\./, "-")] = v.to_s.slice(0, MAX_STRING_LENGTH)
|
9
|
-
h
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.apply_filters(hash, filters)
|
14
|
-
return nil unless hash
|
15
|
-
return hash unless filters
|
13
|
+
def self.cleanup_obj(obj, filters = nil)
|
14
|
+
return nil unless obj
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
if obj.is_a?(Hash)
|
17
|
+
clean_hash = {}
|
18
|
+
obj.each do |k,v|
|
19
|
+
if filters && filters.any? {|f| k.to_s.include?(f.to_s)}
|
20
|
+
clean_hash[k] = "[FILTERED]"
|
21
|
+
else
|
22
|
+
clean_obj = cleanup_obj(v, filters)
|
23
|
+
clean_hash[k] = clean_obj unless clean_obj.nil?
|
24
|
+
end
|
22
25
|
end
|
26
|
+
clean_hash
|
27
|
+
elsif obj.is_a?(Array) || obj.is_a?(Set)
|
28
|
+
obj.map { |el| cleanup_obj(el, filters) }.compact
|
29
|
+
elsif obj.is_a?(Integer) || obj.is_a?(Float)
|
30
|
+
obj
|
31
|
+
else
|
32
|
+
obj.to_s unless obj.to_s =~ /#<.*>/
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
26
|
-
def self.
|
27
|
-
|
28
|
-
|
36
|
+
def self.reduce_hash_size(hash)
|
37
|
+
hash.inject({}) do |h, (k,v)|
|
38
|
+
if v.is_a?(Hash)
|
39
|
+
h[k] = reduce_hash_size(v)
|
40
|
+
else
|
41
|
+
h[k] = v.to_s.slice(0, MAX_STRING_LENGTH)
|
42
|
+
end
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
h
|
45
|
+
end
|
32
46
|
end
|
33
47
|
|
34
48
|
# Helper functions to work around MultiJson changes in 1.3+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bugsnag::Middleware
|
2
|
+
class Callbacks
|
3
|
+
def initialize(bugsnag)
|
4
|
+
@bugsnag = bugsnag
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(notification)
|
8
|
+
if notification.request_data[:before_callbacks]
|
9
|
+
notification.request_data[:before_callbacks].each {|c| c.call(*[notification][0...c.arity]) }
|
10
|
+
end
|
11
|
+
|
12
|
+
@bugsnag.call(notification)
|
13
|
+
|
14
|
+
if notification.request_data[:after_callbacks]
|
15
|
+
notification.request_data[:after_callbacks].each {|c| c.call(*[notification][0...c.arity]) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Bugsnag::Middleware
|
2
|
+
class RackRequest
|
3
|
+
def initialize(bugsnag)
|
4
|
+
@bugsnag = bugsnag
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(notification)
|
8
|
+
if notification.request_data[:rack_env]
|
9
|
+
env = notification.request_data[:rack_env]
|
10
|
+
|
11
|
+
request = ::Rack::Request.new(env)
|
12
|
+
params = request.params
|
13
|
+
session = env["rack.session"]
|
14
|
+
|
15
|
+
# Set the context
|
16
|
+
notification.context = "#{request.request_method} #{request.path}"
|
17
|
+
|
18
|
+
# Set a sensible default for user_id
|
19
|
+
notification.user_id = request.ip
|
20
|
+
|
21
|
+
# Build the clean url (hide the port if it is obvious)
|
22
|
+
url = "#{request.scheme}://#{request.host}"
|
23
|
+
url << ":#{request.port}" unless [80, 443].include?(request.port)
|
24
|
+
url << request.fullpath
|
25
|
+
|
26
|
+
# Add a request tab
|
27
|
+
notification.add_tab(:request, {
|
28
|
+
:url => url,
|
29
|
+
:params => params.to_hash,
|
30
|
+
:userAgent => request.user_agent,
|
31
|
+
:clientIp => request.ip
|
32
|
+
})
|
33
|
+
|
34
|
+
# Add an environment tab
|
35
|
+
notification.add_tab(:environment, env)
|
36
|
+
|
37
|
+
# Add a session tab
|
38
|
+
notification.add_tab(:session, session) if session
|
39
|
+
|
40
|
+
# Add a cookies tab
|
41
|
+
cookies = request.cookies
|
42
|
+
notification.add_tab(:cookies, cookies) if cookies
|
43
|
+
end
|
44
|
+
|
45
|
+
@bugsnag.call(notification)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Bugsnag::Middleware
|
2
|
+
class Rails2Request
|
3
|
+
def initialize(bugsnag)
|
4
|
+
@bugsnag = bugsnag
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(notification)
|
8
|
+
if notification.request_data[:rails2_request]
|
9
|
+
request = notification.request_data[:rails2_request]
|
10
|
+
params = request.parameters || {}
|
11
|
+
session_data = request.session.respond_to?(:to_hash) ? request.session.to_hash : request.session.data
|
12
|
+
|
13
|
+
# Set the context
|
14
|
+
notification.context = "#{params[:controller]}##{params[:action]}"
|
15
|
+
|
16
|
+
# Set a sensible default for user_id
|
17
|
+
notification.user_id = request.remote_ip if request.respond_to?(:remote_ip)
|
18
|
+
|
19
|
+
# Build the clean url
|
20
|
+
url = "#{request.protocol}#{request.host}"
|
21
|
+
url << ":#{request.port}" unless [80, 443].include?(request.port)
|
22
|
+
url << request.fullpath
|
23
|
+
|
24
|
+
# Add a request tab
|
25
|
+
notification.add_tab(:request, {
|
26
|
+
:url => url,
|
27
|
+
:params => params.to_hash,
|
28
|
+
:controller => params[:controller],
|
29
|
+
:action => params[:action]
|
30
|
+
})
|
31
|
+
|
32
|
+
# Add an environment tab
|
33
|
+
notification.add_tab(:environment, request.env) if request.env
|
34
|
+
|
35
|
+
# Add a session tab
|
36
|
+
notification.add_tab(:session, session_data) if session_data
|
37
|
+
|
38
|
+
# Add a cookies tab
|
39
|
+
notification.add_tab(:cookies, request.cookies) if request.cookies
|
40
|
+
end
|
41
|
+
|
42
|
+
@bugsnag.call(notification)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bugsnag::Middleware
|
2
|
+
class Rails3Request
|
3
|
+
def initialize(bugsnag)
|
4
|
+
@bugsnag = bugsnag
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(notification)
|
8
|
+
if notification.request_data[:rack_env]
|
9
|
+
env = notification.request_data[:rack_env]
|
10
|
+
params = env["action_dispatch.request.parameters"]
|
11
|
+
|
12
|
+
# Set the context
|
13
|
+
notification.context = "#{params[:controller]}##{params[:action]}"
|
14
|
+
|
15
|
+
# Augment the request tab
|
16
|
+
if params
|
17
|
+
notification.add_tab(:request, {
|
18
|
+
:railsAction => "#{params[:controller]}##{params[:action]}",
|
19
|
+
:params => params
|
20
|
+
})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@bugsnag.call(notification)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Bugsnag::Middleware
|
2
|
+
class WardenUser
|
3
|
+
SCOPE_PATTERN = /^warden\.user\.([^.]+)\.key$/
|
4
|
+
COMMON_USER_FIELDS = [:email, :name, :first_name, :last_name, :created_at]
|
5
|
+
|
6
|
+
def initialize(bugsnag)
|
7
|
+
@bugsnag = bugsnag
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(notification)
|
11
|
+
if notification.request_data[:rack_env] && notification.request_data[:rack_env]["warden"]
|
12
|
+
env = notification.request_data[:rack_env]
|
13
|
+
session = env["rack.session"] || {}
|
14
|
+
|
15
|
+
# Find all warden user scopes
|
16
|
+
warden_scopes = session.keys.select {|k| k.match(SCOPE_PATTERN)}.map {|k| k.gsub(SCOPE_PATTERN, '\1')}
|
17
|
+
unless warden_scopes.empty?
|
18
|
+
# Pick the best scope for unique id (the default is "user")
|
19
|
+
best_scope = warden_scopes.include?("user") ? "user" : warden_scopes.first
|
20
|
+
|
21
|
+
# Set the user_id
|
22
|
+
if best_scope
|
23
|
+
user_id = session[best_scope][1][0] rescue nil
|
24
|
+
notification.user_id = user_id unless user_id.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Extract useful user information
|
28
|
+
warden_tab = {}
|
29
|
+
warden_scopes.each do |scope|
|
30
|
+
user_object = env["warden"].user({:scope => scope, :run_callbacks => false}) rescue nil
|
31
|
+
if user_object
|
32
|
+
# Build the user info for this scope
|
33
|
+
scope_hash = warden_tab["Warden #{scope.capitalize}"] = {}
|
34
|
+
COMMON_USER_FIELDS.each do |field|
|
35
|
+
scope_hash[field] = user_object.send(field) if user_object.respond_to?(field)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
notification.add_tab(:user, warden_tab) unless warden_tab.empty?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@bugsnag.call(notification)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Bugsnag
|
2
|
+
class MiddlewareStack
|
3
|
+
def initialize
|
4
|
+
@middlewares = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def use(new_middleware)
|
8
|
+
@middlewares << new_middleware
|
9
|
+
end
|
10
|
+
|
11
|
+
def delete(middleware)
|
12
|
+
@middlewares.delete(middleware)
|
13
|
+
end
|
14
|
+
|
15
|
+
def insert_after(after, new_middleware)
|
16
|
+
index = (@middlewares.rindex(after) + 1)
|
17
|
+
if index >= @middlewares.length
|
18
|
+
@middlewares << new_middleware
|
19
|
+
else
|
20
|
+
@middlewares.insert index, new_middleware
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def insert_before(before, new_middleware)
|
25
|
+
index = @middlewares.index(before) || @middlewares.length
|
26
|
+
@middlewares.insert index, new_middleware
|
27
|
+
end
|
28
|
+
|
29
|
+
# This allows people to proxy methods to the array if they want to do more complex stuff
|
30
|
+
def method_missing(method, *args, &block)
|
31
|
+
@middlewares.send(method, *args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Runs the middleware stack and calls
|
35
|
+
def run(notification)
|
36
|
+
# The final lambda is the termination of the middleware stack. It calls deliver on the notification
|
37
|
+
lambda_has_run = false
|
38
|
+
notify_lambda = lambda do |notification|
|
39
|
+
lambda_has_run = true
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
# We reverse them, so we can call "call" on the first middleware
|
45
|
+
middleware_procs.reverse.inject(notify_lambda) { |n,e| e[n] }.call(notification)
|
46
|
+
rescue Exception => e
|
47
|
+
# We dont notify, as we dont want to loop forever in the case of really broken middleware, we will
|
48
|
+
# still send this notify
|
49
|
+
Bugsnag.warn "Bugsnag middleware error: #{e}"
|
50
|
+
Bugsnag.log "Middleware error stacktrace: #{e.backtrace.inspect}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Ensure that the deliver has been performed, and no middleware has botched it
|
54
|
+
notify_lambda.call(notification) unless lambda_has_run
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
# Generates a list of middleware procs that are ready to be run
|
59
|
+
# Pass each one a reference to the next in the queue
|
60
|
+
def middleware_procs
|
61
|
+
@middlewares.map{|middleware| proc { |next_middleware| middleware.new(next_middleware) } }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/bugsnag/notification.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "httparty"
|
2
2
|
require "multi_json"
|
3
|
+
require "pathname"
|
3
4
|
|
4
5
|
module Bugsnag
|
5
6
|
class Notification
|
@@ -9,37 +10,43 @@ module Bugsnag
|
|
9
10
|
NOTIFIER_VERSION = Bugsnag::VERSION
|
10
11
|
NOTIFIER_URL = "http://www.bugsnag.com"
|
11
12
|
|
12
|
-
DEFAULT_ENDPOINT = "notify.bugsnag.com"
|
13
|
-
|
14
13
|
# HTTParty settings
|
15
14
|
headers "Content-Type" => "application/json"
|
16
15
|
default_timeout 5
|
17
|
-
|
18
|
-
|
19
|
-
attr_accessor :
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
16
|
+
|
17
|
+
attr_accessor :context
|
18
|
+
attr_accessor :user_id
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def deliver_exception_payload(endpoint, payload)
|
22
|
+
begin
|
23
|
+
payload_string = Bugsnag::Helpers.dump_json(payload)
|
24
|
+
|
25
|
+
# If the payload is going to be too long, we trim the hashes to send
|
26
|
+
# a minimal payload instead
|
27
|
+
if payload_string.length > 512000
|
28
|
+
payload = Bugsnag::Helpers.reduce_hash_size(payload)
|
29
|
+
payload_string = Bugsnag::Helpers.dump_json(payload)
|
30
|
+
end
|
31
|
+
|
32
|
+
response = post(endpoint, {:body => payload_string})
|
33
|
+
rescue Exception => e
|
34
|
+
Bugsnag.log("Notification to #{endpoint} failed, #{e.inspect}")
|
35
|
+
end
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
|
-
def initialize(exception,
|
39
|
-
|
39
|
+
def initialize(exception, configuration, overrides = nil, request_data = nil)
|
40
|
+
@configuration = configuration
|
41
|
+
@overrides = overrides || {}
|
42
|
+
@request_data = request_data
|
43
|
+
@meta_data = {}
|
44
|
+
|
45
|
+
# Unwrap exceptions
|
46
|
+
@exceptions = []
|
40
47
|
ex = exception
|
41
48
|
while ex != nil
|
42
|
-
|
49
|
+
@exceptions << ex
|
43
50
|
|
44
51
|
if ex.respond_to?(:continued_exception) && ex.continued_exception
|
45
52
|
ex = ex.continued_exception
|
@@ -49,60 +56,126 @@ module Bugsnag
|
|
49
56
|
ex = nil
|
50
57
|
end
|
51
58
|
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add a single value as custom data, to this notification
|
62
|
+
def add_custom_data(name, value)
|
63
|
+
@meta_data[:custom] ||= {}
|
64
|
+
@meta_data[:custom][name.to_sym] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
# Add a new tab to this notification
|
68
|
+
def add_tab(name, value)
|
69
|
+
return if name.nil?
|
52
70
|
|
53
|
-
|
54
|
-
|
71
|
+
if value.is_a? Hash
|
72
|
+
@meta_data[name.to_sym] ||= {}
|
73
|
+
@meta_data[name.to_sym].merge! value
|
74
|
+
else
|
75
|
+
self.add_custom_data(name, value)
|
76
|
+
Bugsnag.warn "Adding a tab requires a hash, adding to custom tab instead (name=#{name})"
|
55
77
|
end
|
56
78
|
end
|
57
79
|
|
80
|
+
# Remove a tab from this notification
|
81
|
+
def remove_tab(name)
|
82
|
+
return if name.nil?
|
83
|
+
|
84
|
+
@meta_data.delete(name.to_sym)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Deliver this notification to bugsnag.com Also runs through the middleware as required.
|
58
88
|
def deliver
|
59
|
-
return unless
|
60
|
-
|
61
|
-
|
89
|
+
return unless @configuration.should_notify?
|
90
|
+
|
91
|
+
# Check we have at least and api_key
|
92
|
+
unless @configuration.api_key
|
62
93
|
Bugsnag.warn "No API key configured, couldn't notify"
|
63
94
|
return
|
64
95
|
end
|
65
96
|
|
66
|
-
|
97
|
+
@meta_data = {}
|
98
|
+
|
99
|
+
# Run the middleware here, at the end of the middleware stack, execute the actual delivery
|
100
|
+
@configuration.middleware.run(self) do
|
101
|
+
# Now override the required fields
|
102
|
+
[:user_id, :context].each do |symbol|
|
103
|
+
if @overrides[symbol]
|
104
|
+
self.send("#{symbol}=", @overrides[symbol] )
|
105
|
+
@overrides.delete symbol
|
106
|
+
end
|
107
|
+
end
|
67
108
|
|
68
|
-
|
109
|
+
# Build the endpoint url
|
110
|
+
endpoint = (@configuration.use_ssl ? "https://" : "http://") + @configuration.endpoint
|
111
|
+
Bugsnag.log("Notifying #{endpoint} of #{@exceptions.last.class}")
|
69
112
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
:userId => self.user_id,
|
75
|
-
:appVersion => self.app_version,
|
76
|
-
:releaseStage => self.release_stage,
|
113
|
+
# Build the payload's exception event
|
114
|
+
payload_event = {
|
115
|
+
:releaseStage => @configuration.release_stage,
|
116
|
+
:appVersion => @configuration.app_version,
|
77
117
|
:context => self.context,
|
118
|
+
:userId => self.user_id,
|
78
119
|
:exceptions => exception_list,
|
79
|
-
:metaData =>
|
80
|
-
}.reject {|k,v| v.nil? }
|
81
|
-
|
120
|
+
:metaData => Bugsnag::Helpers.cleanup_obj(generate_meta_data(@overrides), @configuration.params_filters)
|
121
|
+
}.reject {|k,v| v.nil? }
|
122
|
+
|
123
|
+
# Build the payload hash
|
124
|
+
payload = {
|
125
|
+
:apiKey => @configuration.api_key,
|
126
|
+
:notifier => {
|
127
|
+
:name => NOTIFIER_NAME,
|
128
|
+
:version => NOTIFIER_VERSION,
|
129
|
+
:url => NOTIFIER_URL
|
130
|
+
},
|
131
|
+
:events => [payload_event]
|
132
|
+
}
|
82
133
|
|
83
|
-
|
134
|
+
self.class.deliver_exception_payload(endpoint, payload)
|
135
|
+
end
|
84
136
|
end
|
85
137
|
|
86
138
|
def ignore?
|
87
|
-
|
139
|
+
@configuration.ignore_classes.include?(error_class(@exceptions.last))
|
88
140
|
end
|
89
141
|
|
142
|
+
def request_data
|
143
|
+
@request_data || Bugsnag.configuration.request_data
|
144
|
+
end
|
145
|
+
|
146
|
+
def exceptions
|
147
|
+
@exceptions
|
148
|
+
end
|
90
149
|
|
91
150
|
private
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
151
|
+
# Generate the meta data from both the request configuration and the overrides for this notification
|
152
|
+
def generate_meta_data(overrides)
|
153
|
+
# Copy the request meta data so we dont edit it by mistake
|
154
|
+
meta_data = @meta_data.dup
|
155
|
+
|
156
|
+
overrides.each do |key, value|
|
157
|
+
# If its a hash, its a tab so we can just add it providing its not reserved
|
158
|
+
if value.is_a? Hash
|
159
|
+
key = key.to_sym
|
160
|
+
|
161
|
+
if meta_data[key]
|
162
|
+
# If its a clash, merge with the existing data
|
163
|
+
meta_data[key].merge! value
|
164
|
+
else
|
165
|
+
# Add it as is if its not special
|
166
|
+
meta_data[key] = value
|
167
|
+
end
|
168
|
+
else
|
169
|
+
meta_data[:custom] ||= {}
|
170
|
+
meta_data[:custom][key] = value
|
171
|
+
end
|
99
172
|
end
|
100
|
-
|
101
|
-
|
173
|
+
|
174
|
+
meta_data
|
102
175
|
end
|
103
|
-
|
104
|
-
def exception_list
|
105
|
-
|
176
|
+
|
177
|
+
def exception_list
|
178
|
+
@exceptions.map do |exception|
|
106
179
|
{
|
107
180
|
:errorClass => error_class(exception),
|
108
181
|
:message => exception.message,
|
@@ -118,16 +191,32 @@ module Bugsnag
|
|
118
191
|
end
|
119
192
|
|
120
193
|
def stacktrace(exception)
|
121
|
-
(exception.backtrace || caller).map do |trace|
|
194
|
+
(exception.backtrace || caller).map do |trace|
|
122
195
|
method = nil
|
123
196
|
file, line_str, method_str = trace.split(":")
|
124
197
|
|
198
|
+
next(nil) if file =~ %r{lib/bugsnag}
|
199
|
+
|
200
|
+
# Expand relative paths
|
201
|
+
file = Pathname.new(file).realpath.to_s rescue file
|
202
|
+
|
125
203
|
# Generate the stacktrace line hash
|
126
204
|
trace_hash = {}
|
127
|
-
trace_hash[:inProject] = true if
|
128
|
-
trace_hash[:file] = self.stacktrace_filters.inject(file) {|file, proc| proc.call(file) }
|
205
|
+
trace_hash[:inProject] = true if @configuration.project_root && file.match(/^#{@configuration.project_root}/) && !file.match(/vendor\//)
|
129
206
|
trace_hash[:lineNumber] = line_str.to_i
|
130
207
|
|
208
|
+
# Clean up the file path in the stacktrace
|
209
|
+
if defined?(Bugsnag.configuration.project_root) && Bugsnag.configuration.project_root.to_s != ''
|
210
|
+
file.sub!(/#{Bugsnag.configuration.project_root}\//, "")
|
211
|
+
end
|
212
|
+
|
213
|
+
# Strip common gem path prefixes
|
214
|
+
if defined?(Gem)
|
215
|
+
file = Gem.path.inject(file) {|line, path| line.sub(/#{path}\//, "") }
|
216
|
+
end
|
217
|
+
|
218
|
+
trace_hash[:file] = file
|
219
|
+
|
131
220
|
# Add a method if we have it
|
132
221
|
if method_str
|
133
222
|
method_match = /in `([^']+)'/.match(method_str)
|