honeybadger 1.9.5 → 1.10.0.beta1

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 (46) hide show
  1. data/Appraisals +0 -12
  2. data/CHANGELOG.md +19 -5
  3. data/Gemfile.lock +1 -5
  4. data/MIT-LICENSE +6 -5
  5. data/README.md +6 -596
  6. data/Rakefile +5 -5
  7. data/features/rails.feature +37 -12
  8. data/features/step_definitions/rails_steps.rb +20 -0
  9. data/features/support/honeybadger_shim.rb.template +3 -5
  10. data/gemfiles/rack.gemfile +0 -1
  11. data/gemfiles/rack.gemfile.lock +1 -8
  12. data/gemfiles/rails2.3.gemfile +0 -1
  13. data/gemfiles/rails2.3.gemfile.lock +1 -8
  14. data/gemfiles/rails3.0.gemfile +0 -2
  15. data/gemfiles/rails3.0.gemfile.lock +2 -12
  16. data/gemfiles/rails3.1.gemfile +0 -2
  17. data/gemfiles/rails3.1.gemfile.lock +3 -13
  18. data/gemfiles/rails3.2.gemfile +0 -2
  19. data/gemfiles/rails3.2.gemfile.lock +4 -14
  20. data/gemfiles/rails4.gemfile +0 -2
  21. data/gemfiles/rails4.gemfile.lock +4 -14
  22. data/gemfiles/rake.gemfile +0 -1
  23. data/gemfiles/rake.gemfile.lock +1 -8
  24. data/gemfiles/sinatra.gemfile +0 -1
  25. data/gemfiles/sinatra.gemfile.lock +1 -8
  26. data/honeybadger.gemspec +7 -3
  27. data/lib/honeybadger.rb +7 -5
  28. data/lib/honeybadger/configuration.rb +7 -3
  29. data/lib/honeybadger/monitor/sender.rb +4 -6
  30. data/lib/honeybadger/notice.rb +30 -4
  31. data/lib/honeybadger/rails.rb +2 -0
  32. data/lib/honeybadger/rails3_tasks.rb +2 -2
  33. data/lib/honeybadger/railtie.rb +3 -1
  34. data/lib/honeybadger/sender.rb +63 -53
  35. data/lib/honeybadger/templates/feedback_form.html.erb +82 -0
  36. data/lib/honeybadger/user_feedback.rb +42 -0
  37. data/lib/honeybadger/user_informer.rb +26 -0
  38. data/spec/honeybadger/configuration_spec.rb +4 -1
  39. data/spec/honeybadger/logger_spec.rb +25 -2
  40. data/spec/honeybadger/notice_spec.rb +69 -3
  41. data/spec/honeybadger/sender_spec.rb +77 -54
  42. data/spec/honeybadger/user_feedback_spec.rb +55 -0
  43. data/spec/honeybadger/user_informer_spec.rb +30 -0
  44. data/spec/support/helpers.rb +9 -6
  45. metadata +63 -39
  46. checksums.yaml +0 -7
@@ -4,6 +4,5 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "sinatra"
6
6
  gem "honeybadger", :path=>"../"
7
- gem "faraday", "~> 0.7.0"
8
7
 
9
8
  gemspec :path=>"../"
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/josh/code/honeybadger-ruby
3
3
  specs:
4
- honeybadger (1.9.5)
5
- faraday (~> 0.7)
4
+ honeybadger (1.10.0.beta1)
6
5
  json
7
6
 
8
7
  GEM
@@ -34,10 +33,6 @@ GEM
34
33
  gherkin (~> 2.11.7)
35
34
  multi_json (~> 1.3)
36
35
  diff-lcs (1.2.4)
37
- faraday (0.7.6)
38
- addressable (~> 2.2)
39
- multipart-post (~> 1.1)
40
- rack (~> 1.1)
41
36
  ffi (1.9.3)
