honeybadger 1.3.1 → 1.4.0

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 (53) hide show
  1. data/Appraisals +20 -0
  2. data/Gemfile +1 -13
  3. data/Gemfile.lock +39 -64
  4. data/MIT-LICENSE +5 -0
  5. data/README.md +102 -3
  6. data/Rakefile +9 -134
  7. data/features/metal.feature +6 -4
  8. data/features/rack.feature +3 -4
  9. data/features/rails.feature +106 -99
  10. data/features/rake.feature +0 -2
  11. data/features/sinatra.feature +1 -3
  12. data/features/step_definitions/metal_steps.rb +2 -1
  13. data/features/step_definitions/rack_steps.rb +2 -2
  14. data/features/step_definitions/rails_steps.rb +238 -0
  15. data/features/step_definitions/rake_steps.rb +1 -1
  16. data/features/support/env.rb +7 -7
  17. data/features/support/rails.rb +41 -58
  18. data/gemfiles/rails2.3.gemfile +9 -0
  19. data/gemfiles/rails2.3.gemfile.lock +102 -0
  20. data/gemfiles/rails3.0.gemfile +8 -0
  21. data/gemfiles/rails3.0.gemfile.lock +146 -0
  22. data/gemfiles/rails3.1.gemfile +8 -0
  23. data/gemfiles/rails3.1.gemfile.lock +161 -0
  24. data/gemfiles/rails3.2.gemfile +8 -0
  25. data/gemfiles/rails3.2.gemfile.lock +160 -0
  26. data/honeybadger.gemspec +30 -24
  27. data/lib/honeybadger.rb +117 -113
  28. data/lib/honeybadger/backtrace.rb +9 -3
  29. data/lib/honeybadger/configuration.rb +22 -0
  30. data/lib/honeybadger/notice.rb +9 -0
  31. data/lib/honeybadger/rack.rb +5 -4
  32. data/lib/honeybadger/rails3_tasks.rb +20 -23
  33. data/lib/honeybadger/sender.rb +10 -4
  34. data/lib/honeybadger/shared_tasks.rb +12 -1
  35. data/lib/honeybadger/tasks.rb +6 -1
  36. data/test/test_helper.rb +71 -71
  37. data/test/unit/backtrace_test.rb +26 -23
  38. data/test/unit/capistrano_test.rb +1 -1
  39. data/test/unit/configuration_test.rb +19 -3
  40. data/test/unit/honeybadger_tasks_test.rb +1 -1
  41. data/test/unit/logger_test.rb +1 -1
  42. data/test/unit/notice_test.rb +71 -16
  43. data/test/unit/notifier_test.rb +25 -5
  44. data/test/unit/rack_test.rb +21 -1
  45. data/test/unit/rails/action_controller_catcher_test.rb +1 -1
  46. data/test/unit/rails_test.rb +1 -1
  47. data/test/unit/sender_test.rb +1 -1
  48. metadata +69 -45
  49. data/SUPPORTED_RAILS_VERSIONS +0 -37
  50. data/TESTING.md +0 -33
  51. data/features/step_definitions/file_steps.rb +0 -10
  52. data/features/step_definitions/rails_application_steps.rb +0 -394
  53. data/features/support/terminal.rb +0 -107
@@ -4,7 +4,7 @@ module Honeybadger
4
4
 
5
5
  # Public: Handles backtrace parsing line by line
6
6
  class Line
7
- # regexp (optionnally allowing leading X: for windows support)
7
+ # regexp (optionally allowing leading X: for windows support)
8
8
  INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}.freeze
9
9
 
10
10
  # Public: The file portion of the line (such as app/models/user.rb)
@@ -62,6 +62,11 @@ module Honeybadger
62
62
  "<Line:#{to_s}>"
63
63
  end
64
64
 
65
+ # Public: Determines if this line is part of the application trace or not
66
+ def application?
67
+ (filtered_file =~ /^\[PROJECT_ROOT\]/i) && !(filtered_file =~ /^\[PROJECT_ROOT\]\/vendor/i)
68
+ end
69
+
65
70
  # Public: An excerpt from the source file, lazily loaded to preserve
66
71
  # performance
67
72
  def source(radius = 2)
@@ -94,7 +99,7 @@ module Honeybadger
94
99
  end
95
100
 
96
101
  # Public: holder for an Array of Backtrace::Line instances
