bugsnag 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 492b7ad630873a696cfe885ba27c95d652e8bcd2
4
- data.tar.gz: aabd629b69e8dbfaf6bd528e1e22e42f56cfdc1a
3
+ metadata.gz: ef55d682c90467747f2be118b83bd3a85b156b1d
4
+ data.tar.gz: 62d482b4c71148dfe0713ea33df2289bb1f5c1fb
5
5
  SHA512:
6
- metadata.gz: 91b142a6f7107413fc6b10a9764013d5c76181ca1e19fc655bad998a0d5ef26e30d5f6eae7ee11e1cc6c917a7dccf754e59e46afcb00565c6e246064014ada86
7
- data.tar.gz: 015f3dba4008ae2cdafdb8bc89ff103bf5072d3688f8ee98d9ce40f1e7f1d4b2f98837b20c91ce98775f784a2487c3eb814cd7f90b59b962186ab45a3e06d42a
6
+ metadata.gz: 4a6af3d51710c9f736aaa59c99031f5e5a21e2b7da41182183cb4f06f3ae38fd61ce6993c460f304e35cc9398c7447b8f415c1e00da86a2fc08d0f0a42b28b7a
7
+ data.tar.gz: 9c15c561756b5e4feda9c6b98910010f59850dad2788d5bd9ae7ba92d3181200855b54b4e60a9b6f53a0e80c075ee6fb2932b68ac5e71e5c68f70ff4d50ee595
data/CHANGELOG.md CHANGED
@@ -1,9 +1,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2.6.0
5
+ -----
6
+ - Collect and send snippets of source code to Bugsnag
7
+ - Fix resque integration
8
+ - Allow configuration of delivery method (from the default `:thread_queue` to `:synchronous`)
9
+ - Fix parameter filtering in rails 2
10
+ - Allow pathname in project root
11
+
4
12
  2.5.1
5
13
  -----
6
- - Send headers through
14
+ - Collect and send HTTP headers to bugsnag to help debugging
7
15
 
8
16
  2.5.0
9
17
  -----
data/README.md CHANGED
@@ -552,6 +552,16 @@ send your rack environment, set the `send_environment` option to `true`.
552
552
  config.send_environment = true
