rollbar 2.12.0 → 2.13.0

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