rollbar 2.12.0 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -6
  3. data/README.md +58 -8
  4. data/docs/configuration.md +12 -0
  5. data/gemfiles/rails30.gemfile +1 -0
  6. data/gemfiles/rails31.gemfile +1 -0
  7. data/gemfiles/rails32.gemfile +1 -0
  8. data/gemfiles/rails40.gemfile +3 -0
  9. data/gemfiles/rails41.gemfile +1 -0
  10. data/gemfiles/rails42.gemfile +7 -1
  11. data/gemfiles/rails50.gemfile +2 -1
  12. data/gemfiles/ruby_1_8_and_1_9_2.gemfile +3 -1
  13. data/lib/rollbar.rb +70 -654
  14. data/lib/rollbar/configuration.rb +32 -0
  15. data/lib/rollbar/item.rb +16 -6
  16. data/lib/rollbar/item/backtrace.rb +26 -17
  17. data/lib/rollbar/item/frame.rb +112 -0
  18. data/lib/rollbar/middleware/js.rb +39 -35
  19. data/lib/rollbar/middleware/rails/rollbar.rb +3 -3
  20. data/lib/rollbar/notifier.rb +645 -0
  21. data/lib/rollbar/plugins/delayed_job/job_data.rb +40 -21
  22. data/lib/rollbar/plugins/rails.rb +2 -2
  23. data/lib/rollbar/plugins/rake.rb +32 -6
  24. data/lib/rollbar/plugins/resque.rb +11 -0
  25. data/lib/rollbar/plugins/resque/failure.rb +39 -0
  26. data/lib/rollbar/plugins/validations.rb +10 -0
  27. data/lib/rollbar/request_data_extractor.rb +36 -18
  28. data/lib/rollbar/scrubbers/params.rb +2 -1
  29. data/lib/rollbar/truncation.rb +1 -1
  30. data/lib/rollbar/truncation/frames_strategy.rb +2 -1
  31. data/lib/rollbar/truncation/min_body_strategy.rb +2 -1
  32. data/lib/rollbar/truncation/strings_strategy.rb +1 -1
  33. data/lib/rollbar/version.rb +1 -1
  34. data/spec/controllers/home_controller_spec.rb +13 -24
  35. data/spec/delayed/backend/test.rb +1 -0
  36. data/spec/requests/home_spec.rb +1 -1
  37. data/spec/rollbar/configuration_spec.rb +22 -0
  38. data/spec/rollbar/item/backtrace_spec.rb +26 -0
  39. data/spec/rollbar/item/frame_spec.rb +267 -0
  40. data/spec/rollbar/item_spec.rb +27 -2
  41. data/spec/rollbar/middleware/js_spec.rb +23 -0
  42. data/spec/rollbar/middleware/sinatra_spec.rb +7 -7
  43. data/spec/rollbar/notifier_spec.rb +43 -0
  44. data/spec/rollbar/plugins/delayed_job/{job_data.rb → job_data_spec.rb} +15 -2
  45. data/spec/rollbar/plugins/rack_spec.rb +7 -7
  46. data/spec/rollbar/plugins/rake_spec.rb +1 -2
  47. data/spec/rollbar/plugins/resque/failure_spec.rb +36 -0
  48. data/spec/rollbar/request_data_extractor_spec.rb +103 -1
  49. data/spec/rollbar/truncation/min_body_strategy_spec.rb +1 -1
  50. data/spec/rollbar/truncation/strings_strategy_spec.rb +2 -2
  51. data/spec/rollbar_bc_spec.rb +4 -4
  52. data/spec/rollbar_spec.rb +99 -37
  53. data/spec/spec_helper.rb +2 -2
  54. data/spec/support/notifier_helpers.rb +2 -0
  55. metadata +16 -4
@@ -1,29 +1,48 @@
1
- class JobData
2
- attr_reader :job
1
+ module Rollbar
2
+ module Delayed
3
+ class JobData
4
+ attr_reader :job
3
5
 
4
- def initialize(job)
5
- @job = job
6
- end
6
+ def initialize(job)
7
+ @job = job
8
+ end
7
9
 
