honeybadger 1.11.2 → 1.12.0.beta2

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 (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