honeybadger 1.11.2 → 1.12.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +56 -46
  3. data/CHANGELOG.md +33 -0
  4. data/Gemfile.lock +1 -1
  5. data/MIT-LICENSE +2 -1
  6. data/Rakefile +3 -1
  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/support/env.rb +2 -0
  11. data/gemfiles/binding_of_caller.gemfile +8 -0
  12. data/gemfiles/rails.gemfile +11 -0
  13. data/gemfiles/standalone.gemfile +7 -0
  14. data/honeybadger.gemspec +22 -11
  15. data/lib/honeybadger.rb +15 -9
  16. data/lib/honeybadger/configuration.rb +9 -4
  17. data/lib/honeybadger/dependency.rb +65 -0
  18. data/lib/honeybadger/exception_extensions.rb +35 -0
  19. data/lib/honeybadger/integrations.rb +4 -0
  20. data/lib/honeybadger/integrations/delayed_job.rb +20 -0
  21. data/lib/honeybadger/integrations/delayed_job/plugin.rb +31 -0
  22. data/lib/honeybadger/integrations/sidekiq.rb +34 -0
  23. data/lib/honeybadger/notice.rb +48 -11
  24. data/lib/honeybadger/payload.rb +29 -0
  25. data/lib/honeybadger/rack.rb +8 -54
  26. data/lib/honeybadger/rack/error_notifier.rb +60 -0
  27. data/lib/honeybadger/rack/user_feedback.rb +74 -0
  28. data/lib/honeybadger/rack/user_informer.rb +28 -0
  29. data/lib/honeybadger/rails.rb +5 -4
  30. data/lib/honeybadger/railtie.rb +4 -3
  31. data/lib/honeybadger/user_feedback.rb +3 -67
  32. data/lib/honeybadger/user_informer.rb +3 -21
  33. data/spec/honeybadger/configuration_spec.rb +5 -1
  34. data/spec/honeybadger/dependency_spec.rb +134 -0
  35. data/spec/honeybadger/exception_extensions_spec.rb +40 -0
  36. data/spec/honeybadger/integrations/delayed_job_spec.rb +48 -0
  37. data/spec/honeybadger/integrations/sidekiq_spec.rb +60 -0
  38. data/spec/honeybadger/notice_spec.rb +176 -35
  39. data/spec/honeybadger/payload_spec.rb +27 -0
  40. data/spec/honeybadger/rails_spec.rb +4 -2
  41. metadata +24 -13
  42. data/gemfiles/rack.gemfile.lock +0 -125
  43. data/gemfiles/rails2.3.gemfile.lock +0 -141
  44. data/gemfiles/rails3.0.gemfile.lock +0 -193
  45. data/gemfiles/rails3.1.gemfile.lock +0 -203
  46. data/gemfiles/rails3.2.gemfile.lock +0 -201
  47. data/gemfiles/rails4.0.gemfile.lock +0 -197
  48. data/gemfiles/rails4.1.gemfile.lock +0 -202
  49. data/gemfiles/rake.gemfile.lock +0 -124
  50. data/gemfiles/sinatra.gemfile.lock +0 -124
@@ -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
@@ -1,26 +1,8 @@
1
1
  module Honeybadger
2
- class UserInformer
2
+ class UserInformer < Rack::UserInformer
3
3
  def initialize(app)
4
- @app = app
5
- end
6
-
7
- def replacement(with)
8
- Honeybadger.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
9
- end
10
-
11
- def call(env)
12
- status, headers, body = @app.call(env)
13
- if env['honeybadger.error_id'] && Honeybadger.configuration.user_information
14
- new_body = []
15
- replace = replacement(env['honeybadger.error_id'])
16
- body.each do |chunk|
17
- new_body << chunk.gsub("<!-- HONEYBADGER ERROR -->", replace)
18
- end
19
- body.close if body.respond_to?(:close)
20
- headers['Content-Length'] = new_body.reduce(0) { |a,e| a += e.bytesize }.to_s
21
- body = new_body
22
- end
23
- [status, headers, body]
4
+ warn '[DEPRECATION] Honeybadger::UserInformer is deprecated in 2.0. Use Honeybadger::Rack::UserInformer.'
5
+ super
24
6
  end
25
7
  end
26
8
  end
@@ -30,11 +30,13 @@ describe Honeybadger::Configuration do
30
30
  assert_config_default :source_extract_radius, 2
31
31
  assert_config_default :async, nil
32
32
  assert_config_default :send_request_session, true
33
+ assert_config_default :send_local_variables, false
33
34
  assert_config_default :debug, false
34
35
  assert_config_default :log_exception_on_send_failure, false
35
36
  assert_config_default :fingerprint, nil
36
37
  assert_config_default :hostname, Socket.gethostname
37
38
  assert_config_default :feedback, true
39
+ assert_config_default :features, {'notices' => true, 'local_variables' => true}
38
40
  end
39
41
 
40
42
  it "configures async as Proc" do
@@ -116,6 +118,7 @@ describe Honeybadger::Configuration do
116
118
  assert_config_overridable :send_request_session
117
119
  assert_config_overridable :debug
118
120
  assert_config_overridable :hostname
121
+ assert_config_overridable :features
119
122
  assert_config_overridable :metrics
