diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eda47c..c6d2d17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.2.0 + +Features + - Opt in support for sending exceptions in a background thread to not block web request thread during IO ([#117](https://github.com/MindscapeHQ/raygun4ruby/pull/117)) + +Bugfixes + - Don't attempt to read raw data during GET requests or if rack.input buffer is empty + ## 2.1.0 Features diff --git a/README.md b/README.md index c682060..f49721f 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ You can then test your Raygun integration by running: You should see an "ItWorksException" appear in your Raygun dashboard. You're ready to zap those errors! -NB: Raygun4Ruby currently requires Ruby >= 1.9 +NB: Raygun4Ruby currently requires Ruby >= 2.0 Note that the generator will create a file in `config/initializers` called "raygun.rb". If you need to do any further configuration or customization of Raygun, that's the place to do it! diff --git a/lib/raygun.rb b/lib/raygun.rb index 213c234..21dfc9f 100644 --- a/lib/raygun.rb +++ b/lib/raygun.rb @@ -1,3 +1,4 @@ +require "concurrent" require "httparty" require "logger" require "json" @@ -54,25 +55,10 @@ def configured? end def track_exception(exception_instance, env = {}, user = nil, retry_count = 1) - if should_report?(exception_instance) - log("[Raygun] Tracking Exception...") - Client.new.track_exception(exception_instance, env, user) - end - rescue Exception => e - if configuration.failsafe_logger - failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}") - end - - if retry_count > 0 - new_exception = e.exception("raygun4ruby encountered an exception processing your exception") - new_exception.set_backtrace(e.backtrace) - - env[:custom_data] ||= {} - env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace) - - track_exception(new_exception, env, user, retry_count - 1) + if configuration.send_in_background + track_exception_async(exception_instance, env, user, retry_count) else - raise e + track_exception_sync(exception_instance, env, user, retry_count) end end @@ -122,6 +108,39 @@ def deprecation_warning(message) private + def track_exception_async(*args) + future = Concurrent::Future.execute { track_exception_sync(*args) } + future.add_observer(lambda do |_, value, reason| + if value == nil || value.response.code != "202" + log("[Raygun] unexpected response from Raygun, could indicate error: #{value.inspect}") + end + end, :call) + end + + def track_exception_sync(exception_instance, env, user, retry_count) + if should_report?(exception_instance) + log("[Raygun] Tracking Exception...") + Client.new.track_exception(exception_instance, env, user) + end + rescue Exception => e + if configuration.failsafe_logger + failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}") + end + + if retry_count > 0 + new_exception = e.exception("raygun4ruby encountered an exception processing your exception") + new_exception.set_backtrace(e.backtrace) + + env[:custom_data] ||= {} + env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace) + + track_exception(new_exception, env, user, retry_count - 1) + else + raise e + end + end + + def print_api_key_warning $stderr.puts(NO_API_KEY_MESSAGE) end diff --git a/lib/raygun/client.rb b/lib/raygun/client.rb index 8f21c2b..18476db 100644 --- a/lib/raygun/client.rb +++ b/lib/raygun/client.rb @@ -136,8 +136,11 @@ def raw_data(rack_env) request = Rack::Request.new(rack_env) input = rack_env['rack.input'] + return if request.get? - if input && !request.form_data? + # If size is 0 the buffer is at best empty and at worst + # something like the Puma::NullIO buffer which is missing methods + if input && input.size && !request.form_data? current_position = input.pos input.rewind diff --git a/lib/raygun/configuration.rb b/lib/raygun/configuration.rb index 9869394..5ab5ec1 100644 --- a/lib/raygun/configuration.rb +++ b/lib/raygun/configuration.rb @@ -74,6 +74,9 @@ def self.proc_config_option(name) # form submissions and will not be filtered by the blacklist config_option :record_raw_data + # Should the exceptions to Raygun be sent asynchronously? + config_option :send_in_background + # Exception classes to ignore by default IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError', @@ -111,7 +114,7 @@ def initialize @config_values = {} # set default attribute values - @defaults = OpenStruct.new({ + @defaults = OpenStruct.new( ignore: IGNORE_DEFAULT, custom_data: {}, tags: [], @@ -125,8 +128,9 @@ def initialize debug: false, api_url: 'https://api.raygun.io/', breadcrumb_level: :info, - record_raw_data: false - }) + record_raw_data: false, + send_in_background: false + ) end def [](key) diff --git a/lib/raygun/version.rb b/lib/raygun/version.rb index af848f2..a6196d5 100644 --- a/lib/raygun/version.rb +++ b/lib/raygun/version.rb @@ -1,3 +1,3 @@ module Raygun - VERSION = "2.1.0" + VERSION = "2.2.0" end diff --git a/raygun4ruby.gemspec b/raygun4ruby.gemspec index 365992f..c181231 100644 --- a/raygun4ruby.gemspec +++ b/raygun4ruby.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency "httparty", "> 0.13.7" spec.add_runtime_dependency "json" spec.add_runtime_dependency "rack" + spec.add_runtime_dependency "concurrent-ruby" spec.add_development_dependency "bundler", ">= 1.1" spec.add_development_dependency "rake", "0.9.6" diff --git a/test/unit/client_test.rb b/test/unit/client_test.rb index 2c25972..e32482f 100644 --- a/test/unit/client_test.rb +++ b/test/unit/client_test.rb @@ -213,7 +213,7 @@ def test_getting_request_information queryString: { "a" => "b", "c" => "4945438" }, headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, form: {}, - rawData: {} + rawData: nil } assert_equal expected_hash, @client.send(:request_information, env_hash) @@ -550,7 +550,7 @@ def test_filter_payload_with_whitelist_default_request_get queryString: { "a" => "b", "c" => "4945438" }, headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, form: {}, - rawData: {} + rawData: nil } details = @client.send(:build_payload_hash, test_exception, env_hash)[:details] @@ -574,7 +574,7 @@ def test_filter_payload_with_whitelist_default_request_get_except_querystring queryString: "[FILTERED]", headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, form: {}, - rawData: {} + rawData: nil } details = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details] @@ -597,7 +597,7 @@ def test_filter_payload_with_whitelist_being_false_does_not_filter_query_string queryString: { "a" => "b", "c" => "4945438" }, headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, form: {}, - rawData: {} + rawData: nil } details = @client.send(:build_payload_hash, test_exception, env_hash)[:details] diff --git a/test/unit/configuration_test.rb b/test/unit/configuration_test.rb index 39291d8..abad5e3 100644 --- a/test/unit/configuration_test.rb +++ b/test/unit/configuration_test.rb @@ -159,4 +159,8 @@ def test_breadcrumb_level_default def test_record_raw_data_default assert_equal false, Raygun.configuration.record_raw_data end + + def test_send_in_background_default + assert_equal false, Raygun.configuration.send_in_background + end end