8
- def to_hash
9
- job_data = job.as_json
10
- handler_parent = job_data['job'] ? job_data['job'] : job_data
11
- handler_parent['handler'] = handler_data
10
+ def to_hash
11
+ job_data = extract_job_data
12
12
 
13
- job_data
14
- end
13
+ handler_parent = job_data['job'] ? job_data['job'] : job_data
14
+ handler_parent['handler'] = handler_data
15
+
16
+ job_data
17
+ end
18
+
19
+ private
20
+
21
+ def extract_job_data
22
+ if job.respond_to?(:as_json)
23
+ job.as_json
24
+ else
25
+ Hash[job.to_hash.map { |k, v| [k.to_s, v] }]
26
+ end
27
+ end
28
+
29
+ def handler_data
30
+ payload_object = job.payload_object
15
31
 
16
- private
32
+ return payload_object unless payload_object.respond_to?(:object)
17
33
 
18
- def handler_data
19
- object = job.payload_object.object
34
+ object_data(payload_object.object)
35
+ end
20
36
 
21
- {
22
- :method_name => job.payload_object.method_name,
23
- :args => job.payload_object.args,
24
- :object => object.is_a?(Class) ? object.name : object.to_s
25
- }
26
- rescue
27
- {}
37
+ def object_data(object)
38
+ {
39
+ :method_name => job.payload_object.method_name,
40
+ :args => job.payload_object.args,
41
+ :object => object.is_a?(Class) ? object.name : object.to_s
42
+ }
43
+ rescue
44
+ {}
45
+ end
46
+ end
28
47
  end
29
48
  end
@@ -31,7 +31,7 @@ Rollbar.plugins.define('rails-rollbar.js') do
31
31
  # customer is using SecureHeaders > 3.0
32
32
  class Rails
33
33
  def load(plugin)
34
- plugin_execute = plugin_execute_proc(plugin)
34
+ plugin_execute = plugin_execute_proc_body(plugin)
35
35
 
36
36
  return after_secure_headers(&plugin_execute) if secure_headers_middleware?
37
37
 
@@ -42,7 +42,7 @@ Rollbar.plugins.define('rails-rollbar.js') do
42
42
  Rollbar::Railtie.initializer('rollbar.js.frameworks.rails', :after => 'secure_headers.middleware', &block)
43
43
  end
44
44
 
45
- def plugin_execute_proc(plugin)
45
+ def plugin_execute_proc_body(plugin)
46
46
  proc do
47
47
  plugin.execute do
48
48
  if Rollbar.configuration.js_enabled
@@ -5,17 +5,38 @@ Rollbar.plugins.define('rake') do
5
5
 
6
6
  module Rollbar
7
7
  module Rake
8
+ class << self
9
+ attr_accessor :patched
10
+ end
11
+
12
+ module Handler
13
+ def self.included(base)
14
+ base.class_eval do
15
+ alias_method :orig_display_error_message, :display_error_message
16
+ alias_method :display_error_message, :display_error_message_with_rollbar
17
+ end
18
+ end
19
+
20
+ def display_error_message_with_rollbar(ex)
21
+ Rollbar.error(ex, :use_exception_level_filters => true)
22
+ orig_display_error_message(ex)
23
+ end
24
+ end
25
+
8
26
  def self.patch!
9
- skip_patch && return unless patch?
27
+ unless patch?
28
+ skip_patch
10
29
 
11
- ::Rake::Application.class_eval do
12
- alias_method :orig_display_error_message, :display_error_message
30
+ return
31
+ end
13
32
 
14
- def display_error_message(ex)
15
- Rollbar.error(ex, :use_exception_level_filters => true)
16
- orig_display_error_message(ex)
33
+ ::Rake.application.instance_eval do
34
+ class << self
35
+ include ::Rollbar::Rake::Handler
17
36
  end
18
37
  end
38
+
39
+ self.patched = true
19
40
  end
20
41
 
21
42
  def self.skip_patch
@@ -23,6 +44,7 @@ Rollbar.plugins.define('rake') do
23
44
  end
24
45
 
25
46
  def self.patch?
47
+ return false if patched?
26
48
  return false unless rake_version
27
49
 
