rollbar 2.22.1 → 2.27.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +25 -0
  3. data/.rubocop.yml +136 -0
  4. data/.travis.yml +33 -30
  5. data/Gemfile +2 -0
  6. data/README.md +1 -1
  7. data/data/rollbar.snippet.js +1 -1
  8. data/docs/configuration.md +8 -0
  9. data/gemfiles/rails42.gemfile +4 -0
  10. data/gemfiles/rails50.gemfile +6 -2
  11. data/gemfiles/rails51.gemfile +6 -2
  12. data/gemfiles/rails60.gemfile +1 -1
  13. data/lib/rails/rollbar_runner.rb +3 -1
  14. data/lib/rollbar/capistrano.rb +1 -1
  15. data/lib/rollbar/capistrano_tasks.rb +10 -1
  16. data/lib/rollbar/configuration.rb +28 -6
  17. data/lib/rollbar/delay/girl_friday.rb +3 -7
  18. data/lib/rollbar/delay/resque.rb +2 -3
  19. data/lib/rollbar/delay/sidekiq.rb +2 -4
  20. data/lib/rollbar/delay/sucker_punch.rb +3 -4
  21. data/lib/rollbar/delay/thread.rb +14 -0
  22. data/lib/rollbar/deploy.rb +2 -0
  23. data/lib/rollbar/encoding/encoder.rb +10 -3
  24. data/lib/rollbar/item/backtrace.rb +12 -2
  25. data/lib/rollbar/item/frame.rb +2 -0
  26. data/lib/rollbar/item/locals.rb +45 -1
  27. data/lib/rollbar/item.rb +22 -14
  28. data/lib/rollbar/json.rb +1 -0
  29. data/lib/rollbar/middleware/js.rb +13 -3
  30. data/lib/rollbar/notifier.rb +154 -61
  31. data/lib/rollbar/plugins/active_job.rb +6 -2
  32. data/lib/rollbar/plugins/delayed_job/plugin.rb +11 -1
  33. data/lib/rollbar/plugins/error_context.rb +11 -0
  34. data/lib/rollbar/rake_tasks.rb +1 -1
  35. data/lib/rollbar/request_data_extractor.rb +7 -1
  36. data/lib/rollbar/rollbar_test.rb +6 -117
  37. data/lib/rollbar/scrubbers/url.rb +10 -5
  38. data/lib/rollbar/scrubbers.rb +1 -5
  39. data/lib/rollbar/truncation/frames_strategy.rb +1 -1
  40. data/lib/rollbar/truncation/mixin.rb +1 -1
  41. data/lib/rollbar/truncation/strings_strategy.rb +4 -2
  42. data/lib/rollbar/util.rb +4 -0
  43. data/lib/rollbar/version.rb +1 -1
  44. data/rollbar.gemspec +1 -0
  45. data/spec/support/rollbar_api.rb +67 -0
  46. metadata +6 -4
@@ -17,10 +17,8 @@ module Rollbar
17
17
 
18
18
  def perform(*args)
19
19
  Rollbar.process_from_async_handler(*args)
20
- rescue StandardError
21
- # Raise the exception so Sidekiq can track the errored job
22
- # and retry it
23
- raise
20
+
21
+ # Do not rescue. Sidekiq will call the error handler.
24
22
  end
25
23
  end
26
24
  end
@@ -33,7 +33,7 @@ module Rollbar
33
33
 
34
34
  def perform(*args)
35
35
  Rollbar.process_from_async_handler(*args)
36
- rescue StandardError
36
+
37
37
  # SuckerPunch can configure an exception handler with:
38
38
  #
39
39
  # SuckerPunch.exception_handler { # do something here }
@@ -41,9 +41,8 @@ module Rollbar
41
41
  # This is just passed to Celluloid.exception_handler which will
42
42
  # push the reiceved block to an array of handlers, by default empty, [].
43
43
  #
