ratchetio 0.4.7 → 0.4.8

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.
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