honeybadger 1.13.2 → 1.14.0

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