97
- attr_reader :lines
102
+ attr_reader :lines, :application_lines
98
103
 
99
104
  def self.parse(ruby_backtrace, opts = {})
100
105
  ruby_lines = split_multiline_backtrace(ruby_backtrace)
@@ -108,6 +113,7 @@ module Honeybadger
108
113
 
109
114
  def initialize(lines)
110
115
  self.lines = lines
116
+ self.application_lines = lines.select(&:application?)
111
117
  end
112
118
 
113
119
  # Public
@@ -146,7 +152,7 @@ module Honeybadger
146
152
 
147
153
  private
148
154
 
149
- attr_writer :lines
155
+ attr_writer :lines, :application_lines
150
156
 
151
157
  def self.split_multiline_backtrace(backtrace)
152
158
  if backtrace.to_a.size == 1
@@ -91,6 +91,9 @@ module Honeybadger
91
91
  # The radius around trace line to include in source excerpt
92
92
  attr_accessor :source_extract_radius
93
93
 
94
+ # A Proc object used to send notices asynchronously
95
+ attr_writer :async
96
+
94
97
  DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
95
98
 
96
99
  DEFAULT_BACKTRACE_FILTERS = [
@@ -231,6 +234,25 @@ module Honeybadger
231
234
  !development_environments.include?(environment_name)
232
235
  end
233
236
 
237
+ # Public: Configure async delivery
238
+ #
239
+ # block - An optional block containing an async handler
240
+ #
241
+ # Examples
242
+ #
243
+ # config.async = Proc.new { |notice| Thread.new { notice.deliver } }
244
+ #
245
+ # config.async do |notice|
246
+ # Thread.new { notice.deliver }
247
+ # end
248
+ #
249
+ # Returns configured async handler (should respond to #call(notice))
250
+ def async
251
+ @async = Proc.new if block_given?
252
+ @async
253
+ end
254
+ alias :async? :async
255
+
234
256
  def port
235
257
  @port || default_port
236
258
  end
@@ -114,6 +114,13 @@ module Honeybadger
114
114
  set_context
115
115
  end
116
116
 
117
+ # Public: Send the notice to Honeybadger using the configured sender
118
+ #
119
+ # Returns a reference to the error in Honeybadger
120
+ def deliver
121
+ Honeybadger.sender.send_to_honeybadger(to_json)
122
+ end
123
+
117
124
  # Public: Template used to create JSON payload
118
125
  #
119
126
  # Returns JSON representation of notice
@@ -274,6 +281,8 @@ module Honeybadger
274
281
  parts = line.split(': ')
275
282
  [parts[0].strip, parts[1] || '']
276
283
  end]
284
+ elsif backtrace.application_lines.any?
285
+ backtrace.application_lines.first.source(source_extract_radius)
277
286
  else
278
287
  backtrace.lines.first.source(source_extract_radius)
279
288
  end
@@ -34,21 +34,22 @@ module Honeybadger
34
34
  end
35
35
 
36
36
  def notify_honeybadger(exception,env)
37
- Honeybadger.notify_or_ignore(exception,:rack_env => env) unless ignored_user_agent?(env)
37
+ Honeybadger.notify_or_ignore(exception, :rack_env => env) unless ignored_user_agent?(env)
38
38
  end
39
39
 
40
40
  def call(env)
41
41
  begin
42
42
  response = @app.call(env)
43
43
  rescue Exception => raised
44
- env['honeybadger.error_id'] = notify_honeybadger(raised,env)
44
+ env['honeybadger.error_id'] = notify_honeybadger(raised, env)
45
45
  raise
46
46
  ensure
47
47
  Honeybadger.context.clear!
48
48
  end
49
49
 
50
- if env['rack.exception']
51
- env['honeybadger.error_id'] = notify_honeybadger(env['rack.exception'],env)
50
+ framework_exception = env['rack.exception'] || env['sinatra.error']
51
+ if framework_exception
52
+ env['honeybadger.error_id'] = notify_honeybadger(framework_exception, env)
52
53
  end
53
54
 
54
55
  response
@@ -3,16 +3,26 @@ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
3
 
4
4
  namespace :honeybadger do
5
5
  desc "Verify your gem installation by sending a test exception to the honeybadger service"
