honeybadger 1.9.5 → 1.10.0.beta1

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