honeybadger 1.13.2 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Appraisals +60 -45
- data/CHANGELOG.md +30 -1
- data/Gemfile.lock +1 -1
- data/MIT-LICENSE +2 -1
- data/Rakefile +8 -4
- data/features/standalone.feature +73 -0
- data/features/step_definitions/rack_steps.rb +1 -2
- data/features/step_definitions/standalone_steps.rb +12 -0
- data/features/step_definitions/thor_steps.rb +4 -0
- data/features/support/env.rb +2 -0
- data/features/support/test.thor +22 -0
- data/features/thor.feature +5 -0
- data/gemfiles/binding_of_caller.gemfile +8 -0
- data/gemfiles/rails.gemfile +11 -0
- data/gemfiles/rake.gemfile +1 -1
- data/gemfiles/standalone.gemfile +7 -0
- data/gemfiles/thor.gemfile +8 -0
- data/honeybadger.gemspec +27 -11
- data/lib/honeybadger.rb +15 -10
- data/lib/honeybadger/configuration.rb +9 -4
- data/lib/honeybadger/dependency.rb +65 -0
- data/lib/honeybadger/exception_extensions.rb +35 -0
- data/lib/honeybadger/integrations.rb +5 -0
- data/lib/honeybadger/integrations/delayed_job.rb +20 -0
- data/lib/honeybadger/integrations/delayed_job/plugin.rb +31 -0
- data/lib/honeybadger/integrations/sidekiq.rb +17 -9
- data/lib/honeybadger/integrations/thor.rb +29 -0
- data/lib/honeybadger/notice.rb +47 -99
- data/lib/honeybadger/payload.rb +101 -0
- data/lib/honeybadger/rack.rb +8 -54
- data/lib/honeybadger/rack/error_notifier.rb +60 -0
- data/lib/honeybadger/rack/user_feedback.rb +74 -0
- data/lib/honeybadger/rack/user_informer.rb +28 -0
- data/lib/honeybadger/rails.rb +5 -4
- data/lib/honeybadger/railtie.rb +4 -3
- data/lib/honeybadger/user_feedback.rb +3 -67
- data/lib/honeybadger/user_informer.rb +3 -21
- data/spec/honeybadger/configuration_spec.rb +5 -1
- data/spec/honeybadger/dependency_spec.rb +134 -0
- data/spec/honeybadger/exception_extensions_spec.rb +40 -0
- data/spec/honeybadger/integrations/delayed_job_spec.rb +48 -0
- data/spec/honeybadger/integrations/sidekiq_spec.rb +60 -0
- data/spec/honeybadger/integrations/thor_spec.rb +29 -0
- data/spec/honeybadger/notice_spec.rb +137 -127
- data/spec/honeybadger/payload_spec.rb +162 -0
- data/spec/honeybadger/rack_spec.rb +6 -6
- data/spec/honeybadger/rails/action_controller_spec.rb +2 -0
- data/spec/honeybadger/rails_spec.rb +4 -2
- data/spec/honeybadger/user_feedback_spec.rb +2 -2
- data/spec/honeybadger/user_informer_spec.rb +3 -3
- metadata +49 -66
- data/gemfiles/rack.gemfile.lock +0 -125
- data/gemfiles/rails2.3.gemfile.lock +0 -141
- data/gemfiles/rails3.0.gemfile.lock +0 -193
- data/gemfiles/rails3.1.gemfile.lock +0 -203
- data/gemfiles/rails3.2.gemfile.lock +0 -201
- data/gemfiles/rails4.0.gemfile.lock +0 -197
- data/gemfiles/rails4.1.gemfile.lock +0 -202
- data/gemfiles/rake.gemfile.lock +0 -124
- data/gemfiles/sinatra.gemfile.lock +0 -124
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
class Payload < SimpleDelegator
|
5
|
+
TOP_LEVEL_KEYS = [:api_key, :notifier, :error, :request, :server]
|
6
|
+
OBJECT_WHITELIST = [Hash, Array, String, Integer, Float, TrueClass, FalseClass, NilClass]
|
7
|
+
|
8
|
+
# Define getters for top level keys
|
9
|
+
TOP_LEVEL_KEYS.each do |key|
|
10
|
+
define_method key do
|
11
|
+
self[key]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(hash = {}, options = {})
|
16
|
+
fail ArgumentError, 'must be a Hash' unless hash.kind_of?(Hash)
|
17
|
+
|
18
|
+
@max_depth = options[:max_depth] || 20
|
19
|
+
@filters = options[:filters]
|
20
|
+
|
21
|
+
super(sanitize(hash))
|
22
|
+
|
23
|
+
TOP_LEVEL_KEYS.each {|k| self[k] ||= {} }
|
24
|
+
|
25
|
+
filter_url!(request[:url]) if request[:url]
|
26
|
+
filter_urls!(request[:cgi_data]) if request[:cgi_data]
|
27
|
+
|
28
|
+
filter!(request[:params]) if request[:params]
|
29
|
+
filter!(request[:session]) if request[:session]
|
30
|
+
filter!(request[:cgi_data]) if request[:cgi_data]
|
31
|
+
filter!(request[:local_variables]) if request[:local_variables]
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
attr_reader :max_depth, :filters
|
37
|
+
|
38
|
+
# Removes non-serializable data and truncates to max depth.
|
39
|
+
def sanitize(data, depth = 0, stack = [])
|
40
|
+
return '[possible infinite recursion halted]' if stack.any?{|item| item == data.object_id }
|
41
|
+
|
42
|
+
if data.respond_to?(:to_hash)
|
43
|
+
return '[max depth reached]' if depth >= max_depth
|
44
|
+
data.to_hash.reduce({}) do |result, (key, value)|
|
45
|
+
result.merge(key => sanitize(value, depth+1, stack + [data.object_id]))
|
46
|
+
end
|
47
|
+
elsif data.respond_to?(:to_ary)
|
48
|
+
return '[max depth reached]' if depth >= max_depth
|
49
|
+
data.to_ary.collect do |value|
|
50
|
+
sanitize(value, depth+1, stack + [data.object_id])
|
51
|
+
end
|
52
|
+
elsif OBJECT_WHITELIST.any? {|c| data.kind_of?(c) }
|
53
|
+
data
|
54
|
+
else
|
55
|
+
data.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def filter!(hash)
|
60
|
+
if filters
|
61
|
+
hash.each do |key, value|
|
62
|
+
if filter_key?(key)
|
63
|
+
hash[key] = "[FILTERED]"
|
64
|
+
elsif value.respond_to?(:to_hash)
|
65
|
+
filter!(hash[key])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def filter_key?(key)
|
72
|
+
return false unless filters
|
73
|
+
|
74
|
+
filters.any? do |filter|
|
75
|
+
if filter.is_a?(Regexp)
|
76
|
+
key.to_s =~ filter
|
77
|
+
else
|
78
|
+
key.to_s.eql?(filter.to_s)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def filter_url!(url)
|
84
|
+
return nil unless url =~ /\S/
|
85
|
+
|
86
|
+
url.scan(/(?:^|&|\?)([^=?&]+)=([^&]+)/).each do |m|
|
87
|
+
next unless filter_key?(m[0])
|
88
|
+
url.gsub!(/#{m[1]}/, '[FILTERED]')
|
89
|
+
end
|
90
|
+
|
91
|
+
url
|
92
|
+
end
|
93
|
+
|
94
|
+
def filter_urls!(hash)
|
95
|
+
hash.each_pair do |key, value|
|
96
|
+
next unless value.kind_of?(String) && key =~ /\A[A-Z_]+\Z/ && value =~ /\S/
|
97
|
+
filter_url!(value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/honeybadger/rack.rb
CHANGED
@@ -1,58 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
# Synopsis:
|
6
|
-
#
|
7
|
-
# require 'rack'
|
8
|
-
# require 'honeybadger'
|
9
|
-
#
|
10
|
-
# Honeybadger.configure do |config|
|
11
|
-
# config.api_key = 'my_api_key'
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# app = Rack::Builder.app do
|
15
|
-
# run lambda { |env| raise "Rack down" }
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# use Honeybadger::Rack
|
19
|
-
# run app
|
20
|
-
#
|
21
|
-
# Use a standard Honeybadger.configure call to configure your api key.
|
22
|
-
class Rack
|
23
|
-
def initialize(app)
|
24
|
-
@app = app
|
25
|
-
Honeybadger.configuration.framework = "Rack: #{::Rack.release}"
|
26
|
-
end
|
1
|
+
require 'honeybadger/rack/error_notifier'
|
2
|
+
require 'honeybadger/rack/user_informer'
|
3
|
+
require 'honeybadger/rack/user_feedback'
|
27
4
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
any? { |ua| ua === env['HTTP_USER_AGENT'] }
|
34
|
-
end
|
35
|
-
|
36
|
-
def notify_honeybadger(exception,env)
|
37
|
-
Honeybadger.notify_or_ignore(exception, :rack_env => env) unless ignored_user_agent?(env)
|
38
|
-
end
|
39
|
-
|
40
|
-
def call(env)
|
41
|
-
begin
|
42
|
-
response = @app.call(env)
|
43
|
-
rescue Exception => raised
|
44
|
-
env['honeybadger.error_id'] = notify_honeybadger(raised, env)
|
45
|
-
raise
|
46
|
-
ensure
|
47
|
-
Honeybadger.context.clear!
|
48
|
-
end
|
49
|
-
|
50
|
-
framework_exception = env['rack.exception'] || env['sinatra.error']
|
51
|
-
if framework_exception
|
52
|
-
env['honeybadger.error_id'] = notify_honeybadger(framework_exception, env)
|
53
|
-
end
|
54
|
-
|
55
|
-
response
|
5
|
+
module Honeybadger
|
6
|
+
module Rack
|
7
|
+
def self.new(*args, &block)
|
8
|
+
warn '[DEPRECATION] Honeybadger::Rack is deprecated in 2.0. Use Honeybadger::Rack::ErrorNotifier.'
|
9
|
+
ErrorNotifier.new(*args, &block)
|
56
10
|
end
|
57
11
|
end
|
58
12
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
module Rack
|
3
|
+
# Middleware for Rack applications. Any errors raised by the upstream
|
4
|
+
# application will be delivered to Honeybadger and re-raised.
|
5
|
+
#
|
6
|
+
# Synopsis:
|
7
|
+
#
|
8
|
+
# require 'rack'
|
9
|
+
# require 'honeybadger'
|
10
|
+
#
|
11
|
+
# Honeybadger.configure do |config|
|
12
|
+
# config.api_key = 'my_api_key'
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# app = Rack::Builder.app do
|
16
|
+
# run lambda { |env| raise "Rack down" }
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# use Honeybadger::Rack::ErrorNotifier
|
20
|
+
# run app
|
21
|
+
#
|
22
|
+
# Use a standard Honeybadger.configure call to configure your api key.
|
23
|
+
class ErrorNotifier
|
24
|
+
def initialize(app)
|
25
|
+
@app = app
|
26
|
+
Honeybadger.configuration.framework = "Rack: #{::Rack.release}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def ignored_user_agent?(env)
|
30
|
+
true if Honeybadger.
|
31
|
+
configuration.
|
32
|
+
ignore_user_agent.
|
33
|
+
flatten.
|
34
|
+
any? { |ua| ua === env['HTTP_USER_AGENT'] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify_honeybadger(exception,env)
|
38
|
+
Honeybadger.notify_or_ignore(exception, :rack_env => env) unless ignored_user_agent?(env)
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(env)
|
42
|
+
begin
|
43
|
+
response = @app.call(env)
|
44
|
+
rescue Exception => raised
|
45
|
+
env['honeybadger.error_id'] = notify_honeybadger(raised, env)
|
46
|
+
raise
|
47
|
+
ensure
|
48
|
+
Honeybadger.context.clear!
|
49
|
+
end
|
50
|
+
|
51
|
+
framework_exception = env['rack.exception'] || env['sinatra.error']
|
52
|
+
if framework_exception
|
53
|
+
env['honeybadger.error_id'] = notify_honeybadger(framework_exception, env)
|
54
|
+
end
|
55
|
+
|
56
|
+
response
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'i18n'
|
6
|
+
rescue LoadError
|
7
|
+
module Honeybadger
|
8
|
+
module I18n
|
9
|
+
def self.t(key, options={})
|
10
|
+
options[:default]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Honeybadger
|
17
|
+
module Rack
|
18
|
+
class UserFeedback
|
19
|
+
def initialize(app)
|
20
|
+
@app = app
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
status, headers, body = @app.call(env)
|
25
|
+
if enabled? && env['honeybadger.error_id'] && form = render_form(env['honeybadger.error_id'])
|
26
|
+
new_body = []
|
27
|
+
body.each do |chunk|
|
28
|
+
new_body << chunk.gsub("<!-- HONEYBADGER FEEDBACK -->", form)
|
29
|
+
end
|
30
|
+
body.close if body.respond_to?(:close)
|
31
|
+
headers['Content-Length'] = new_body.reduce(0) { |a,e| a += e.bytesize }.to_s
|
32
|
+
body = new_body
|
33
|
+
end
|
34
|
+
[status, headers, body]
|
35
|
+
end
|
36
|
+
|
37
|
+
def config
|
38
|
+
Honeybadger.configuration
|
39
|
+
end
|
40
|
+
|
41
|
+
def enabled?
|
42
|
+
config.feedback && config.features['feedback']
|
43
|
+
end
|
44
|
+
|
45
|
+
def action
|
46
|
+
URI.parse("#{config.protocol}://#{config.host}:#{config.port}/v1/feedback/").to_s
|
47
|
+
rescue URI::InvalidURIError
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def render_form(error_id, action = action)
|
52
|
+
return unless action
|
53
|
+
ERB.new(@template ||= File.read(template_file)).result(binding)
|
54
|
+
end
|
55
|
+
|
56
|
+
def custom_template_file
|
57
|
+
@custom_template_file ||= config.project_root &&
|
58
|
+
File.join(config.project_root, 'lib', 'honeybadger', 'templates', 'feedback_form.erb')
|
59
|
+
end
|
60
|
+
|
61
|
+
def custom_template_file?
|
62
|
+
custom_template_file && File.exists?(custom_template_file)
|
63
|
+
end
|
64
|
+
|
65
|
+
def template_file
|
66
|
+
if custom_template_file?
|
67
|
+
custom_template_file
|
68
|
+
else
|
69
|
+
File.expand_path('../../templates/feedback_form.erb', __FILE__)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
module Rack
|
3
|
+
class UserInformer
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def replacement(with)
|
9
|
+
Honeybadger.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
status, headers, body = @app.call(env)
|
14
|
+
if env['honeybadger.error_id'] && Honeybadger.configuration.user_information
|
15
|
+
new_body = []
|
16
|
+
replace = replacement(env['honeybadger.error_id'])
|
17
|
+
body.each do |chunk|
|
18
|
+
new_body << chunk.gsub("<!-- HONEYBADGER ERROR -->", replace)
|
19
|
+
end
|
20
|
+
body.close if body.respond_to?(:close)
|
21
|
+
headers['Content-Length'] = new_body.reduce(0) { |a,e| a += e.bytesize }.to_s
|
22
|
+
body = new_body
|
23
|
+
end
|
24
|
+
[status, headers, body]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/honeybadger/rails.rb
CHANGED
@@ -18,11 +18,11 @@ module Honeybadger
|
|
18
18
|
|
19
19
|
if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware)
|
20
20
|
::Rails.configuration.middleware.insert_after 'ActionController::Failsafe',
|
21
|
-
Honeybadger::Rack
|
21
|
+
Honeybadger::Rack::ErrorNotifier
|
22
22
|
::Rails.configuration.middleware.insert_after 'Rack::Lock',
|
23
|
-
Honeybadger::UserInformer
|
24
|
-
::Rails.configuration.middleware.insert_after Honeybadger::UserInformer,
|
25
|
-
Honeybadger::UserFeedback
|
23
|
+
Honeybadger::Rack::UserInformer
|
24
|
+
::Rails.configuration.middleware.insert_after Honeybadger::Rack::UserInformer,
|
25
|
+
Honeybadger::Rack::UserFeedback
|
26
26
|
end
|
27
27
|
|
28
28
|
Honeybadger.configure(true) do |config|
|
@@ -34,6 +34,7 @@ module Honeybadger
|
|
34
34
|
|
35
35
|
if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:after_initialize)
|
36
36
|
::Rails.configuration.after_initialize do
|
37
|
+
Honeybadger::Dependency.inject!
|
37
38
|
Honeybadger.ping(Honeybadger.configuration)
|
38
39
|
end
|
39
40
|
end
|
data/lib/honeybadger/railtie.rb
CHANGED
@@ -10,9 +10,9 @@ module Honeybadger
|
|
10
10
|
end
|
11
11
|
|
12
12
|
initializer "honeybadger.use_rack_middleware" do |app|
|
13
|
-
app.config.middleware.insert 0, "Honeybadger::UserInformer"
|
14
|
-
app.config.middleware.insert_after "Honeybadger::UserInformer","Honeybadger::UserFeedback"
|
15
|
-
app.config.middleware.insert_after "Honeybadger::UserFeedback","Honeybadger::Rack"
|
13
|
+
app.config.middleware.insert 0, "Honeybadger::Rack::UserInformer"
|
14
|
+
app.config.middleware.insert_after "Honeybadger::Rack::UserInformer","Honeybadger::Rack::UserFeedback"
|
15
|
+
app.config.middleware.insert_after "Honeybadger::Rack::UserFeedback","Honeybadger::Rack::ErrorNotifier"
|
16
16
|
end
|
17
17
|
|
18
18
|
config.after_initialize do
|
@@ -44,6 +44,7 @@ module Honeybadger
|
|
44
44
|
::ActionDispatch::ShowExceptions.send(:include,Honeybadger::Rails::Middleware::ExceptionsCatcher)
|
45
45
|
end
|
46
46
|
|
47
|
+
Honeybadger::Dependency.inject!
|
47
48
|
Honeybadger.ping(Honeybadger.configuration)
|
48
49
|
end
|
49
50
|
end
|
@@ -1,72 +1,8 @@
|
|
1
|
-
require 'erb'
|
2
|
-
require 'uri'
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'i18n'
|
6
|
-
rescue LoadError
|
7
|
-
module Honeybadger
|
8
|
-
module I18n
|
9
|
-
def self.t(key, options={})
|
10
|
-
options[:default]
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
1
|
module Honeybadger
|
17
|
-
class UserFeedback
|
2
|
+
class UserFeedback < Rack::UserFeedback
|
18
3
|
def initialize(app)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def call(env)
|
23
|
-
status, headers, body = @app.call(env)
|
24
|
-
if enabled? && env['honeybadger.error_id'] && form = render_form(env['honeybadger.error_id'])
|
25
|
-
new_body = []
|
26
|
-
body.each do |chunk|
|
27
|
-
new_body << chunk.gsub("<!-- HONEYBADGER FEEDBACK -->", form)
|
28
|
-
end
|
29
|
-
body.close if body.respond_to?(:close)
|
30
|
-
headers['Content-Length'] = new_body.reduce(0) { |a,e| a += e.bytesize }.to_s
|
31
|
-
body = new_body
|
32
|
-
end
|
33
|
-
[status, headers, body]
|
34
|
-
end
|
35
|
-
|
36
|
-
def config
|
37
|
-
Honeybadger.configuration
|
38
|
-
end
|
39
|
-
|
40
|
-
def enabled?
|
41
|
-
config.feedback && config.features['feedback']
|
42
|
-
end
|
43
|
-
|
44
|
-
def action
|
45
|
-
URI.parse("#{config.protocol}://#{config.host}:#{config.port}/v1/feedback/").to_s
|
46
|
-
rescue URI::InvalidURIError
|
47
|
-
nil
|
48
|
-
end
|
49
|
-
|
50
|
-
def render_form(error_id, action = action)
|
51
|
-
return unless action
|
52
|
-
ERB.new(@template ||= File.read(template_file)).result(binding)
|
53
|
-
end
|
54
|
-
|
55
|
-
def custom_template_file
|
56
|
-
@custom_template_file ||= config.project_root &&
|
57
|
-
File.join(config.project_root, 'lib', 'honeybadger', 'templates', 'feedback_form.erb')
|
58
|
-
end
|
59
|
-
|
60
|
-
def custom_template_file?
|
61
|
-
custom_template_file && File.exists?(custom_template_file)
|
62
|
-
end
|
63
|
-
|
64
|
-
def template_file
|
65
|
-
if custom_template_file?
|
66
|
-
custom_template_file
|
67
|
-
else
|
68
|
-
File.expand_path('../templates/feedback_form.erb', __FILE__)
|
69
|
-
end
|
4
|
+
warn '[DEPRECATION] Honeybadger::UserFeedback is deprecated in 2.0. Use Honeybadger::Rack::UserFeedback.'
|
5
|
+
super
|
70
6
|
end
|
71
7
|
end
|
72
8
|
end
|