44
- # We reraise the exception here casue it's safe and users could have defined
45
- # their own exception handler for SuckerPunch
46
- raise
44
+
45
+ # Do not rescue. SuckerPunch will call the error handler.
47
46
  end
48
47
  end
49
48
  end
@@ -9,7 +9,10 @@ module Rollbar
9
9
  Error = Class.new(StandardError)
10
10
  TimeoutError = Class.new(Error)
11
11
 
12
+ DEFAULT_PRIORITY = 1
13
+
12
14
  class << self
15
+ attr_writer :options
13
16
  attr_reader :reaper
14
17
 
15
18
  def call(payload)
@@ -20,6 +23,10 @@ module Rollbar
20
23
  thread
21
24
  end
22
25
 
26
+ def options
27
+ @options || {}
28
+ end
29
+
23
30
  private
24
31
 
25
32
  def threads
@@ -61,9 +68,16 @@ module Rollbar
61
68
  end
62
69
  end # class << self
63
70
 
71
+ def priority
72
+ self.class.options[:priority] || DEFAULT_PRIORITY
73
+ end
74
+
64
75
  def call(payload)
76
+ priority = self.priority
77
+
65
78
  ::Thread.new do
66
79
  begin
80
+ ::Thread.current.priority = priority
67
81
  Rollbar.process_from_async_handler(payload)
68
82
  rescue StandardError
69
83
  # Here we swallow the exception:
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module Rollbar
2
4
  # Deploy Tracking API wrapper module
3
5
  module Deploy
@@ -3,7 +3,6 @@ module Rollbar
3
3
  class Encoder
4
4
  ALL_ENCODINGS = [::Encoding::UTF_8, ::Encoding::ISO_8859_1, ::Encoding::ASCII_8BIT, ::Encoding::US_ASCII].freeze
5
5
  ASCII_ENCODINGS = [::Encoding::US_ASCII, ::Encoding::ASCII_8BIT, ::Encoding::ISO_8859_1].freeze
6
- ENCODING_OPTIONS = { :invalid => :replace, :undef => :replace, :replace => '' }.freeze
7
6
  UTF8 = 'UTF-8'.freeze
8
7
  BINARY = 'binary'.freeze
9
8
 
@@ -21,10 +20,19 @@ module Rollbar
21
20
  encoded_value = if encoding == ::Encoding::UTF_8 && value.valid_encoding?
22
21
  value
23
22
  else
24
- force_encoding(value).encode(*encoding_args(value))
23
+ force_encoding(value).encode(
24
+ *encoding_args(value),
25
+ # Ruby 2.7 requires this to look like keyword args,
26
+ # and Ruby 1.9.3 doesn't understand keyword args, so
27
+ # don't use hash rockets here and both will be happy.
28
+ invalid: :replace, undef: :replace, replace: '' # rubocop:disable Style/HashSyntax
29
+ )
25
30
  end
26
31
 
27
32
  object.is_a?(Symbol) ? encoded_value.to_sym : encoded_value
33
+ rescue StandardError => e
34
+ # If encoding fails for any reason, replace the string with a diagnostic error.
35
+ "error encoding string: #{e.class}: #{e.message}"
28
36
  end
29
37
 
30
38
  private
@@ -54,7 +62,6 @@ module Rollbar
54
62
  def encoding_args(value)
55
63
  args = [UTF8]
56
64
  args << BINARY if ASCII_ENCODINGS.include?(value.encoding)
57
- args << ENCODING_OPTIONS
58
65
 
59
66
  args
60
67
  end
@@ -74,10 +74,20 @@ module Rollbar
74
74
  end
75
75
 
76
76
  def map_frames(current_exception)
77
- exception_backtrace(current_exception).map do |frame|
77
+ frames = cleaned_backtrace(current_exception).map do |frame|
78
78
  Rollbar::Item::Frame.new(self, frame,
79
79
  :configuration => configuration).to_h