28
50
  major, minor, = rake_version.split('.').map(&:to_i)
@@ -30,6 +52,10 @@ Rollbar.plugins.define('rake') do
30
52
  major > 0 || major == 0 && minor > 8
31
53
  end
32
54
 
55
+ def self.patched?
56
+ patched
57
+ end
58
+
33
59
  def self.rake_version
34
60
  if Object.const_defined?('RAKEVERSION')
35
61
  return RAKEVERSION
@@ -0,0 +1,11 @@
1
+ Rollbar.plugins.define('resque') do
2
+ require_dependency('resque')
3
+
4
+ # We want to have Resque::Failure::Rollbar loaded before
5
+ # possible initializers, so the users can use the class
6
+ # when configuring Rollbar::Failure.backend or
7
+ # Rollbar::Failure::Multiple.classes
8
+ execute! do
9
+ require 'rollbar/plugins/resque/failure'
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ require 'rollbar'
2
+
3
+ module Resque
4
+ module Failure
5
+ # Falure class to use in Resque in order to send
6
+ # Resque errors to the Rollbar API
7
+ class Rollbar < Base
8
+ def save
9
+ payload_with_options =
10
+ if use_exception_level_filters?
11
+ payload.merge(:use_exception_level_filters => true)
12
+ else
13
+ payload
14
+ end
15
+
16
+ rollbar.error(exception, payload_with_options)
17
+ end
18
+
19
+ private
20
+
21
+ # We want to disable async reporting since original
22
+ # resque-rollbar implementation disabled it.
23
+ def rollbar
24
+ notifier = ::Rollbar.notifier.scope
25
+ notifier.configuration.use_async = false
26
+
27
+ notifier
28
+ end
29
+
30
+ def use_exception_level_filters?
31
+ Gem::Version.new(rollbar_version) > Gem::Version.new('1.3.0')
32
+ end
33
+
34
+ def rollbar_version
35
+ ::Rollbar::VERSION
36
+ end
37
+ end
38
+ end
39
+ end
@@ -29,5 +29,15 @@ Rollbar.plugins.define('active_model') do
29
29
  ActiveModel::Validations.module_eval do
30
30
  include Rollbar::ActiveRecordExtension
31
31
  end
32
+
33
+ # If ActiveRecord::Base has been already loaded,
34
+ # it's including a not updated version of ActiveModel::Validations
35
+ # We then want to include Rollbar::ActiveRecordExtension
36
+ # in ActiveRecord::Base
37
+ if defined?(ActiveRecord::Base)
38
+ ActiveRecord::Base.class_eval do
39
+ include Rollbar::ActiveRecordExtension
40
+ end
41
+ end
32
42
  end
33
43
  end
@@ -5,9 +5,12 @@ require 'rollbar/scrubbers'
5
5
  require 'rollbar/scrubbers/url'
6
6
  require 'rollbar/scrubbers/params'
7
7
  require 'rollbar/util/ip_obfuscator'
8
+ require 'rollbar/json'
8
9
 
9
10
  module Rollbar
10
11
  module RequestDataExtractor
12
+ ALLOWED_HEADERS_REGEX = /^HTTP_|^CONTENT_TYPE$|^CONTENT_LENGTH$/
13
+
11
14
  def extract_person_data_from_controller(env)
12
15
  if env.has_key?('rollbar.person_data')
13
16
  person_data = env['rollbar.person_data'] || {}
@@ -21,9 +24,8 @@ module Rollbar
21
24
 
22
25
  def extract_request_data_from_rack(env)
23
26
  rack_req = ::Rack::Request.new(env)
24
-
25
27
  sensitive_params = sensitive_params_list(env)
26
- request_params = scrub_params(rollbar_request_params(env), sensitive_params)
28
+
27
29
  get_params = scrub_params(rollbar_get_params(rack_req), sensitive_params)
28
30
  post_params = scrub_params(rollbar_post_params(rack_req), sensitive_params)
29
31
  raw_body_params = scrub_params(mergeable_raw_body_params(rack_req), sensitive_params)
@@ -32,17 +34,18 @@ module Rollbar
32
34
  route_params = scrub_params(rollbar_route_params(env), sensitive_params)