553
553
  ```
554
554
 
555
+ ###send_code
556
+
557
+ Bugsnag automatically sends a small snippet of the code that crashed to help you diagnose
558
+ even faster from within your dashboard. If you don't want to send this snippet you can
559
+ set the `send_code` option to `false`.
560
+
561
+ ```ruby
562
+ config.send_code = false
563
+ ```
564
+
555
565
  Bugsnag Middleware
556
566
  ------------------
557
567
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.5.1
1
+ 2.6.0
data/bugsnag.gemspec CHANGED
@@ -31,5 +31,6 @@ Gem::Specification.new do |s|
31
31
  s.add_development_dependency 'rspec'
32
32
  s.add_development_dependency 'rdoc'
33
33
  s.add_development_dependency 'pry'
34
+ s.add_development_dependency 'webmock'
34
35
  end
35
36
 
data/lib/bugsnag.rb CHANGED
@@ -1,13 +1,16 @@
1
1
  require "rubygems"
2
2
 
3
3
  require "bugsnag/version"
4
- require "bugsnag/queue"
5
4
  require "bugsnag/configuration"
6
5
  require "bugsnag/meta_data"
7
6
  require "bugsnag/notification"
8
7
  require "bugsnag/helpers"
9
8
  require "bugsnag/deploy"
10
9
 
10
+ require "bugsnag/delivery"
11
+ require "bugsnag/delivery/synchronous"
12
+ require "bugsnag/delivery/thread_queue"
13
+
11
14
  require "bugsnag/rack"
12
15
  require "bugsnag/railtie" if defined?(Rails::Railtie)
13
16
 
@@ -11,6 +11,7 @@ module Bugsnag
11
11
  attr_accessor :auto_notify
12
12
  attr_accessor :use_ssl
13
13
  attr_accessor :send_environment
14
+ attr_accessor :send_code
14
15
  attr_accessor :project_root
15
16
  attr_accessor :app_version
16
17
  attr_accessor :app_type
@@ -27,6 +28,7 @@ module Bugsnag
27
28
  attr_accessor :proxy_password
28
29
  attr_accessor :timeout
29
30
  attr_accessor :hostname
31
+ attr_accessor :delivery_method
30
32
 
31
33
  attr_writer :ignore_classes
32
34
 
@@ -50,16 +52,20 @@ module Bugsnag
50
52
 
51
53
  DEFAULT_IGNORE_USER_AGENTS = [].freeze
52
54
 
55
+ DEFAULT_DELIVERY_METHOD = :thread_queue
56
+
53
57
  def initialize
54
58
  # Set up the defaults
55
59
  self.auto_notify = true
56
60
  self.use_ssl = true
57
61
  self.send_environment = false
62
+ self.send_code = true
58
63
  self.params_filters = Set.new(DEFAULT_PARAMS_FILTERS)
59
64
  self.ignore_classes = Set.new(DEFAULT_IGNORE_CLASSES)
60
65
  self.ignore_user_agents = Set.new(DEFAULT_IGNORE_USER_AGENTS)
61
66
  self.endpoint = DEFAULT_ENDPOINT
62
67
  self.hostname = default_hostname
68
+ self.delivery_method = DEFAULT_DELIVERY_METHOD
63
69
 
64
70
  # Read the API key from the environment
65
71
  self.api_key = ENV["BUGSNAG_API_KEY"]
@@ -0,0 +1,18 @@
1
+ module Bugsnag
2
+ module Delivery
3
+ class << self
4
+ def register(name, delivery_method)
5
+ delivery_methods[name.to_sym] = delivery_method
6
+ end
7
+
8
+ def [](name)
9
+ delivery_methods[name.to_sym]
10
+ end
11
+
12
+ private
13
+ def delivery_methods
14
+ @delivery_methods ||= {}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module Bugsnag
2
+ module Delivery
3
+ class Synchronous
4
+ HEADERS = {"Content-Type" => "application/json"}
5
+ TIMEOUT = 5
6
+
7
+ class << self
8
+ def deliver(url, body)
9
+ begin
10
+ response = HTTParty.post(url, {:body => body, :headers => HEADERS, :timeout => TIMEOUT})
11
+ Bugsnag.debug("Notification to #{url} finished, response was #{response.code}, payload was #{body}")
12
+ rescue StandardError => e
13
+ # KLUDGE: Since we don't re-raise http exceptions, this breaks rspec
14
+ raise if e.class.to_s == "RSpec::Expectations::ExpectationNotMetError"
15
+
16
+ Bugsnag.warn("Notification to #{url} failed, #{e.inspect}")
17
+ Bugsnag.warn(e.backtrace)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Bugsnag::Delivery.register(:synchronous, Bugsnag::Delivery::Synchronous)
@@ -0,0 +1,51 @@
1
+ require "thread"
2
+
3
+ module Bugsnag
4
+ module Delivery
5
+ class ThreadQueue < Synchronous
6
+ MAX_OUTSTANDING_REQUESTS = 100
7
+ STOP = Object.new
8
+
9
+ class << self
10
+ def deliver(url, body)
11
+ if queue.length > MAX_OUTSTANDING_REQUESTS
12
+ Bugsnag.warn("Dropping notification, #{queue.length} outstanding requests")
13
+ return
14
+ end
15
+
16
+ # Add delivery to the worker thread
17
+ queue.push proc { super }
18
+
19
+ # Make sure the worker thread is started
20
+ ensure_worker_thread_started
21
+ end
22
+
23
+ private
24
+ def queue
25
+ @queue ||= Queue.new
26
+ end
27
+
28
+ def ensure_worker_thread_started
29
+ unless @worker_thread
30
+ @worker_thread = Thread.new do
31
+ while x = queue.pop
32
+ break if x == STOP
33
+ x.call
34
+ end
35
+ end
36
+
37
+ at_exit do
38
+ Bugsnag.warn("Waiting for #{queue.length} outstanding request(s)") unless queue.empty?
39
+ queue.push STOP
40
+ @worker_thread.join
41
+ end
42
+ end
43
+
44
+ @worker_thread
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ Bugsnag::Delivery.register(:thread_queue, Bugsnag::Delivery::ThreadQueue)
@@ -24,51 +24,23 @@ module Bugsnag
24
24
 
25
25
  CURRENT_PAYLOAD_VERSION = "2"
26
26
 
27
- # HTTParty settings
28
- headers "Content-Type" => "application/json"
29
- default_timeout 5
30
-
31
27
  attr_accessor :context
32
28
  attr_accessor :user
33
29
  attr_accessor :configuration
34
30
  attr_accessor :meta_data
35
31
 
36
- @queue = Bugsnag::Queue.new
37
-
38
32
  class << self
39
- def deliver_exception_payload(endpoint, payload)
40
- begin
41
- payload_string = Bugsnag::Helpers.dump_json(payload)
42
-
43
- # If the payload is going to be too long, we trim the hashes to send
44
- # a minimal payload instead
45
- if payload_string.length > 128000
46
- payload[:events].each {|e| e[:metaData] = Bugsnag::Helpers.reduce_hash_size(e[:metaData])}
47
- payload_string = Bugsnag::Helpers.dump_json(payload)
48
- end
49
-
50
- do_post(endpoint, payload_string)
33
+ def deliver_exception_payload(url, payload, configuration=Bugsnag.configuration, delivery_method=nil)
51
34
 
52
- rescue StandardError => e
53
- # KLUDGE: Since we don't re-raise http exceptions, this breaks rspec
54
- raise if e.class.to_s == "RSpec::Expectations::ExpectationNotMetError"
55
-
56
- Bugsnag.warn("Notification to #{endpoint} failed, #{e.inspect}")
57
- Bugsnag.warn(e.backtrace)
35
+ # If the payload is going to be too long, we trim the hashes to send
36
+ # a minimal payload instead
37
+ payload_string = Bugsnag::Helpers.dump_json(payload)
38
+ if payload_string.length > 128000
39
+ payload[:events].each {|e| e[:metaData] = Bugsnag::Helpers.reduce_hash_size(e[:metaData])}
40
+ payload_string = Bugsnag::Helpers.dump_json(payload)
58
41
  end
59
42
 
60
- end
61
-
62
- def do_post(endpoint, payload_string)
63
- @queue.push proc{
64
- begin
65
- response = post(endpoint, {:body => payload_string})
66
- Bugsnag.debug("Notification to #{endpoint} finished, response was #{response.code}, payload was #{payload_string}")
67
- rescue StandardError => e
68
- Bugsnag.warn("Notification to #{endpoint} failed, #{e.inspect}")
69
- Bugsnag.warn(e.backtrace)
70
- end
71
- }
43
+ Bugsnag::Delivery[delivery_method || configuration.delivery_method].deliver(url, payload_string)
72
44
  end
73
45
  end
74
46
 
@@ -93,6 +65,11 @@ module Bugsnag
93
65
  @overrides.delete :api_key
94
66
  end
95
67
 
68
+ if @overrides.key? :delivery_method
69
+ @delivery_method = @overrides[:delivery_method]
70
+ @overrides.delete :delivery_method
71
+ end
72
+
96
73
  # Unwrap exceptions
97
74
  @exceptions = []
98
75
 
@@ -272,7 +249,8 @@ module Bugsnag
272
249
  :events => [payload_event]
273
250
  }
274
251
 
275
- self.class.deliver_exception_payload(endpoint, payload)
252
+ # Deliver the payload
253
+ self.class.deliver_exception_payload(endpoint, payload, @configuration, @delivery_method)
276
254
  end
277
255
  end
278
256
 
@@ -391,6 +369,10 @@ module Bugsnag
391
369
  trace_hash[:inProject] = true if @configuration.project_root && file.match(/^#{@configuration.project_root}/) && !file.match(/vendor\//)
392
370
  trace_hash[:lineNumber] = line_str.to_i
393
371
 
372
+ if @configuration.send_code
373
+ trace_hash[:code] = code(file, trace_hash[:lineNumber])
374
+ end
375
+
394
376
  # Clean up the file path in the stacktrace
395
377
  if defined?(Bugsnag.configuration.project_root) && Bugsnag.configuration.project_root.to_s != ''
396
378
  file.sub!(/#{Bugsnag.configuration.project_root}\//, "")
@@ -413,5 +395,41 @@ module Bugsnag
413
395
  end
414
396
  end.compact
415
397
  end
398
+
399
+ def code(file, line_number, num_lines = 7)
400
+ code_hash = {}
401
+
402
+ from_line = [line_number - num_lines, 1].max
403
+
404
+ # Populate code hash with line numbers and code lines
405
+ File.open(file) do |f|
406
+ current_line_number = 0
407
+ f.each_line do |line|
408
+ current_line_number += 1
409
+
410
+ next if current_line_number < from_line
411
+
412
+ code_hash[current_line_number] = line[0...200].rstrip
413
+
414
+ break if code_hash.length >= ( num_lines * 1.5 ).ceil
415
+ end
416
+ end
417
+
418
+ while code_hash.length > num_lines
419
+ last_line = code_hash.keys.max
420
+ first_line = code_hash.keys.min
421
+
422
+ if (last_line - line_number) > (line_number - first_line)
423
+ code_hash.delete(last_line)
424
+ else
425
+ code_hash.delete(first_line)
426
+ end
427
+ end
428
+
429
+ code_hash
430
+ rescue
431
+ Bugsnag.warn("Error fetching code: #{$!.inspect}")
432
+ nil
433
+ end
416
434
  end
417
435
  end
data/lib/bugsnag/rack.rb CHANGED
@@ -14,7 +14,7 @@ module Bugsnag
14
14
  config.release_stage ||= ENV["RACK_ENV"] if ENV["RACK_ENV"]
15
15
 
16
16
  # Try to set the project_root if it hasn't already been set, or show a warning if we can't
17
- unless config.project_root && !config.project_root.empty?
17
+ unless config.project_root && !config.project_root.to_s.empty?
18
18
  if defined?(settings)
19
19
  config.project_root = settings.root
20
20
  else
@@ -53,4 +53,4 @@ module Bugsnag
53
53
  Bugsnag.clear_request_data
54
54
  end
55
55
  end
56
- end
56
+ end
data/lib/bugsnag/rails.rb CHANGED
@@ -19,26 +19,42 @@ module Bugsnag
19
19
  ActiveRecord::Base.send(:include, Bugsnag::Rails::ActiveRecordRescue)
20
20
  end
21
21
 
22
- # Try to find where to log to
23
- rails_logger = nil
24
- if defined?(::Rails.logger)
25
- rails_logger = ::Rails.logger
26
- elsif defined?(RAILS_DEFAULT_LOGGER)
27
- rails_logger = RAILS_DEFAULT_LOGGER
28
- end
29
-
30
22
  Bugsnag.configure do |config|
31
23
  config.logger ||= rails_logger
32
- config.release_stage = RAILS_ENV if defined?(RAILS_ENV)
33
- config.project_root = RAILS_ROOT if defined?(RAILS_ROOT)
24
+ config.release_stage = rails_env if rails_env
25
+ config.project_root = rails_root if rails_root
34
26
 
35
27
  config.middleware.insert_before(Bugsnag::Middleware::Callbacks,Bugsnag::Middleware::Rails2Request)
36
28
  end
37
29
 
38
30
  # Auto-load configuration settings from config/bugsnag.yml if it exists
39
- config_file = File.join(RAILS_ROOT, "config", "bugsnag.yml")
31
+ config_file = File.join(rails_root, "config", "bugsnag.yml")
40
32
  config = YAML.load_file(config_file) if File.exists?(config_file)
41
- Bugsnag.configure(config[RAILS_ENV] ? config[RAILS_ENV] : config) if config
33
+ Bugsnag.configure(config[rails_env] ? config[rails_env] : config) if config
34
+ end
35
+
36
+ def self.rails_logger
37
+ if defined?(::Rails.logger)
38
+ rails_logger = ::Rails.logger
39
+ elsif defined?(RAILS_DEFAULT_LOGGER)
40
+ rails_logger = RAILS_DEFAULT_LOGGER
41
+ end
42
+ end
43
+
44
+ def self.rails_env
45
+ if defined?(::Rails.env)
46
+ ::Rails.env
47
+ elsif defined?(RAILS_ENV)
48
+ RAILS_ENV
49
+ end
50
+ end
51
+
52
+ def self.rails_root
53
+ if defined?(::Rails.root)
54
+ ::Rails.root
55
+ elsif defined?(RAILS_ROOT)
56
+ RAILS_ROOT
57
+ end
42
58
  end
43
59
  end
44
60
  end
@@ -2,6 +2,8 @@
2
2
  module Bugsnag::Rails
3
3
  module ActionControllerRescue
4
4
  def self.included(base)
5
+ base.extend(ClassMethods)
6
+
5
7
  # Hook into rails exception rescue stack
6
8
  base.send(:alias_method, :rescue_action_in_public_without_bugsnag, :rescue_action_in_public)
7
9
  base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_bugsnag)
@@ -30,5 +32,31 @@ module Bugsnag::Rails
30
32
 
31
33
  rescue_action_locally_without_bugsnag(exception)
32
34
  end
35
+
36
+ module ClassMethods
37
+
38
+ def self.extended(base)
39
+ base.singleton_class.class_eval do
40
+ alias_method_chain :filter_parameter_logging, :bugsnag
41
+ end
42
+ end
43
+
44
+ # Rails 2 does parameter filtering via a controller configuration method
45
+ # that dynamically defines a method on the controller, so the configured
46
+ # parameters aren't easily accessible. Intercept these parameters as
47
+ # they're configured so that the Bugsnag configuration can take them
48
+ # into account.
49
+ #
50
+ def filter_parameter_logging_with_bugsnag(*filter_words, &block)
51
+ if filter_words.length > 0
52
+ Bugsnag.configure do |config|
53
+ # Use the same regular expression that Rails parameter filtering uses.
54
+ config.params_filters << Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true)
55
+ end
56
+ end
57
+ filter_parameter_logging_without_bugsnag(*filter_words, &block)
58
+ end
59
+ end
60
+
33
61
  end
34
62
  end