80
- end.reverse
80
+ end
81
+ frames.reverse!
82
+ end
83
+
84
+ def cleaned_backtrace(current_exception)
85
+ normalized_backtrace = exception_backtrace(current_exception)
86
+ if configuration.backtrace_cleaner
87
+ configuration.backtrace_cleaner.clean(normalized_backtrace)
88
+ else
89
+ normalized_backtrace
90
+ end
81
91
  end
82
92
 
83
93
  # Returns the backtrace to be sent to our API. There are 3 options:
@@ -97,6 +97,8 @@ module Rollbar
97
97
  end
98
98
 
99
99
  def locals_data(filename, lineno)
100
+ return unless configuration.locals[:enabled]
101
+
100
102
  ::Rollbar::Item::Locals.locals_for_location(filename, lineno)
101
103
  end
102
104
 
@@ -1,5 +1,6 @@
1
1
  require 'rollbar/notifier'
2
2
  require 'rollbar/scrubbers/params'
3
+ require 'rollbar/util'
3
4
 
4
5
  module Rollbar
5
6
  class Item
@@ -39,8 +40,51 @@ module Rollbar
39
40
  end
40
41
  end
41
42
 
43
+ # Prepare objects to be handled by the payload serializer.
44
+ #
45
+ # Hashes and Arrays are traversed. Then all types execpt strings and
46
+ # immediates are exported using #inspect. Sending the object itself to the
47
+ # serializer can result in large recursive expansions, especially in Rails
48
+ # environments with ActiveRecord, ActiveSupport, etc. on the stack.
49
+ # Other export options could be #to_s, #to_h, and #as_json. Several of these
50
+ # will omit the class name, or are not implemented for many types.
51
+ #
52
+ # #inspect has the advantage that it is specifically intended for debugging
53
+ # output. If the user wants more or different information in the payload
54
+ # about a specific type, #inspect is the correct place to implement it.
55
+ # Likewise the default implementation should be oriented toward usefulness
56
+ # in debugging.
57
+ #
58
+ # Because #inspect outputs a string, it can be handled well by the string
59
+ # truncation strategy for large payloads.
60
+ #
42
61
  def prepare_value(value)
43
- value.to_s
62
+ return simple_value?(value) ? value : value.inspect unless value.is_a?(Hash) || value.is_a?(Array)
63
+
64
+ cloned_value = ::Rollbar::Util.deep_copy(value)
65
+ ::Rollbar::Util.iterate_and_update_with_block(cloned_value) do |v|
66
+ simple_value?(v) ? v : v.inspect
67
+ end
68
+
69
+ cloned_value
70
+ end
71
+
72
+ def simple_classes
73
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
74
+ [String, Symbol, Integer, Float, TrueClass, FalseClass, NilClass]
75
+ else
76
+ [String, Symbol, Fixnum, Bignum, Float, TrueClass, FalseClass, NilClass] # rubocop:disable Lint/UnifiedInteger
77
+ end
78
+ end
79
+
80
+ def simple_value?(value)
81
+ simple_classes.each do |klass|
82
+ # Use instance_of? so that subclasses and module containers will
83
+ # be treated like complex object types, not simple values.
84
+ return true if value.instance_of?(klass)
85
+ end
86
+
87
+ false
44
88
  end
45
89
 
46
90
  def scrub(hash)
data/lib/rollbar/item.rb CHANGED
@@ -65,7 +65,6 @@ module Rollbar
65
65
  def build
66
66
  data = build_data
67
67
  self.payload = {
68
- 'access_token' => configuration.access_token,
69
68
  'data' => data
70
69
  }
71
70
 
@@ -109,15 +108,11 @@ module Rollbar
109
108
  # the risk just to send this diagnostic object. In versions < 4.1, ActiveSupport hooks
110
109
  # Ruby's JSON.generate so deeply there's no workaround.
111
110
  'not serialized in ActiveSupport < 4.1'