42
37
  formatador (0.2.4)
43
38
  fuubar (1.2.1)
@@ -65,7 +60,6 @@ GEM
65
60
  lumberjack (1.0.4)
66
61
  method_source (0.8.2)
67
62
  multi_json (1.8.2)
68
- multipart-post (1.2.0)
69
63
  net-scp (1.1.2)
70
64
  net-ssh (>= 2.6.5)
71
65
  net-sftp (2.1.2)
@@ -118,7 +112,6 @@ DEPENDENCIES
118
112
  aruba
119
113
  capistrano (~> 2.0)
120
114
  cucumber (~> 1.2.1)
121
- faraday (~> 0.7.0)
122
115
  fuubar
123
116
  growl
124
117
  guard (~> 1.8.3)
@@ -4,6 +4,5 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "sinatra"
6
6
  gem "honeybadger", :path=>"../"
7
- gem "faraday", "~> 0.7.0"
8
7
 
9
8
  gemspec :path=>"../"
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/josh/code/honeybadger-ruby
3
3
  specs:
4
- honeybadger (1.9.5)
5
- faraday (~> 0.7)
4
+ honeybadger (1.10.0.beta1)
6
5
  json
7
6
 
8
7
  GEM
@@ -34,10 +33,6 @@ GEM
34
33
  gherkin (~> 2.11.7)
35
34
  multi_json (~> 1.3)
36
35
  diff-lcs (1.2.4)
37
- faraday (0.7.6)
38
- addressable (~> 2.2)
39
- multipart-post (~> 1.1)
40
- rack (~> 1.1)
41
36
  ffi (1.9.3)
42
37
  formatador (0.2.4)
43
38
  fuubar (1.2.1)
@@ -65,7 +60,6 @@ GEM
65
60
  lumberjack (1.0.4)
66
61
  method_source (0.8.2)
67
62
  multi_json (1.8.2)
68
- multipart-post (1.2.0)
69
63
  net-scp (1.1.2)
70
64
  net-ssh (>= 2.6.5)
71
65
  net-sftp (2.1.2)
@@ -118,7 +112,6 @@ DEPENDENCIES
118
112
  aruba
119
113
  capistrano (~> 2.0)
120
114
  cucumber (~> 1.2.1)
121
- faraday (~> 0.7.0)
122
115
  fuubar
123
116
  growl
124
117
  guard (~> 1.8.3)
data/honeybadger.gemspec CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
4
4
  s.rubygems_version = '1.3.5'
5
5
 
6
6
  s.name = 'honeybadger'
7
- s.version = '1.9.5'
8
- s.date = '2013-12-06'
7
+ s.version = '1.10.0.beta1'
8
+ s.date = '2013-12-04'
9
9
 
10
10
  s.summary = 'Error reports you can be happy about.'
11
11
  s.description = 'Make managing application errors a more pleasant experience.'
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.rdoc_options = ['--charset=UTF-8', '--markup tomdoc']
20
20
  s.extra_rdoc_files = %w[README.md MIT-LICENSE]
21
21
 
22
- s.add_dependency('faraday', '~> 0.7')
23
22
  s.add_dependency('json')
24
23
 
25
24
  s.add_development_dependency('cucumber', '~> 1.2.1')
@@ -108,6 +107,9 @@ Gem::Specification.new do |s|
108
107
  lib/honeybadger/shared_tasks.rb
109
108
  lib/honeybadger/stats.rb
110
109
  lib/honeybadger/tasks.rb
110
+ lib/honeybadger/templates/feedback_form.html.erb
111
+ lib/honeybadger/user_feedback.rb
112
+ lib/honeybadger/user_informer.rb
111
113
  lib/honeybadger_tasks.rb
112
114
  lib/rails/generators/honeybadger/honeybadger_generator.rb
113
115
  rails/init.rb
@@ -125,6 +127,8 @@ Gem::Specification.new do |s|
125
127
  spec/honeybadger/rails/action_controller_spec.rb
