ratchetio 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ **0.4.8**
4
+ - Add ability to send reports asynchronously, using girl_friday or Threading by default.
5
+ - Add ability to save reports to a file (for use with ratchet-agent) instead of sending across to Ratchet servers.
6
+
3
7
  **0.4.7**
4
8
  - Sensitive params now scrubbed out of requests. Param name list is customizable via the `scrub_fields` config option.
5
9
 
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gemspec
5
5
  gem 'sqlite3', :platform => [:ruby, :mswin, :mingw]
6
6
  gem 'jruby-openssl', :platform => :jruby
7
7
  gem 'activerecord-jdbcsqlite3-adapter', :platform => :jruby
8
+ gem 'girl_friday'
data/README.md CHANGED
@@ -85,7 +85,7 @@ If the methods to extract the `id`, `username`, and `email` from the object retu
85
85
 
86
86
  ## Exception level filters
87
87
 
88
- By default, all exceptions reported through `Ratchetio.report_exception()` are reporeted at the "error" level, except for the following, which are reported at "warning" level:
88
+ By default, all exceptions reported through `Ratchetio.report_exception()` are reported at the "error" level, except for the following, which are reported at "warning" level:
89
89
 
90
90
  - ActiveRecord::RecordNotFound
91
91
  - AbstractController::ActionNotFound
@@ -94,6 +94,41 @@ By default, all exceptions reported through `Ratchetio.report_exception()` are r
94
94
  If you'd like to customize this list, see the example code in `config/initializers/ratchetio.rb`. Supported levels: "critical", "error", "warning", "info", "debug", "ignore". Set to "ignore" to cause the exception not to be reported at all.
95
95
 
96
96
 
