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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +60 -45
  3. data/CHANGELOG.md +30 -1
  4. data/Gemfile.lock +1 -1
  5. data/MIT-LICENSE +2 -1
  6. data/Rakefile +8 -4
  7. data/features/standalone.feature +73 -0
  8. data/features/step_definitions/rack_steps.rb +1 -2
  9. data/features/step_definitions/standalone_steps.rb +12 -0
  10. data/features/step_definitions/thor_steps.rb +4 -0
  11. data/features/support/env.rb +2 -0
  12. data/features/support/test.thor +22 -0
  13. data/features/thor.feature +5 -0
  14. data/gemfiles/binding_of_caller.gemfile +8 -0
  15. data/gemfiles/rails.gemfile +11 -0
  16. data/gemfiles/rake.gemfile +1 -1
  17. data/gemfiles/standalone.gemfile +7 -0
  18. data/gemfiles/thor.gemfile +8 -0
  19. data/honeybadger.gemspec +27 -11
  20. data/lib/honeybadger.rb +15 -10
  21. data/lib/honeybadger/configuration.rb +9 -4
  22. data/lib/honeybadger/dependency.rb +65 -0
  23. data/lib/honeybadger/exception_extensions.rb +35 -0
  24. data/lib/honeybadger/integrations.rb +5 -0
  25. data/lib/honeybadger/integrations/delayed_job.rb +20 -0
  26. data/lib/honeybadger/integrations/delayed_job/plugin.rb +31 -0
  27. data/lib/honeybadger/integrations/sidekiq.rb +17 -9
  28. data/lib/honeybadger/integrations/thor.rb +29 -0
  29. data/lib/honeybadger/notice.rb +47 -99
  30. data/lib/honeybadger/payload.rb +101 -0
  31. data/lib/honeybadger/rack.rb +8 -54
  32. data/lib/honeybadger/rack/error_notifier.rb +60 -0
  33. data/lib/honeybadger/rack/user_feedback.rb +74 -0
  34. data/lib/honeybadger/rack/user_informer.rb +28 -0
  35. data/lib/honeybadger/rails.rb +5 -4
  36. data/lib/honeybadger/railtie.rb +4 -3
  37. data/lib/honeybadger/user_feedback.rb +3 -67
  38. data/lib/honeybadger/user_informer.rb +3 -21
  39. data/spec/honeybadger/configuration_spec.rb +5 -1
  40. data/spec/honeybadger/dependency_spec.rb +134 -0
  41. data/spec/honeybadger/exception_extensions_spec.rb +40 -0
  42. data/spec/honeybadger/integrations/delayed_job_spec.rb +48 -0
  43. data/spec/honeybadger/integrations/sidekiq_spec.rb +60 -0
  44. data/spec/honeybadger/integrations/thor_spec.rb +29 -0
  45. data/spec/honeybadger/notice_spec.rb +137 -127
  46. data/spec/honeybadger/payload_spec.rb +162 -0
  47. data/spec/honeybadger/rack_spec.rb +6 -6
  48. data/spec/honeybadger/rails/action_controller_spec.rb +2 -0
  49. data/spec/honeybadger/rails_spec.rb +4 -2
  50. data/spec/honeybadger/user_feedback_spec.rb +2 -2
  51. data/spec/honeybadger/user_informer_spec.rb +3 -3
  52. metadata +49 -66
  53. data/gemfiles/rack.gemfile.lock +0 -125
  54. data/gemfiles/rails2.3.gemfile.lock +0 -141
  55. data/gemfiles/rails3.0.gemfile.lock +0 -193
  56. data/gemfiles/rails3.1.gemfile.lock +0 -203
  57. data/gemfiles/rails3.2.gemfile.lock +0 -201
  58. data/gemfiles/rails4.0.gemfile.lock +0 -197
  59. data/gemfiles/rails4.1.gemfile.lock +0 -202
  60. data/gemfiles/rake.gemfile.lock +0 -124
  61. 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
@@ -1,58 +1,12 @@
1
- module Honeybadger
2
- # Middleware for Rack applications. Any errors raised by the upstream
3
- # application will be delivered to Honeybadger and re-raised.
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
- def ignored_user_agent?(env)
29
- true if Honeybadger.
30
- configuration.
31
- ignore_user_agent.
32
- flatten.
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
@@ -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
@@ -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
- @app = app
20
- end
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