126
128
  spec/honeybadger/rails_spec.rb
127
129
  spec/honeybadger/sender_spec.rb
130
+ spec/honeybadger/user_feedback_spec.rb
131
+ spec/honeybadger/user_informer_spec.rb
128
132
  spec/honeybadger_tasks_spec.rb
129
133
  spec/spec_helper.rb
130
134
  spec/support/array_including.rb
data/lib/honeybadger.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'faraday'
2
- require 'timeout'
1
+ require 'net/http'
2
+ require 'net/https'
3
3
  require 'json'
4
4
  require 'digest'
5
5
  require 'logger'
@@ -10,16 +10,18 @@ require 'honeybadger/notice'
10
10
  require 'honeybadger/rack'
11
11
  require 'honeybadger/sender'
12
12
  require 'honeybadger/stats'
13
+ require 'honeybadger/user_informer'
14
+ require 'honeybadger/user_feedback'
13
15
 
14
16
  require 'honeybadger/railtie' if defined?(Rails::Railtie)
15
17
 
16
18
  module Honeybadger
17
- VERSION = '1.9.5'
19
+ VERSION = '1.10.0.beta1'
18
20
  LOG_PREFIX = "** [Honeybadger] "
19
21
 
20
22
  HEADERS = {
21
- 'Content-type' => 'application/json',
22
- 'Accept' => 'text/json, application/json'
23
+ 'Content-type' => 'application/json',
24
+ 'Accept' => 'text/json, application/json'
23
25
  }
24
26
 
25
27
  class << self
@@ -7,8 +7,8 @@ module Honeybadger
7
7
  :ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
8
8
  :params_filters, :project_root, :port, :protocol, :proxy_host, :proxy_pass,
9
9
  :proxy_port, :proxy_user, :secure, :use_system_ssl_cert_chain, :framework,
10
- :user_information, :rescue_rake_exceptions, :source_extract_radius,
11
- :send_request_session, :debug, :fingerprint, :hostname].freeze
10
+ :user_information, :feedback, :rescue_rake_exceptions, :source_extract_radius,
11
+ :send_request_session, :debug, :fingerprint, :hostname, :metrics].freeze
12
12
 
13
13
  # The API key for your project, found on the project edit form.
14
14
  attr_accessor :api_key
@@ -84,7 +84,10 @@ module Honeybadger
84
84
  # The text that the placeholder is replaced with. {{error_id}} is the actual error number.
85
85
  attr_accessor :user_information
86
86
 
87
- # The framework Honeybadger is configured to use
87
+ # Display user feedback form when configured?
88
+ attr_accessor :feedback
89
+
90
+ # The framework Honeybadger is configured to use.
88
91
  attr_accessor :framework
89
92
 
90
93
  # Should Honeybadger catch exceptions from Rake tasks?
@@ -173,6 +176,7 @@ module Honeybadger
173
176
  @metrics = true
174
177
  @features = { 'notices' => true }
175
178
  @limit = nil
179
+ @feedback = true
176
180
  end
177
181
 
178
182
  # Public: Takes a block and adds it to the list of backtrace filters. When
@@ -10,19 +10,17 @@ module Honeybadger
10
10
  return nil
11
11
  end
12
12
 
13
- response = client.post do |p|
14
- p.url "/v1/metrics"
15
- p.body = data.to_json
13
+ response = rescue_http_errors do
14
+ http_connection.post('/v1/metrics', data.to_json, http_headers)
16
15
  end
17
16
 
18
- if response.success?
17
+ if Net::HTTPSuccess === response
19
18
  true
20
19
  else
21
- Honeybadger.configuration.features['metrics'] = false if response.status == 403
20
+ Honeybadger.configuration.features['metrics'] = false if Net::HTTPForbidden === response
22
21
  log(:error, "Metrics Failure", response, data)