97
+
98
+ ## Asynchronous reporting
99
+
100
+ By default, all messages are reported synchronously. You can enable asynchronous reporting by adding the following in `config/initializers/ratchetio.rb`:
101
+
102
+ ```ruby
103
+ config.use_async = true
104
+ ```
105
+
106
+ Ratchet uses [girl_friday](https://github.com/mperham/girl_friday) to handle asynchronous reporting when installed, and falls back to Threading if girl_friday is not installed.
107
+
108
+ You can supply your own handler using `config.async_handler`. The handler should schedule the payload for later processing (i.e. with a delayed_job, in a resque queue, etc.) and should itself return immediately. For example:
109
+
110
+ ```ruby
111
+ config.async_handler = Proc.new { |payload|
112
+ Thread.new { Ratchetio.process_payload(payload) }
113
+ }
114
+ ```
115
+
116
+ Make sure you pass `payload` to `Ratchetio.process_payload` in your own implementation.
117
+
118
+
119
+ ## Using with ratchet-agent
120
+
121
+ For even more asynchrony, you can configure the gem to write to a file instead of sending the payload to Ratchet servers directly. [ratchet-agent](https://github.com/ratchetio/ratchet-agent) can then be hooked up to this file to actually send the payload across. To enable, add the following in `config/initializers/ratchetio.rb`:
122
+
123
+ ```ruby
124
+ config.write_to_file = true
125
+ # optional, defaults to "#{AppName}.ratchet"
126
+ config.filepath = '/path/to/file.ratchet' #should end in '.ratchet' for use with ratchet-agent
127
+ ```
128
+
129
+ For this to work, you'll also need to set up ratchet-agent--see its docs for details.
130
+
131
+
97
132
  ## Help / Support
98
133
 
99
134
  If you run into any issues, please email us at `support@ratchet.io`
@@ -17,4 +17,12 @@ Ratchetio.configure do |config|
17
17
  # Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore'
18
18
  # 'ignore' will cause the exception to not be reported at all.
19
19
  # config.exception_level_filters.merge!('MyCriticalException' => 'critical')
20
+
21
+ # Enable asynchronous reporting (uses girl_friday or Threading if girl_friday
22
+ # is not installed)
23
+ # config.use_async = true
24
+ # Supply your own async handler:
25
+ # config.async_handler = Proc.new { |payload|
26
+ # Thread.new { Ratchetio.process_payload(payload) }
27
+ # }
20
28
  end
@@ -8,6 +8,8 @@ module Ratchetio
8
8
  attr_accessor :default_logger
9
9
  attr_accessor :enabled
10
10
  attr_accessor :endpoint
11
+ attr_accessor :write_to_file
12
+ attr_accessor :filepath
11
13
  attr_accessor :environment
12
14
  attr_accessor :exception_level_filters
13
15
  attr_accessor :framework
@@ -18,6 +20,8 @@ module Ratchetio
18
20
  attr_accessor :person_email_method
19
21
  attr_accessor :root
20
22
  attr_accessor :scrub_fields
23
+ attr_accessor :use_async
24
+ attr_accessor :async_handler
21
25
 
22
26
  DEFAULT_ENDPOINT = 'https://submit.ratchet.io/api/1/item/'
23
27
 
@@ -25,6 +29,7 @@ module Ratchetio
25
29
  @default_logger = lambda { Logger.new(STDERR) }
26
30
  @enabled = true
27
31
  @endpoint = DEFAULT_ENDPOINT
32
+ @write_to_file = false
28
33
  @framework = 'Plain'
29
34
  @exception_level_filters = {
30
35
  'ActiveRecord::RecordNotFound' => 'warning',
@@ -36,6 +41,8 @@ module Ratchetio
36
41
  @person_username_method = 'username'
37
42
  @person_email_method = 'email'
38
43
  @scrub_fields = [:passwd, :password, :secret]
44
+ @use_async = false
45
+ @async_handler = nil
39
46
  end
40
47
 
41
48
  # allow params to be read like a hash
@@ -13,6 +13,7 @@ module Ratchetio
13
13
  config.environment ||= ::Rails.env
14
14
  config.root ||= ::Rails.root
15
15
  config.framework = "Rails: #{::Rails::VERSION::STRING}"
16
+ config.filepath ||= ::Rails.application.class.parent_name + '.ratchet'
16
17
  end
17
18
 
18
19
  ActiveSupport.on_load(:action_controller) do
@@ -1,3 +1,3 @@
1
1
  module Ratchetio
2
- VERSION = "0.4.7"
2
+ VERSION = "0.4.8"
3
3
  end
data/lib/ratchetio.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'net/https'
2
2
  require 'socket'
3
+ require 'thread'
3
4
  require 'uri'
4
5
 
5
6
  require 'ratchetio/version'
6
7
  require 'ratchetio/configuration'
7
8
  require 'ratchetio/railtie' if defined?(Rails)
8
9
  require 'ratchetio/goalie' if defined?(Goalie)
10
+ require "girl_friday" if defined?(GirlFriday)
9
11
 
10
12
  module Ratchetio
11
13
  class << self
@@ -67,7 +69,7 @@ module Ratchetio
67
69
  data[:person] = person_data if person_data
68
70
 
69
71
  payload = build_payload(data)
70
- send_payload(payload)
72
+ schedule_payload(payload)
71
73
  rescue => e
72
74
  logger.error "[Ratchet.io] Error reporting exception to Ratchet.io: #{e}"
73
75
  end
@@ -90,11 +92,23 @@ module Ratchetio
90
92
 
91
93
  data = message_data(message, level, extra_data)
92
94
  payload = build_payload(data)
93
- send_payload(payload)
95
+ schedule_payload(payload)
94
96
  rescue => e
95
97
  logger.error "[Ratchet.io] Error reporting message to Ratchet.io: #{e}"
96
98
  end
97
99
 
100
+ def process_payload(payload)
101
+ begin
102
+ if configuration.write_to_file
103
+ write_payload(payload)
104
+ else
105
+ send_payload(payload)
106
+ end
107
+ rescue => e
108
+ logger.error "[Ratchet.io] Error reporting message to Ratchet.io: #{e}"
109
+ end
110
+ end
111
+
98
112
  private
99
113
 
100
114
  def message_data(message, level, extra_data)
@@ -147,10 +161,36 @@ module Ratchetio
147
161
  end
148
162
  configuration.logger
149
163
  end
150
-
164
+
165
+ def write_payload(payload)
166
+ if configuration.use_async
167
+ @file_semaphore.synchronize {
168
+ do_write_payload(payload)
169
+ }
170
+ else
171
+ do_write_payload(payload)
172
+ end
173
+ end
174
+
175
+ def do_write_payload(payload)
176
+ logger.info '[Ratchet.io] Writing payload to file'
177
+
178
+ begin
179
+ unless @file
180
+ @file = File.open(configuration.filepath, "a")
181
+ end
182
+
183
+ @file.puts payload
184
+ @file.flush
185
+ logger.info "[Ratchet.io] Success"
186
+ rescue IOError => e
187
+ logger.error "[Ratchet.io] Error opening/writing to file: #{e}"
188
+ end
189
+ end
190
+
151
191
  def send_payload(payload)
152
192
  logger.info '[Ratchet.io] Sending payload'
153
-
193
+
154
194
  uri = URI.parse(configuration.endpoint)
155
195
  http = Net::HTTP.new(uri.host, uri.port)
156
196
 
@@ -170,6 +210,26 @@ module Ratchetio
170
210
  logger.info "[Ratchet.io] Response: #{response.body}"
171
211
  end
172
212
  end
213
+
214
+ def schedule_payload(payload)
215
+ logger.info '[Ratchet.io] Scheduling payload'
216
+
217
+ if configuration.use_async
218
+ unless configuration.async_handler
219
+ configuration.async_handler = method(:default_async_handler)
220
+ end
221
+
222
+ if configuration.write_to_file
223
+ unless @file_semaphore
224
+ @file_semaphore = Mutex.new
225
+ end
226
+ end
227
+
228
+ configuration.async_handler.call(payload)
229
+ else
230
+ process_payload(payload)
231
+ end
232
+ end
173
233
 
174
234
  def build_payload(data)
175
235
  payload = {
@@ -205,6 +265,20 @@ module Ratchetio
205
265
 
206
266
  data
207
267
  end
208
-
268
+
269
+ def default_async_handler(payload)
270
+ if defined?(GirlFriday)
271
+ unless @queue
272
+ @queue = GirlFriday::WorkQueue.new(nil, :size => 5) do |payload|
273
+ process_payload(payload)
274
+ end
275
+ end
276
+
277
+ @queue.push(payload)
278
+ else
279
+ logger.warn '[Ratchet.io] girl_friday not found to handle async call, falling back to Thread'
280
+ Thread.new { process_payload(payload) }
281
+ end
282
+ end
209
283
  end
210
284
  end
data/ratchetio.gemspec CHANGED
@@ -18,4 +18,5 @@ Gem::Specification.new do |gem|
18
18
  gem.add_development_dependency 'devise', '>= 2.1.2'
19
19
  gem.add_development_dependency 'rspec-rails', '~> 2.12.0'
20
20
  gem.add_development_dependency 'database_cleaner', '>= 0.9.1'
21
+ gem.add_development_dependency 'girl_friday', '>= 0.11.1'
21
22
  end
@@ -125,6 +125,107 @@ describe Ratchetio do
125
125
  end
126
126
  end
127
127
  end
128
+
129
+ context 'payload_destination' do
130
+ before(:each) do
131
+ configure
132
+ Ratchetio.configure do |config|
133
+ config.logger = logger_mock
134
+ config.filepath = 'test.ratchet'
135
+ end
136
+
137
+ begin
138
+ foo = bar
139
+ rescue => e
140
+ @exception = e
141
+ end
142
+ end
143
+
144
+ let(:logger_mock) { double("Rails.logger").as_null_object }
145
+
146
+ it 'should send the payload over the network by default' do
147
+ logger_mock.should_not_receive(:info).with('[Ratchet.io] Writing payload to file')
148
+ logger_mock.should_receive(:info).with('[Ratchet.io] Sending payload')
149
+ logger_mock.should_receive(:info).with('[Ratchet.io] Success')
150
+ Ratchetio.report_exception(@exception)
151
+ end
152
+
153
+ it 'should save the payload to a file if set' do
154
+ logger_mock.should_not_receive(:info).with('[Ratchet.io] Sending payload')
155
+ logger_mock.should_receive(:info).with('[Ratchet.io] Writing payload to file')
156
+ logger_mock.should_receive(:info).with('[Ratchet.io] Success')
157
+
158
+ filepath = ''
159
+
160
+ Ratchetio.configure do |config|
161
+ config.write_to_file = true
162
+ filepath = config.filepath
163
+ end
164
+
165
+ Ratchetio.report_exception(@exception)
166
+
167
+ File.exist?(filepath).should == true
168
+ File.read(filepath).should include test_access_token
169
+ File.delete(filepath)
170
+
171
+ Ratchetio.configure do |config|
172
+ config.write_to_file = false
173
+ end
174
+ end
175
+ end
176
+
177
+ context 'asynchronous_handling' do
178
+ before(:each) do
179
+ configure
180
+ Ratchetio.configure do |config|
181
+ config.logger = logger_mock
182
+ end
183
+
184
+ begin
185
+ foo = bar
186
+ rescue => e
187
+ @exception = e
188
+ end
189
+ end
190
+
191
+ let(:logger_mock) { double("Rails.logger").as_null_object }
192
+
193
+ it 'should send the payload using the default asynchronous handler girl_friday' do
194
+ logger_mock.should_receive(:info).with('[Ratchet.io] Success')
195
+
196
+ Ratchetio.configure do |config|
197
+ config.use_async = true
198
+ GirlFriday::WorkQueue::immediate!
199
+ end
200
+
201
+ Ratchetio.report_exception(@exception)
202
+
203
+ Ratchetio.configure do |config|
204
+ config.use_async = false
205
+ GirlFriday::WorkQueue::queue!
206
+ end
207
+ end
208
+
209
+ it 'should send the payload using a user-supplied asynchronous handler' do
210
+ logger_mock.should_receive(:info).with('Custom async handler called')
211
+ logger_mock.should_receive(:info).with('[Ratchet.io] Success')
212
+
213
+ Ratchetio.configure do |config|
214
+ config.use_async = true
215
+ config.async_handler = Proc.new { |payload|
216
+ logger_mock.info 'Custom async handler called'
217
+ Ratchetio.process_payload(payload)
218
+ }
219
+ end
220
+
221
+ Ratchetio.report_exception(@exception)
222
+
223
+ Ratchetio.configure do |config|
224
+ config.use_async = false
225
+ config.async_handler = Ratchetio.method(:default_async_handler)
226
+ end
227
+ end
228
+ end
128
229
 
129
230
  context 'message_data' do
130
231
  before(:each) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratchetio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.4.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-05 00:00:00.000000000 Z
12
+ date: 2012-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -75,6 +75,22 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: 0.9.1
78
+ - !ruby/object:Gem::Dependency
79
+ name: girl_friday
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.11.1
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.11.1
78
94
  description: Rails plugin to catch and send exceptions to Ratchet.io
79
95
  email:
80
96
  - brian@ratchet.io