6
- task :test => [:environment] do
7
- Rails.logger = defined?(ActiveSupport::TaggedLogging) ?
8
- ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) :
9
- Logger.new(STDOUT)
6
+ task :test => :environment do
7
+ Rails.logger = if defined?(ActiveSupport::TaggedLogging)
8
+ ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
9
+ else
10
+ Logger.new(STDOUT)
11
+ end
12
+ Rails.logger.level = Logger::INFO
10
13
 
11
- Rails.logger.level = Logger::DEBUG
12
14
  Honeybadger.configure(true) do |config|
13
15
  config.logger = Rails.logger
14
16
  end
15
17
 
18
+ # Suppress error logging in Rails' exception handling middleware. Rails 3.0
19
+ # uses ActionDispatch::ShowExceptions to rescue/show exceptions, but does
20
+ # not log anything but application trace. Rails 3.2 now falls back to
21
+ # logging the framework trace (moved to ActionDispatch::DebugExceptions),
22
+ # which caused cluttered output while running the test task.
23
+ class ActionDispatch::DebugExceptions ; def logger(*args) ; @logger ||= Logger.new('/dev/null') ; end ; end
24
+ class ActionDispatch::ShowExceptions ; def logger(*args) ; @logger ||= Logger.new('/dev/null') ; end ; end
25
+
16
26
  require './app/controllers/application_controller'
17
27
 
18
28
  class HoneybadgerTestingException < RuntimeError; end
@@ -22,6 +32,11 @@ namespace :honeybadger do
22
32
  exit
23
33
  end
24
34
 
35
+ if Honeybadger.configuration.async?
36
+ puts "Temporarily disabling asynchronous delivery"
37
+ Honeybadger.configuration.async = nil
38
+ end
39
+
25
40
  Honeybadger.configuration.development_environments = []
26
41
 
27
42
  puts "Configuration:"
@@ -43,35 +58,17 @@ namespace :honeybadger do
43
58
  raise exception_class.new, 'Testing honeybadger via "rake honeybadger:test". If you can see this, it works.'
44
59
  end
45
60
 
46
- # def rescue_action(exception)
47
- # rescue_action_in_public exception
48
- # end
49
-
50
61
  # Ensure we actually have an action to go to.
51
62
  def verify; end
52
63
 
53
- # def consider_all_requests_local
54
- # false
55
- # end
56
-
57
- # def local_request?
58
- # false
59
- # end
60
-
61
64
  def exception_class
62
65
  exception_name = ENV['EXCEPTION'] || "HoneybadgerTestingException"
63
66
  Object.const_get(exception_name)
64
67
  rescue
65
68
  Object.const_set(exception_name, Class.new(Exception))
66
69
  end
67
-
68
- def logger
69
- nil
70
- end
71
70
  end
72
- class HoneybadgerVerificationController < ApplicationController; end
73
71
 
74
- Rails.application.routes_reloader.execute_if_updated
75
72
  Rails.application.routes.draw do
76
73
  match 'verify' => 'application#verify', :as => 'verify'
77
74
  end
@@ -48,9 +48,9 @@ module Honeybadger
48
48
 
49
49
  case response
50
50
  when Net::HTTPSuccess then
51
- log(:info, "Success: #{response.class}", response)
51
+ log(:info, "Success: #{response.class}", response, data)
52
52
  else
53
- log(:error, "Failure: #{response.class}", response)
53
+ log(:error, "Failure: #{response.class}", response, data)
54
54
  end
55
55
 
56
56
  if response && response.respond_to?(:body)
@@ -84,8 +84,14 @@ module Honeybadger
84
84
  URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
85
85
  end
86
86
 
87
- def log(level, message, response = nil)
88
- logger.send(level, LOG_PREFIX + message) if logger
87
+ def log(level, message, response = nil, data = nil)
88
+ if logger
89
+ logger.send(level, LOG_PREFIX + message)
90
+
91
+ # Log the notice payload for debugging
92
+ logger.debug(LOG_PREFIX + "Notice: #{data}") if data
93
+ end
94
+
89
95
  Honeybadger.report_environment_info
90
96
  Honeybadger.report_response_body(response.body) if response && response.respond_to?(:body)
91
97
  end
@@ -1,7 +1,18 @@
1
1
  namespace :honeybadger do
2
2
  desc "Notify Honeybadger of a new deploy."
3
- task :deploy => :environment do
3
+ task :deploy do
4
4
  require 'honeybadger_tasks'