112
- elsif configuration.use_async
113
- # Currently serialization is performed by each handler, and this invariably
114
- # means it is actually performed by ActiveSupport.
115
- #
116
- # TODO: Since serialization must be done prior to scheduling the job,
117
- # it should at least be done by rollbar-gem itself. Much work has been done
118
- # to avoid the bugs in ActiveSupport JSON. The async handlers are currently
119
- # still subject to all those knnown issues.
120
- 'not serialized for async/delayed handlers'
111
+ elsif configuration.use_async && !configuration.async_json_payload
112
+ # The setting allows serialization to be performed by each handler,
113
+ # and this usually means it is actually performed by ActiveSupport,
114
+ # which cannot safely serialize this key.
115
+ 'not serialized when async_json_payload is not set'
121
116
  else
122
117
  scrub(configuration.configured_options.configured)
123
118
  end
@@ -141,11 +136,18 @@ module Rollbar
141
136
  uuid = stringified_payload['data']['uuid']
142
137
  host = stringified_payload['data'].fetch('server', {})['host']
143
138
 
139
+ original_error = {
140
+ :message => message,
141
+ :exception => exception,
142
+ :configuration => configuration,
143
+ :uuid => uuid,
144
+ :host => host
145
+ }
146
+
144
147
  notifier.send_failsafe(
145
148
  too_large_payload_string(attempts),
146
149
  nil,
147
- uuid,
148
- host
150
+ original_error
149
151
  )
150
152
  logger.error("[Rollbar] Payload too large to be sent for UUID #{uuid}: #{Rollbar::JSON.dump(payload)}")
151
153
  end
@@ -187,13 +189,19 @@ module Rollbar
187
189
  end
188
190
 
189
191
  def build_extra
192
+ merged_extra = Util.deep_merge(scrub(extra), scrub(error_context))
193
+
190
194
  if custom_data_method? && !Rollbar::Util.method_in_stack(:custom_data, __FILE__)
191
- Util.deep_merge(scrub(custom_data), scrub(extra) || {})
195
+ Util.deep_merge(scrub(custom_data), merged_extra)
192
196
  else
193
- scrub(extra)
197
+ merged_extra.empty? ? nil : merged_extra # avoid putting an empty {} in the payload.
194
198
  end
195
199
  end
196
200
 
201
+ def error_context
202
+ exception.respond_to?(:rollbar_context) && exception.rollbar_context
203
+ end
204
+
197
205
  def scrub(data)
198
206
  return data unless data.is_a? Hash
199
207
 
data/lib/rollbar/json.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rollbar/language_support'
2
+ require 'json'
2
3
 
3
4
  module Rollbar
4
5
  module JSON # :nodoc:
@@ -80,10 +80,18 @@ module Rollbar
80
80
  return app_result unless response_string
81
81
 
82
82
  env[JS_IS_INJECTED_KEY] = true
83
- response = ::Rack::Response.new(response_string, app_result[0],
84
- app_result[1])
85
83
 
86
- response.finish
84
+ status, headers, = app_result
85
+ headers['Content-Length'] = response_string.bytesize.to_s if headers.key?('Content-Length')
86
+
87
+ response = ::Rack::Response.new(response_string, status, headers)
88
+
89
+ finished = response.finish
90
+
91
+ # Rack < 2.x Response#finish returns self in array[2]. Rack >= 2.x returns self.body.
92
+ # Always return with the response object here regardless of rack version.
93
+ finished[2] = response
94
+ finished
87
95
  end
88
96
 
89
97
  def build_body_with_js(env, body, head_open_end)
@@ -116,6 +124,8 @@ module Rollbar
116
124
  end
117
125
 
118
126
  def config_js_tag(env)
127
+ require 'json'
128
+
119
129
  js_config = Rollbar::Util.deep_copy(config[:options])
120
130
 
121
131
  add_person_data(js_config, env)