23
22
  false
24
23
  end
25
-
26
24
  rescue => e
27
25
  log(:error, "[Honeybadger::Monitor::Sender#send_metrics] Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
28
26
  true
@@ -82,11 +82,13 @@ module Honeybadger
82
82
  # System stats
83
83
  attr_reader :stats
84
84
 
85
+ # The api_key to use when sending notice (optional)
86
+ attr_reader :api_key
87
+
85
88
  def initialize(args)
86
89
  self.args = args
87
90
  self.exception = args[:exception]
88
91
  self.project_root = args[:project_root]
89
- self.url = args[:url] || rack_env(:url)
90
92
 
91
93
  self.notifier_name = args[:notifier_name]
92
94
  self.notifier_version = args[:notifier_version]
@@ -114,8 +116,10 @@ module Honeybadger
114
116
  end
115
117
  end
116
118
 
119
+ self.url = filter_url(args[:url] || rack_env(:url))
117
120
  self.hostname = local_hostname
118
121
  self.stats = Stats.all
122
+ self.api_key = args[:api_key]
119
123
 
120
124
  self.source_extract_radius = args[:source_extract_radius] || 2
121
125
  self.source_extract = extract_source_from_backtrace
@@ -143,6 +147,7 @@ module Honeybadger
143
147
  # Returns JSON representation of notice
144
148
  def as_json(options = {})
145
149
  {
150
+ :api_key => api_key,
146
151
  :notifier => {
147
152
  :name => notifier_name,
148
153
  :url => notifier_url,
@@ -231,7 +236,7 @@ module Honeybadger
231
236
  :environment_filters, :session_data, :project_root, :url, :ignore,
232
237
  :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version,
233
238
  :component, :action, :cgi_data, :environment_name, :hostname, :stats, :context,
234
- :source_extract, :source_extract_radius, :send_request_session
239
+ :source_extract, :source_extract_radius, :send_request_session, :api_key
235
240
 
236
241
  # Private: Arguments given in the initializer
237
242
  attr_accessor :args
@@ -291,7 +296,24 @@ module Honeybadger
291
296
  end
292
297
  end
293
298
 
294
- # Private: Replaces the contents of params that match params_filters.
299
+ # Internal: Filters query parameters from URL
300
+ #
301
+ # url - String URL to filter
302
+ #
303
+ # Returns filtered String URL
304
+ def filter_url(url)
305
+ return nil unless url =~ /\S/
306
+
307
+ url = url.dup
308
+ url.scan(/&([^=]+)=([^&]+)/).each do |m|
309
+ next unless filter_key?(m[0])
310
+ url.gsub!(/#{m[1]}/, '[FILTERED]')
311
+ end
312
+
313
+ url
314
+ end
315
+
316
+ # Internal: Replaces the contents of params that match params_filters.
295
317
  # TODO: extract this to a different class
296
318
  def clean_params
297
319
  clean_unserializable_data_from(:parameters)
@@ -360,7 +382,11 @@ module Honeybadger
360
382
 
361
383
  def filter_key?(key)
362
384
  params_filters.any? do |filter|
363
- key.to_s.eql?(filter.to_s)
385
+ if filter.is_a?(Regexp)
386
+ key.to_s =~ filter
387
+ else
388
+ key.to_s.eql?(filter.to_s)
389
+ end
364
390
  end
365
391
  end
366
392
 
@@ -19,6 +19,8 @@ module Honeybadger
19
19
  if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware)
20
20
  ::Rails.configuration.middleware.insert_after 'ActionController::Failsafe',
21
21
  Honeybadger::Rack
22
+ ::Rails.configuration.middleware.insert_after 'Rack::Lock',
23
+ Honeybadger::UserInformer
22
24
  end
23
25
 
24
26
  Honeybadger.configure(true) do |config|
@@ -29,7 +29,7 @@ namespace :honeybadger do
29
29
  # Detect and disable the better_errors gem
30
30
  if defined? BetterErrors::Middleware
31
31
  puts 'Better Errors detected: temporarily disabling middleware.'
32
- class BetterErrors::Middleware ; def call(env) @app.call(env); end ; end
32
+ class BetterErrors::Middleware ; def call(env) ; end ; end
33
33
  end
34
34
 
35
35
  begin
@@ -87,7 +87,7 @@ namespace :honeybadger do
87
87
  puts 'Processing request.'
88
88
 
89
89
  ssl = defined?(Rails.configuration.force_ssl) && Rails.configuration.force_ssl
90
- env = Rack::MockRequest.env_for("http#{ ssl ? 's' : nil }://www.example.com/verify", 'REMOTE_ADDR' => '127.0.0.1')
90
+ env = Rack::MockRequest.env_for("http#{ ssl ? 's' : nil }://www.example.com/verify")
91
91
 
92
92
  Rails.application.call(env)
93
93
  end
@@ -10,7 +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::Rack"
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"
14
16
  end
15
17
 
16
18
  config.after_initialize do
@@ -5,6 +5,9 @@ module Honeybadger
5
5
  Errno::EINVAL,
6
6
  Errno::ECONNRESET,
7
7
  EOFError,
8
+ Net::HTTPBadResponse,
9
+ Net::HTTPHeaderSyntaxError,
10
+ Net::ProtocolError,
8
11
  Errno::ECONNREFUSED].freeze
9
12
 
10
13
  def initialize(options = {})
@@ -27,7 +30,7 @@ module Honeybadger
27
30
 
28
31
  # Public: Sends the notice data off to Honeybadger for processing.
29
32
  #
30
- # notice - The notice data to be sent (Hash or JSON string)
33
+ # notice - The notice to be sent (Notice, Hash or JSON string)
31
34
  #
32
35
  # Returns error id from successful response
33
36
  def send_to_honeybadger(notice)
@@ -36,27 +39,21 @@ module Honeybadger
36
39
  return nil
37
40
  end
38
41
 
39
- return nil unless api_key_ok?
42
+ api_key = api_key_ok?(!notice.is_a?(String) && notice['api_key']) or return nil
40
43
 
41
44
  data = notice.is_a?(String) ? notice : notice.to_json
42
45
 
43
- response = begin
44
- client.post do |p|
45
- p.url NOTICES_URI
46
- p.body = data
47
- end
48
- rescue *HTTP_ERRORS => e
49
- log(:error, "Unable to contact the Honeybadger server. HTTP Error=#{e}")
50
- nil
51
- end
52
-
53
- if response.success?
46
+ response = rescue_http_errors do
47
+ http_connection.post(url.path, data, http_headers({'X-API-Key' => api_key}))
48
+ end
49
+
50
+ if Net::HTTPSuccess === response
54
51
  log(Honeybadger.configuration.debug ? :info : :debug, "Success: #{response.class}", response, data)
55
52
  JSON.parse(response.body).fetch('id')
56
53
  else
57
54
  log(:error, "Failure: #{response.class}", response, data)
55
+ nil
58
56
  end
59
-
60
57
  rescue => e
61
58
  log(:error, "[Honeybadger::Sender#send_to_honeybadger] Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
62
59
  nil
@@ -65,18 +62,16 @@ module Honeybadger
65
62
  def ping(data = {})
66
63
  return nil unless api_key_ok?
67
64
 
68
- response = client.post do |p|
69
- p.url "/v1/ping"
70
- p.body = data.to_json
65
+ response = rescue_http_errors do
66
+ http_connection.post('/v1/ping/', data.to_json, http_headers)
71
67
  end
72
68
 
73
- if response.success?
69
+ if Net::HTTPSuccess === response
74
70
  JSON.parse(response.body)
75
71
  else
76
72
  log(:error, "Ping Failure", response, data)
77
73
  nil
78
74
  end
79
-
80
75
  rescue => e
81
76
  log(:error, "[Honeybadger::Sender#ping] Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
82
77
  nil
@@ -100,44 +95,18 @@ module Honeybadger
100
95
 
101
96
  private
102
97
 
103
- def api_key_ok?
104
- if api_key.nil? || api_key == ''
105
- log(:error, "API key not found.")
106
- return nil
107
- end
108
- true
98
+ def url
99
+ URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
109
100
  end
110
101
 
111
- def proxy_options
112
- if proxy_host
113
- { :uri => "#{protocol}://#{proxy_host}:#{proxy_port || port}", :user => proxy_user, :password => proxy_pass }
102
+ def api_key_ok?(api_key = nil)
103
+ api_key ||= self.api_key
104
+ unless api_key =~ /\S/
105
+ log(:error, "API key not found.")
106
+ return nil
114
107
  end
115
- end
116
108
 
117
- def request_options
118
- { :timeout => http_read_timeout, :open_timeout => http_open_timeout }
119
- end
120
-
121
- def client
122
- @client ||= Faraday.new(:request => request_options, :proxy => proxy_options).tap do |conn|
123
- conn.build do |builder|
124
- builder.adapter Faraday.default_adapter
125
- end
126
-
127
- conn.url_prefix = "#{protocol}://#{host}:#{port}"
128
- conn.headers['User-agent'] = "HB-Ruby #{Honeybadger::VERSION}; #{RUBY_VERSION}; #{RUBY_PLATFORM}"
129
- conn.headers['X-API-Key'] = api_key.to_s
130
- conn.headers['Content-Type'] = 'application/json'
131
- conn.headers['Accept'] = 'text/json, application/json'
132
-
133
- if secure?
134
- conn.ssl[:verify_mode] = OpenSSL::SSL::VERIFY_PEER
135
- conn.ssl[:ca_file] = Honeybadger.configuration.ca_bundle_path
136
- end
137
- end
138
- rescue => e
139
- log(:error, "[Honeybadger::Sender#client] Failure initializing the HTTP connection.\nError: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
140
- raise e
109
+ api_key
141
110
  end
142
111
 
143
112
  def log(level, message, response = nil, data = nil)
@@ -149,5 +118,46 @@ module Honeybadger
149
118
  Honeybadger.report_response_body(response.body) if response && response.body =~ /\S/
150
119
  Honeybadger.write_verbose_log("Notice: #{data}", :debug) if data && Honeybadger.configuration.debug
151
120
  end
121
+
122
+ def rescue_http_errors(&block)
123
+ yield
124
+ rescue *HTTP_ERRORS => e
125
+ log(:error, "Unable to contact the Honeybadger server. HTTP Error=#{e}")
126
+ nil
127
+ end
128
+
129
+ def http_connection
130
+ setup_http_connection
131
+ end
132
+
133
+ def http_headers(headers=nil)
134
+ {}.tap do |hash|
135
+ hash.merge!(HEADERS)
136
+ hash.merge!({'X-API-Key' => api_key})
137
+ hash.merge!(headers) if headers
138
+ end
139
+ end
140
+
141
+ def setup_http_connection
142
+ http_class = Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass)
143
+ http = http_class.new(url.host, url.port)
144
+
145
+ http.read_timeout = http_read_timeout
146
+ http.open_timeout = http_open_timeout
147
+
148
+ if secure?
149
+ http.use_ssl = true
150
+
151
+ http.ca_file = Honeybadger.configuration.ca_bundle_path
152
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
153
+ else
154
+ http.use_ssl = false
155
+ end
156
+
157
+ http
158
+ rescue => e
159
+ log(:error, "[Honeybadger::Sender#setup_http_connection] Failure initializing the HTTP connection.\nError: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}")
160
+ raise e
161
+ end
152
162
  end
153
163
  end