33
35
 
34
36
  url = scrub_url(rollbar_url(env), sensitive_params)
35
- params = request_params.merge(get_params).merge(post_params).merge(raw_body_params)
36
37
 
37
38
  data = {
38
- :params => params,
39
39
  :url => url,
40
+ :params => route_params,
41
+ :GET => get_params,
42
+ :POST => post_params,
43
+ :body => Rollbar::JSON.dump(raw_body_params),
40
44
  :user_ip => rollbar_user_ip(env),
41
45
  :headers => rollbar_headers(env),
42
46
  :cookies => cookies,
43
47
  :session => session,
44
- :method => rollbar_request_method(env),
45
- :route => route_params
48
+ :method => rollbar_request_method(env)
46
49
  }
47
50
 
48
51
  if env['action_dispatch.request_id']
@@ -92,7 +95,7 @@ module Rollbar
92
95
  end
93
96
 
94
97
  def rollbar_headers(env)
95
- env.keys.grep(/^HTTP_/).map do |header|
98
+ env.keys.grep(ALLOWED_HEADERS_REGEX).map do |header|
96
99
  name = header.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
97
100
  if name == 'Cookie'
98
101
  {}
@@ -125,13 +128,33 @@ module Rollbar
125
128
  end
126
129
 
127
130
  def rollbar_user_ip(env)
128
- user_ip_string = (env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']).to_s
131
+ user_ip_string = (env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || x_forwarded_for_client(env['HTTP_X_FORWARDED_FOR']) || env['REMOTE_ADDR']).to_s
129
132
 
130
133
  Rollbar::Util::IPObfuscator.obfuscate_ip(user_ip_string)
131
134
  rescue
132
135
  nil
133
136
  end
134
137
 
138
+ def x_forwarded_for_client(header_value)
139
+ return nil unless header_value
140
+
141
+ ips = header_value.split(',').map(&:strip)
142
+
143
+ find_not_private_ip(ips)
144
+ end
145
+
146
+ def find_not_private_ip(ips)
147
+ ips.detect do |ip|
148
+ octets = ip.match(/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/)[1, 4].map(&:to_i)
149
+
150
+ is_private = (octets[0] == 10) ||
151
+ ((octets[0] == 172) && (octets[1] >= 16) && (octets[1] <= 31)) ||
152
+ ((octets[0] == 192) && (octets[1] == 168))
153
+
154
+ !is_private
155
+ end
156
+ end
157
+
135
158
  def rollbar_get_params(rack_req)
136
159
  rack_req.GET
137
160
  rescue
@@ -162,21 +185,16 @@ module Rollbar
162
185
  rack_req.env['ACCEPT'] =~ /\bjson\b/)
163
186
  end
164
187
 
165
- def rollbar_request_params(env)
166
- env['action_dispatch.request.parameters'] || {}
167
- end
168
-
169
188
  def rollbar_route_params(env)
170
189
  return {} unless defined?(Rails)
171
190
 
172
191
  begin
173
- route = ::Rails.application.routes.recognize_path(env['PATH_INFO'])
192
+ environment = { :method => rollbar_request_method(env) }
174
193
 
175
- {
176
- :controller => route[:controller],
177
- :action => route[:action],
178
- :format => route[:format]
179
- }
194
+ # recognize_path() will return the controller, action
195
+ # route params (if any)and format (if defined)
196
+ ::Rails.application.routes.recognize_path(env['PATH_INFO'],
197
+ environment)
180
198
  rescue
181
199
  {}
182
200
  end
@@ -1,3 +1,4 @@
1
+ require 'tempfile'
1
2
  require 'rollbar/scrubbers'
2
3
 
3
4
  module Rollbar
@@ -7,7 +8,7 @@ module Rollbar
7
8
  # Also, if that configuration option is se to :scrub_all, it will scrub all
8
9
  # received parameters
9
10
  class Params
10
- SKIPPED_CLASSES = [Tempfile]
11
+ SKIPPED_CLASSES = [::Tempfile]
11
12
  ATTACHMENT_CLASSES = %w(ActionDispatch::Http::UploadedFile Rack::Multipart::UploadedFile).freeze
