Skip to content

Commit d6967fd

Browse files
authored
Merge pull request #117 from MindscapeHQ/background-sending
Background sending of exceptions so web request thread is not blocked
2 parents 797ef7e + 1ea285f commit d6967fd

File tree

9 files changed

+67
-28
lines changed

9 files changed

+67
-28
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 2.2.0
2+
3+
Features
4+
- 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))
5+
6+
Bugfixes
7+
- Don't attempt to read raw data during GET requests or if rack.input buffer is empty
8+
19
## 2.1.0
210

311
Features

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ You can then test your Raygun integration by running:
3333

3434
You should see an "ItWorksException" appear in your Raygun dashboard. You're ready to zap those errors!
3535

36-
NB: Raygun4Ruby currently requires Ruby >= 1.9
36+
NB: Raygun4Ruby currently requires Ruby >= 2.0
3737

3838
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!
3939

lib/raygun.rb

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require "concurrent"
12
require "httparty"
23
require "logger"
34
require "json"
@@ -54,25 +55,10 @@ def configured?
5455
end
5556

5657
def track_exception(exception_instance, env = {}, user = nil, retry_count = 1)
57-
if should_report?(exception_instance)
58-
log("[Raygun] Tracking Exception...")
59-
Client.new.track_exception(exception_instance, env, user)
60-
end
61-
rescue Exception => e
62-
if configuration.failsafe_logger
63-
failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}")
64-
end
65-
66-
if retry_count > 0
67-
new_exception = e.exception("raygun4ruby encountered an exception processing your exception")
68-
new_exception.set_backtrace(e.backtrace)
69-
70-
env[:custom_data] ||= {}
71-
env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace)
72-
73-
track_exception(new_exception, env, user, retry_count - 1)
58+
if configuration.send_in_background
59+
track_exception_async(exception_instance, env, user, retry_count)
7460
else
75-
raise e
61+
track_exception_sync(exception_instance, env, user, retry_count)
7662
end
7763
end
7864

@@ -122,6 +108,39 @@ def deprecation_warning(message)
122108

123109
private
124110

111+
def track_exception_async(*args)
112+
future = Concurrent::Future.execute { track_exception_sync(*args) }
113+
future.add_observer(lambda do |_, value, reason|
114+
if value == nil || value.response.code != "202"
115+
log("[Raygun] unexpected response from Raygun, could indicate error: #{value.inspect}")
116+
end
117+
end, :call)
118+
end
119+
120+
def track_exception_sync(exception_instance, env, user, retry_count)
121+
if should_report?(exception_instance)
122+
log("[Raygun] Tracking Exception...")
123+
Client.new.track_exception(exception_instance, env, user)
124+
end
125+
rescue Exception => e
126+
if configuration.failsafe_logger
127+
failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}")
128+
end
129+
130+
if retry_count > 0
131+
new_exception = e.exception("raygun4ruby encountered an exception processing your exception")
132+
new_exception.set_backtrace(e.backtrace)
133+
134+
env[:custom_data] ||= {}
135+
env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace)
136+
137+
track_exception(new_exception, env, user, retry_count - 1)
138+
else
139+
raise e
140+
end
141+
end
142+
143+
125144
def print_api_key_warning
126145
$stderr.puts(NO_API_KEY_MESSAGE)
127146
end

lib/raygun/client.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,11 @@ def raw_data(rack_env)
136136

137137
request = Rack::Request.new(rack_env)
138138
input = rack_env['rack.input']
139+
return if request.get?
139140

140-
if input && !request.form_data?
141+
# If size is 0 the buffer is at best empty and at worst
142+
# something like the Puma::NullIO buffer which is missing methods
143+
if input && input.size && !request.form_data?
141144
current_position = input.pos
142145
input.rewind
143146

lib/raygun/configuration.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ def self.proc_config_option(name)
7474
# form submissions and will not be filtered by the blacklist
7575
config_option :record_raw_data
7676

77+
# Should the exceptions to Raygun be sent asynchronously?
78+
config_option :send_in_background
79+
7780
# Exception classes to ignore by default
7881
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
7982
'ActionController::RoutingError',
@@ -111,7 +114,7 @@ def initialize
111114
@config_values = {}
112115

113116
# set default attribute values
114-
@defaults = OpenStruct.new({
117+
@defaults = OpenStruct.new(
115118
ignore: IGNORE_DEFAULT,
116119
custom_data: {},
117120
tags: [],
@@ -125,8 +128,9 @@ def initialize
125128
debug: false,
126129
api_url: 'https://api.raygun.io/',
127130
breadcrumb_level: :info,
128-
record_raw_data: false
129-
})
131+
record_raw_data: false,
132+
send_in_background: false
133+
)
130134
end
131135

132136
def [](key)

lib/raygun/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Raygun
2-
VERSION = "2.1.0"
2+
VERSION = "2.2.0"
33
end

raygun4ruby.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
2222
spec.add_runtime_dependency "httparty", "> 0.13.7"
2323
spec.add_runtime_dependency "json"
2424
spec.add_runtime_dependency "rack"
25+
spec.add_runtime_dependency "concurrent-ruby"
2526

2627
spec.add_development_dependency "bundler", ">= 1.1"
2728
spec.add_development_dependency "rake", "0.9.6"

test/unit/client_test.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def test_getting_request_information
213213
queryString: { "a" => "b", "c" => "4945438" },
214214
headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" },
215215
form: {},
216-
rawData: {}
216+
rawData: nil
217217
}
218218

219219
assert_equal expected_hash, @client.send(:request_information, env_hash)
@@ -550,7 +550,7 @@ def test_filter_payload_with_whitelist_default_request_get
550550
queryString: { "a" => "b", "c" => "4945438" },
551551
headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" },
552552
form: {},
553-
rawData: {}
553+
rawData: nil
554554
}
555555

556556
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
574574
queryString: "[FILTERED]",
575575
headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" },
576576
form: {},
577-
rawData: {}
577+
rawData: nil
578578
}
579579

580580
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
597597
queryString: { "a" => "b", "c" => "4945438" },
598598
headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" },
599599
form: {},
600-
rawData: {}
600+
rawData: nil
601601
}
602602

603603
details = @client.send(:build_payload_hash, test_exception, env_hash)[:details]

test/unit/configuration_test.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,8 @@ def test_breadcrumb_level_default
159159
def test_record_raw_data_default
160160
assert_equal false, Raygun.configuration.record_raw_data
161161
end
162+
163+
def test_send_in_background_default
164+
assert_equal false, Raygun.configuration.send_in_background
165+
end
162166
end

0 commit comments

Comments
 (0)