honeybadger 1.13.2 → 1.14.0
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 +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
|