5
+
6
+ if defined?(Rails.root)
7
+ initializer_file = Rails.root.join('config', 'initializers','honeybadger.rb')
8
+
9
+ if initializer_file.exist?
10
+ load initializer_file
11
+ else
12
+ Rake::Task[:environment].invoke
13
+ end
14
+ end
15
+
5
16
  HoneybadgerTasks.deploy(:environment => ENV['TO'],
6
17
  :revision => ENV['REVISION'],
7
18
  :repository => ENV['REPO'],
@@ -4,7 +4,7 @@ require File.join(File.dirname(__FILE__), 'shared_tasks')
4
4
  namespace :honeybadger do
5
5
  desc "Verify your gem installation by sending a test exception to the honeybadger service"
6
6
  task :test => ['honeybadger:log_stdout', :environment] do
7
- RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
7
+ RAILS_DEFAULT_LOGGER.level = Logger::INFO
8
8
 
9
9
  require 'action_controller/test_process'
10
10
 
@@ -17,6 +17,11 @@ namespace :honeybadger do
17
17
  exit
18
18
  end
19
19
 
20
+ if Honeybadger.configuration.async?
21
+ puts "Temporarily disabling asynchronous delivery"
22
+ Honeybadger.configuration.async = nil
23
+ end
24
+
20
25
  Honeybadger.configuration.development_environments = []
21
26
 
22
27
  catcher = Honeybadger::Rails::ActionControllerCatcher
data/test/test_helper.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  require 'test/unit'
2
2
 
3
- require 'bundler/setup'
4
-
5
3
  require 'mocha'
6
4
  require 'shoulda'
7
5
  require 'bourne'
@@ -27,14 +25,12 @@ end
27
25
  module DefinesConstants
28
26
  def setup
29
27
  @defined_constants = []
30
- Honeybadger.context.clear!
31
28
  end
32
29
 
33
30
  def teardown
34
31
  @defined_constants.each do |constant|
35
32
  Object.__send__(:remove_const, constant)
36
33
  end
37
- Honeybadger.context.clear!
38
34
  end
39
35
 
40
36
  def define_constant(name, value)
@@ -55,91 +51,95 @@ class CollectingSender
55
51
  end
56
52
  end
57
53
 
58
- module Honeybadger
59
- class UnitTest < Test::Unit::TestCase
60
- def assert_no_difference(expression, message = nil, &block)
61
- assert_difference expression, 0, message, &block
62
- end
54
+ class Test::Unit::TestCase
55
+ def teardown
56
+ Honeybadger.context.clear!
57
+ end
63
58
 
64
- def stub_sender
65
- stub('sender', :send_to_honeybadger => nil)
66
- end
59
+ def assert_no_difference(expression, message = nil, &block)
60
+ assert_difference expression, 0, message, &block
61
+ end
67
62
 
68
- def stub_sender!
69
- Honeybadger.sender = stub_sender
70
- end
63
+ def stub_sender
64
+ stub('sender', :send_to_honeybadger => nil)
65
+ end
66
+
67
+ def stub_sender!
68
+ Honeybadger.sender = stub_sender
69
+ end
71
70
 
72
- def stub_notice
73
- stub('notice', :to_json => 'some yaml', :ignore? => false)
71
+ def stub_notice
72
+ Honeybadger::Notice.new({}).tap do |notice|
73
+ notice.stubs(:ignored? => false, :to_json => '{"foo":"bar"}')
74
74
  end
75
+ end
75
76
 
76
- def stub_notice!
77
- stub_notice.tap do |notice|
78
- Honeybadger::Notice.stubs(:new => notice)
79
- end
77
+ def stub_notice!
78
+ stub_notice.tap do |notice|
79
+ Honeybadger::Notice.stubs(:new => notice)
80
80
  end
81
+ end
81
82
 
82
- def reset_config
83
- Honeybadger.configuration = nil
84
- Honeybadger.configure do |config|
85
- config.api_key = 'abc123'
86
- end
83
+ def reset_config
84
+ Honeybadger.configuration = nil
85
+ Honeybadger.configure do |config|
86
+ config.api_key = 'abc123'
87
87
  end
88
+ end
88
89
 
89
- def build_notice_data(exception = nil)
90
- exception ||= build_exception
91
- {
92
- :api_key => 'abc123',
93
- :error_class => exception.class.name,
94
- :error_message => "#{exception.class.name}: #{exception.message}",
95
- :backtrace => exception.backtrace,
96
- :environment => { 'PATH' => '/bin', 'REQUEST_URI' => '/users/1' },
97
- :request => {
98
- :params => { 'controller' => 'users', 'action' => 'show', 'id' => '1' },
99
- :rails_root => '/path/to/application',
100
- :url => "http://test.host/users/1"
101
- },
102
- :session => {
103
- :key => '123abc',
104
- :data => { 'user_id' => '5', 'flash' => { 'notice' => 'Logged in successfully' } }
105
- }
90
+ def build_notice_data(exception = nil)
91
+ exception ||= build_exception
92
+ {
93
+ :api_key => 'abc123',
94
+ :error_class => exception.class.name,
95
+ :error_message => "#{exception.class.name}: #{exception.message}",
96
+ :backtrace => exception.backtrace,
97
+ :environment => { 'PATH' => '/bin', 'REQUEST_URI' => '/users/1' },
98
+ :request => {
99
+ :params => { 'controller' => 'users', 'action' => 'show', 'id' => '1' },
100
+ :rails_root => '/path/to/application',
101
+ :url => "http://test.host/users/1"
102
+ },
103
+ :session => {
104
+ :key => '123abc',
105
+ :data => { 'user_id' => '5', 'flash' => { 'notice' => 'Logged in successfully' } }
106
106
  }
107
- end
107
+ }
108
+ end
108
109
 
109
- def build_exception(opts = {})
110
- backtrace = ["test/honeybadger/rack_test.rb:15:in `build_exception'",
111
- "test/honeybadger/rack_test.rb:52:in `test_delivers_exception_from_rack'",
112
- "/Users/josh/Developer/.rvm/gems/ruby-1.9.3-p0/gems/mocha-0.10.5/lib/mocha/integration/mini_test/version_230_to_262.rb:28:in `run'"]
113
- opts = { :backtrace => backtrace }.merge(opts)
114
- BacktracedException.new(opts)
115
- end
110
+ def build_exception(opts = {})
111
+ backtrace = ["test/honeybadger/rack_test.rb:15:in `build_exception'",
112
+ "test/honeybadger/rack_test.rb:52:in `test_delivers_exception_from_rack'",
113
+ "/Users/josh/Developer/.rvm/gems/ruby-1.9.3-p0/gems/mocha-0.10.5/lib/mocha/integration/mini_test/version_230_to_262.rb:28:in `run'"]
114
+ opts = { :backtrace => backtrace }.merge(opts)
115
+ BacktracedException.new(opts)
116
+ end
116
117
 
117
- def assert_array_starts_with(expected, actual)
118
- assert_respond_to actual, :to_ary
119
- array = actual.to_ary.reverse
120
- expected.reverse.each_with_index do |value, i|
121
- assert_equal value, array[i]
122
- end
118
+ def assert_array_starts_with(expected, actual)
119
+ assert_respond_to actual, :to_ary
120
+ array = actual.to_ary.reverse
121
+ expected.reverse.each_with_index do |value, i|
122
+ assert_equal value, array[i]
123
123
  end
124
+ end
124
125
 
125
- def assert_logged(expected)
126
- assert_received(Honeybadger, :write_verbose_log) do |expect|
127
- expect.with {|actual| actual =~ expected }
128
- end
126
+ def assert_logged(expected)
127
+ assert_received(Honeybadger, :write_verbose_log) do |expect|
128
+ expect.with {|actual| actual =~ expected }
129
129
  end
130
+ end
130
131
 
131
- def assert_not_logged(expected)
132
- assert_received(Honeybadger, :write_verbose_log) do |expect|
133
- expect.with {|actual| actual =~ expected }.never
134
- end
132
+ def assert_not_logged(expected)
133
+ assert_received(Honeybadger, :write_verbose_log) do |expect|
134
+ expect.with {|actual| actual =~ expected }.never
135
135
  end
136
+ end
136
137
 
137
- def assert_caught_and_sent
138
- assert !Honeybadger.sender.collected.empty?
139
- end
138
+ def assert_caught_and_sent
139
+ assert !Honeybadger.sender.collected.empty?
140
+ end
140
141
 
141
- def assert_caught_and_not_sent
142
- assert Honeybadger.sender.collected.empty?
143
- end
142
+ def assert_caught_and_not_sent
143
+ assert Honeybadger.sender.collected.empty?
144
144
  end
145
145
  end