12
13
  SCRUB_ALL = :scrub_all
13
14
 
@@ -9,7 +9,7 @@ module Rollbar
9
9
  module Truncation
10
10
  extend ::Rollbar::Truncation::Mixin
11
11
 
12
- MAX_PAYLOAD_SIZE = 128 * 1024 # 128kb
12
+ MAX_PAYLOAD_SIZE = 512 * 1024 # 512kb
13
13
  STRATEGIES = [RawStrategy,
14
14
  FramesStrategy,
15
15
  StringsStrategy,
@@ -1,4 +1,5 @@
1
1
  require 'rollbar/truncation/mixin'
2
+ require 'rollbar/util'
2
3
 
3
4
  module Rollbar
4
5
  module Truncation
@@ -10,7 +11,7 @@ module Rollbar
10
11
  end
11
12
 
12
13
  def call(payload)
13
- new_payload = payload.clone
14
+ new_payload = Rollbar::Util.deep_copy(payload)
14
15
  body = new_payload['data']['body']
15
16
 
16
17
  if body['trace_chain']
@@ -1,4 +1,5 @@
1
1
  require 'rollbar/truncation/mixin'
2
+ require 'rollbar/util'
2
3
 
3
4
  module Rollbar
4
5
  module Truncation
@@ -10,7 +11,7 @@ module Rollbar
10
11
  end
11
12
 
12
13
  def call(payload)
13
- new_payload = payload.clone
14
+ new_payload = Rollbar::Util.deep_copy(payload)
14
15
  body = new_payload['data']['body']
15
16
 
16
17
  if body['trace_chain']
@@ -14,7 +14,7 @@ module Rollbar
14
14
 
15
15
  def call(payload)
16
16
  result = nil
17
- new_payload = payload.clone
17
+ new_payload = Rollbar::Util.deep_copy(payload)
18
18
 
19
19
  STRING_THRESHOLDS.each do |threshold|
20
20
  truncate_proc = truncate_strings_proc(threshold)
@@ -1,3 +1,3 @@
1
1
  module Rollbar
2
- VERSION = '2.12.0'
2
+ VERSION = '2.13.0'.freeze
3
3
  end
@@ -6,6 +6,7 @@ describe HomeController do
6
6
  let(:notifier) { Rollbar.notifier }
7
7
 
8
8
  before do
9
+ Rollbar.clear_notifier!
9
10
  reset_configuration
10
11
  preconfigure_rails_notifier
11
12
 
@@ -53,7 +54,6 @@ describe HomeController do
53
54
  data.should have_key(:headers)
54
55
  data.should have_key(:session)
55
56
  data.should have_key(:method)
56
- data.should have_key(:route)
57
57
  end
58
58
 
59
59
  it "should build empty person data when no one is logged-in" do
@@ -169,21 +169,10 @@ describe HomeController do
169
169
  end
170
170
 
171
171
  context "rollbar_route_params", :type => 'request' do
172
- it "should save route params in request[:route]" do
173
- route = controller.send(:rollbar_request_data)[:route]
174
-
175
- route.should have_key(:controller)
176
- route.should have_key(:action)
177
- route.should have_key(:format)
178
-
179
- route[:controller].should == 'home'
180
- route[:action].should == 'index'
181
- end
182
-
183
172
  it "should save controller and action in the payload body" do
184
173
  post '/report_exception'
185
174
 
186
- route = controller.send(:rollbar_request_data)[:route]
175
+ route = controller.send(:rollbar_request_data)[:params]
187
176
 
188
177
  route[:controller].should == 'home'
189
178
  route[:action].should == 'report_exception'
@@ -206,7 +195,7 @@ describe HomeController do
206
195
 
207
196
  post '/report_exception', params
208
197
 
209
- filtered = Rollbar.last_report[:request][:params]
198
+ filtered = Rollbar.last_report[:request][:POST]
210
199
 
211
200
  expect(filtered["passwd"]).to match(/\**/)
212
201
  expect(filtered["password"]).to match(/\**/)
@@ -229,7 +218,7 @@ describe HomeController do
229
218
 
230
219
  post '/report_exception', params
231
220
 
232
- filtered = Rollbar.last_report[:request][:params]
221
+ filtered = Rollbar.last_report[:request][:POST]
233
222
 
234
223
  filtered["passwd"].should == "visible"
235
224
  # config.filter_parameters is set to [:password] in
@@ -262,7 +251,7 @@ describe HomeController do
262
251
  put '/report_exception', { :putparam => "putval" }
263
252
 
264
253
  Rollbar.last_report.should_not be_nil
265
- Rollbar.last_report[:request][:params]["putparam"].should == "putval"
254
+ Rollbar.last_report[:request][:POST]["putparam"].should == "putval"
266
255
  end
267
256
 
268
257
  context 'using deprecated report_exception' do
@@ -272,7 +261,7 @@ describe HomeController do
272
261
  put '/deprecated_report_exception', { :putparam => "putval" }
273
262
 
274
263
  Rollbar.last_report.should_not be_nil
275
- Rollbar.last_report[:request][:params]["putparam"].should == "putval"
264
+ Rollbar.last_report[:request][:POST]["putparam"].should == "putval"
276
265
  end
277
266
  end
278
267
 
@@ -284,7 +273,7 @@ describe HomeController do
284
273
  post '/report_exception', params, { 'CONTENT_TYPE' => 'application/json' }
285
274
 
286
275
  Rollbar.last_report.should_not be_nil
287
- Rollbar.last_report[:request][:params]['jsonparam'].should == 'jsonval'
276
+ expect(Rollbar.last_report[:request][:body]).to be_eql(params)
288
277
  end
289
278
  end
290
279
 
@@ -306,7 +295,7 @@ describe HomeController do
306
295
  config['action_dispatch.show_exceptions'] = true
307
296
  end
308
297
 
309
- after(:each) do
298
+ after do
310
299
  if Dummy::Application.respond_to? :env_config
311
300
  config = Dummy::Application.env_config
312
301
  else
@@ -356,7 +345,7 @@ describe HomeController do
356
345
  expect { get '/foo/bar', { :foo => :bar } }.to raise_exception(ActionController::RoutingError)
357
346
 
358
347
  report = Rollbar.last_report
359
- expect(report[:request][:params]['foo']).to be_eql('bar')
348
+ expect(report[:request][:GET]['foo']).to be_eql('bar')
360
349
  end
361
350
  end
362
351
 
@@ -378,7 +367,7 @@ describe HomeController do
378
367
  it "saves attachment data" do
379
368
  expect { post '/file_upload', { :upload => file1 } }.to raise_exception(NameError)
380
369
 
381
- upload_param = Rollbar.last_report[:request][:params]['upload']
370
+ upload_param = Rollbar.last_report[:request][:POST]['upload']
382
371
 
383
372
  expect(upload_param).to have_key(:filename)
384
373
  expect(upload_param).to have_key(:type)
@@ -392,7 +381,7 @@ describe HomeController do
392
381
  context 'with multiple uploads', :type => :request do
393
382
  it "saves attachment data for all uploads" do
394
383
  expect { post '/file_upload', { :upload => [file1, file2] } }.to raise_exception(NameError)
395
- sent_params = Rollbar.last_report[:request][:params]['upload']
384
+ sent_params = Rollbar.last_report[:request][:POST]['upload']
396
385
 
397
386
  expect(sent_params).to be_kind_of(Array)
398
387
  expect(sent_params.size).to be(2)
@@ -420,7 +409,7 @@ describe HomeController do
420
409
  post '/cause_exception', params, { 'ACCEPT' => 'application/vnd.github.v3+json' }
421
410
  end.to raise_exception(NameError)
422
411
 
423
- expect(Rollbar.last_report[:request][:params]['foo']).to be_eql('bar')
412
+ expect(Rollbar.last_report[:request][:POST]['foo']).to be_eql('bar')
424
413
  end
425
414
  end
426
415
 
@@ -448,7 +437,7 @@ describe HomeController do
448
437
  end
449
438
  end
450
439
 
451
- after(:each) do
440
+ after do
452
441
  Rollbar.configure do |config|
453
442
  config.logger = ::Rails.logger
454
443
  end