120
123
  assert_config_overridable :feedback
121
124
  assert_config_overridable :log_exception_on_send_failure
@@ -134,7 +137,8 @@ describe Honeybadger::Configuration do
134
137
  :notifier_version, :params_filters, :project_root, :port, :protocol,
135
138
  :proxy_host, :proxy_pass, :proxy_port, :proxy_user, :secure,
136
139
  :source_extract_radius, :async, :send_request_session, :debug,
137
- :fingerprint, :hostname, :metrics, :feedback, :log_exception_on_send_failure].each do |option|
140
+ :fingerprint, :hostname, :features, :metrics, :feedback,
141
+ :log_exception_on_send_failure].each do |option|
138
142
  expect(hash[option]).to eq config[option]
139
143
  end
140
144
  end
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ describe Honeybadger::Dependency do
4
+ let(:dependency) { Honeybadger::Dependency.new }
5
+ subject { dependency }
6
+
7
+ before { Honeybadger::Dependency.stub(:instances).and_return([]) }
8
+
9
+ describe ".register" do
10
+ it "returns a new dependency" do
11
+ instance = double()
12
+ Honeybadger::Dependency.stub(:new).and_return(instance)
13
+ expect(Honeybadger::Dependency.register {}).to eq [instance]
14
+ end
15
+
16
+ it "registers a new dependency" do
17
+ expect { Honeybadger::Dependency.register {} }.to change(described_class, :instances).from([]).to([kind_of(Honeybadger::Dependency)])
18
+ end
19
+ end
20
+
21
+ describe ".inject!" do
22
+ it "injects all satisfied instances" do
23
+ Honeybadger::Dependency.instances.replace([mock_dependency, mock_dependency])
24
+ Honeybadger::Dependency.inject!
25
+ end
26
+
27
+ it "skips all unsatisfied instances" do
28
+ Honeybadger::Dependency.instances.replace([mock_dependency(false), mock_dependency(false)])
29
+ Honeybadger::Dependency.inject!
30
+ end
31
+ end
32
+
33
+ describe "#requirement" do
34
+ let(:block) { Proc.new {} }
35
+
36
+ it "returns and Array of requirements" do
37
+ expect(subject.requirement(&block)).to eq [block]
38
+ end
39
+
40
+ it "registers a new requirement" do
41
+ expect { subject.requirement(&block) }.to change(subject, :requirements).from([]).to([block])
42
+ end
43
+ end
44
+
45
+ describe "#injection" do
46
+ let(:block) { Proc.new {} }
47
+
48
+ it "returns an Array of injections" do
49
+ expect(subject.injection(&block)).to eq [block]
50
+ end
51
+
52
+ it "registers a new injection" do
53
+ expect { subject.injection(&block) }.to change(subject, :injections).from([]).to([block])
54
+ end
55
+ end
56
+
57
+ describe "#ok?" do
58
+ subject { dependency.ok? }
59
+
60
+ context "when not injected yet" do
61
+ it { should be_true }
62
+ end
63
+
64
+ context "when already injected" do
65
+ before { dependency.inject! }
66
+
67
+ it { should be_false }
68
+ end
69
+
70
+ context "all requirements are met" do
71
+ before do
72
+ 3.times { dependency.requirement { true } }
73
+ end
74
+ end
75
+
76
+ context "some requirements fail" do
77
+ before do
78
+ 3.times { dependency.requirement { true } }
79
+ dependency.requirement { false }
80
+ end
81
+
82
+ it { should be_false }
83
+ end
84
+
85
+ context "some requirements error" do
86
+ before do
87
+ dependency.requirement { true }
88
+ dependency.requirement { fail 'oops!' }
89
+ end
90
+
91
+ it { should be_false }
92
+
93
+ it "logs the failure" do
94
+ Honeybadger.should_receive(:write_verbose_log).with(/oops!/, :error).once
95
+ dependency.ok?
96
+ end
97
+ end
98
+ end
99
+
100
+ describe "#inject!" do
101
+ it "calls injections" do
102
+ dependency.injections.replace([mock_injection, mock_injection])
103
+ dependency.inject!
104
+ end
105
+
106
+ context "some injections fail" do
107
+ before do
108
+ failing_injection = Proc.new { fail 'oh noes!' }
109
+ dependency.injections.replace([mock_injection, failing_injection, mock_injection(false)])
110
+ end
111
+
112
+ it "halts injection silently" do
113
+ expect { dependency.inject! }.not_to raise_error
114
+ end
115
+
116
+ it "logs the failure" do
117
+ Honeybadger.should_receive(:write_verbose_log).with(/oh noes!/, :error).once
118
+ dependency.inject!
119
+ end
120
+
121
+ it "marks the dependency as injected" do
122
+ expect { dependency.inject!}.to change(dependency, :injected?).from(false).to(true)
123
+ end
124
+ end
125
+ end
126
+
127
+ def mock_dependency(ok = true)
128
+ double(:ok? => ok).tap { |d| d.send(ok ? :should_receive : :should_not_receive, :inject!) }
129
+ end
130
+
131
+ def mock_injection(positive = true)
132
+ double().tap { |d| d.send(positive ? :should_receive : :should_not_receive, :call) }
133
+ end
134
+ end