rollbar 2.8.3 → 3.6.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 (238) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +18 -0
  3. data/.github/pull_request_template.md +34 -0
  4. data/.github/workflows/ci.yml +67 -0
  5. data/.gitignore +3 -1
  6. data/.rubocop.yml +206 -7
  7. data/Appraisals +10 -10
  8. data/CHANGELOG.md +257 -3
  9. data/Gemfile +74 -13
  10. data/README.md +38 -833
  11. data/Rakefile +0 -0
  12. data/THANKS.md +1 -0
  13. data/data/rollbar.snippet.js +1 -1
  14. data/docs/configuration.md +64 -3
  15. data/docs/plugins.md +46 -0
  16. data/gemfiles/rails50.gemfile +56 -0
  17. data/gemfiles/rails51.gemfile +57 -0
  18. data/gemfiles/rails52.gemfile +56 -0
  19. data/gemfiles/rails60.gemfile +52 -0
  20. data/gemfiles/rails61.gemfile +52 -0
  21. data/gemfiles/rails70.gemfile +52 -0
  22. data/gemfiles/rails71.gemfile +52 -0
  23. data/lib/generators/rollbar/rollbar_generator.rb +24 -20
  24. data/lib/generators/rollbar/templates/{initializer.rb → initializer.erb} +19 -5
  25. data/lib/rails/rollbar_runner.rb +26 -22
  26. data/lib/rollbar/capistrano.rb +78 -38
  27. data/lib/rollbar/capistrano3.rb +62 -1
  28. data/lib/rollbar/capistrano_tasks.rb +166 -0
  29. data/lib/rollbar/configuration.rb +291 -71
  30. data/lib/rollbar/delay/active_job.rb +17 -0
  31. data/lib/rollbar/delay/delayed_job.rb +23 -0
  32. data/lib/rollbar/delay/girl_friday.rb +4 -9
  33. data/lib/rollbar/delay/resque.rb +3 -6
  34. data/lib/rollbar/delay/shoryuken.rb +36 -0
  35. data/lib/rollbar/delay/sidekiq.rb +6 -8
  36. data/lib/rollbar/delay/sucker_punch.rb +17 -22
  37. data/lib/rollbar/delay/thread.rb +74 -3
  38. data/lib/rollbar/deploy.rb +91 -0
  39. data/lib/rollbar/encoding/encoder.rb +22 -11
  40. data/lib/rollbar/encoding.rb +2 -7
  41. data/lib/rollbar/exception_reporter.rb +36 -12
  42. data/lib/rollbar/item/backtrace.rb +118 -0
  43. data/lib/rollbar/item/frame.rb +121 -0
  44. data/lib/rollbar/item/locals.rb +103 -0
  45. data/lib/rollbar/item.rb +314 -0
  46. data/lib/rollbar/js.rb +0 -28
  47. data/lib/rollbar/json.rb +7 -55
  48. data/lib/rollbar/language_support.rb +7 -19
  49. data/lib/rollbar/lazy_store.rb +8 -12
  50. data/lib/rollbar/logger.rb +71 -0
  51. data/lib/rollbar/logger_proxy.rb +18 -1
  52. data/lib/rollbar/middleware/js/json_value.rb +36 -0
  53. data/lib/rollbar/middleware/js.rb +297 -0
  54. data/lib/rollbar/middleware/rack/builder.rb +4 -4
  55. data/lib/rollbar/middleware/rack/test_session.rb +4 -4
  56. data/lib/rollbar/middleware/rack.rb +52 -0
  57. data/lib/rollbar/middleware/rails/rollbar.rb +19 -7
  58. data/lib/rollbar/middleware/rails/show_exceptions.rb +21 -9
  59. data/lib/rollbar/middleware/sinatra.rb +2 -40
  60. data/lib/rollbar/notifier/trace_with_bindings.rb +75 -0
  61. data/lib/rollbar/notifier.rb +913 -0
  62. data/lib/rollbar/plugin.rb +126 -0
  63. data/lib/rollbar/plugins/active_job.rb +54 -0
  64. data/lib/rollbar/plugins/basic_socket.rb +31 -0
  65. data/lib/rollbar/plugins/delayed_job/job_data.rb +50 -0
  66. data/lib/rollbar/plugins/delayed_job/plugin.rb +88 -0
  67. data/lib/rollbar/plugins/delayed_job.rb +12 -0
  68. data/lib/rollbar/plugins/error_context.rb +11 -0
  69. data/lib/rollbar/plugins/goalie.rb +65 -0
  70. data/lib/rollbar/plugins/rack.rb +18 -0
  71. data/lib/rollbar/plugins/rails/controller_methods.rb +56 -0
  72. data/lib/rollbar/plugins/rails/error_subscriber.rb +12 -0
  73. data/lib/rollbar/plugins/rails/railtie30.rb +18 -0
  74. data/lib/rollbar/plugins/rails/railtie32.rb +18 -0
  75. data/lib/rollbar/plugins/rails/railtie_mixin.rb +37 -0
  76. data/lib/rollbar/plugins/rails.rb +89 -0
  77. data/lib/rollbar/plugins/rake.rb +73 -0
  78. data/lib/rollbar/plugins/resque/failure.rb +39 -0
  79. data/lib/rollbar/plugins/resque.rb +11 -0
  80. data/lib/rollbar/plugins/sidekiq/plugin.rb +77 -0
  81. data/lib/rollbar/plugins/sidekiq.rb +37 -0
  82. data/lib/rollbar/plugins/thread.rb +14 -0
  83. data/lib/rollbar/plugins/validations.rb +45 -0
  84. data/lib/rollbar/plugins.rb +47 -0
  85. data/lib/rollbar/rails.rb +0 -1
  86. data/lib/rollbar/rake_tasks.rb +4 -66
  87. data/lib/rollbar/request_data_extractor.rb +157 -117
  88. data/lib/rollbar/rollbar_test.rb +38 -0
  89. data/lib/rollbar/scrubbers/params.rb +133 -0
  90. data/lib/rollbar/scrubbers/url.rb +90 -35
  91. data/lib/rollbar/scrubbers.rb +13 -0
  92. data/lib/rollbar/truncation/frames_strategy.rb +2 -1
  93. data/lib/rollbar/truncation/min_body_strategy.rb +3 -4
  94. data/lib/rollbar/truncation/mixin.rb +1 -1
  95. data/lib/rollbar/truncation/remove_any_key_strategy.rb +126 -0
  96. data/lib/rollbar/truncation/remove_extra_strategy.rb +37 -0
  97. data/lib/rollbar/truncation/remove_request_strategy.rb +21 -0
  98. data/lib/rollbar/truncation/strings_strategy.rb +6 -5
  99. data/lib/rollbar/truncation.rb +10 -4
  100. data/lib/rollbar/util/hash.rb +37 -6
  101. data/lib/rollbar/util/ip_anonymizer.rb +33 -0
  102. data/lib/rollbar/util/ip_obfuscator.rb +1 -1
  103. data/lib/rollbar/util.rb +101 -55
  104. data/lib/rollbar/version.rb +1 -1
  105. data/lib/rollbar.rb +91 -879
  106. data/lib/tasks/benchmark.rake +104 -0
  107. data/lib/tasks/tasks.rake +3 -3
  108. data/rollbar.gemspec +21 -32
  109. data/spec/support/rollbar_api.rb +67 -0
  110. metadata +78 -439
  111. data/.travis.yml +0 -155
  112. data/gemfiles/rails30.gemfile +0 -20
  113. data/gemfiles/rails31.gemfile +0 -16
  114. data/gemfiles/rails32.gemfile +0 -17
  115. data/gemfiles/rails40.gemfile +0 -17
  116. data/gemfiles/rails41.gemfile +0 -15
  117. data/gemfiles/rails42.gemfile +0 -15
  118. data/lib/rollbar/active_job.rb +0 -11
  119. data/lib/rollbar/active_record_extension.rb +0 -14
  120. data/lib/rollbar/core_ext/basic_socket.rb +0 -7
  121. data/lib/rollbar/core_ext/thread.rb +0 -9
  122. data/lib/rollbar/delayed_job.rb +0 -78
  123. data/lib/rollbar/encoding/legacy_encoder.rb +0 -20
  124. data/lib/rollbar/goalie.rb +0 -33
  125. data/lib/rollbar/js/frameworks/rails.rb +0 -29
  126. data/lib/rollbar/js/frameworks.rb +0 -6
  127. data/lib/rollbar/js/middleware.rb +0 -129
  128. data/lib/rollbar/js/version.rb +0 -5
  129. data/lib/rollbar/json/default.rb +0 -11
  130. data/lib/rollbar/json/oj.rb +0 -15
  131. data/lib/rollbar/rack.rb +0 -9
  132. data/lib/rollbar/rails/controller_methods.rb +0 -40
  133. data/lib/rollbar/railtie.rb +0 -46
  134. data/lib/rollbar/rake.rb +0 -38
  135. data/lib/rollbar/sidekiq.rb +0 -40
  136. data/lib/rollbar/tasks/rollbar.cap +0 -45
  137. data/spec/cacert.pem +0 -3988
  138. data/spec/controllers/home_controller_spec.rb +0 -455
  139. data/spec/delay/sidekiq_spec.rb +0 -61
  140. data/spec/delay/sucker_punch_spec.rb +0 -25
  141. data/spec/delayed/backend/test.rb +0 -139
  142. data/spec/delayed/serialization/test.rb +0 -0
  143. data/spec/dummyapp/.gitignore +0 -73
  144. data/spec/dummyapp/Rakefile +0 -7
  145. data/spec/dummyapp/app/assets/javascripts/application.js +0 -3
  146. data/spec/dummyapp/app/assets/stylesheets/application.css.scss +0 -37
  147. data/spec/dummyapp/app/controllers/application_controller.rb +0 -3
  148. data/spec/dummyapp/app/controllers/home_controller.rb +0 -60
  149. data/spec/dummyapp/app/controllers/users_controller.rb +0 -17
  150. data/spec/dummyapp/app/helpers/.gitkeep +0 -0
  151. data/spec/dummyapp/app/mailers/.gitkeep +0 -0
  152. data/spec/dummyapp/app/models/.gitkeep +0 -0
  153. data/spec/dummyapp/app/models/user.rb +0 -7
  154. data/spec/dummyapp/app/views/devise/registrations/edit.html.erb +0 -27
  155. data/spec/dummyapp/app/views/devise/registrations/new.html.erb +0 -20
  156. data/spec/dummyapp/app/views/devise/shared/_links.html.erb +0 -25
  157. data/spec/dummyapp/app/views/home/cause_exception.html.erb +0 -1
  158. data/spec/dummyapp/app/views/home/index.html.erb +0 -4
  159. data/spec/dummyapp/app/views/home/report_exception.html.erb +0 -1
  160. data/spec/dummyapp/app/views/js/test.html.erb +0 -1
  161. data/spec/dummyapp/app/views/layouts/_messages.html.erb +0 -5
  162. data/spec/dummyapp/app/views/layouts/_navigation.html.erb +0 -21
  163. data/spec/dummyapp/app/views/layouts/application.html.erb +0 -25
  164. data/spec/dummyapp/app/views/layouts/simple.html.erb +0 -18
  165. data/spec/dummyapp/app/views/users/index.html.erb +0 -8
  166. data/spec/dummyapp/app/views/users/show.html.erb +0 -3
  167. data/spec/dummyapp/config/application.rb +0 -59
  168. data/spec/dummyapp/config/boot.rb +0 -10
  169. data/spec/dummyapp/config/database.yml +0 -25
  170. data/spec/dummyapp/config/environment.rb +0 -5
  171. data/spec/dummyapp/config/environments/development.rb +0 -37
  172. data/spec/dummyapp/config/environments/production.rb +0 -67
  173. data/spec/dummyapp/config/environments/test.rb +0 -37
  174. data/spec/dummyapp/config/initializers/backtrace_silencers.rb +0 -7
  175. data/spec/dummyapp/config/initializers/inflections.rb +0 -15
  176. data/spec/dummyapp/config/initializers/mime_types.rb +0 -5
  177. data/spec/dummyapp/config/initializers/rollbar.rb +0 -23
  178. data/spec/dummyapp/config/initializers/secret_token.rb +0 -7
  179. data/spec/dummyapp/config/initializers/session_store.rb +0 -8
  180. data/spec/dummyapp/config/initializers/wrap_parameters.rb +0 -16
  181. data/spec/dummyapp/config/locales/devise.en.yml +0 -58
  182. data/spec/dummyapp/config/locales/en.yml +0 -5
  183. data/spec/dummyapp/config/routes.rb +0 -17
  184. data/spec/dummyapp/config.ru +0 -4
  185. data/spec/dummyapp/db/migrate/20121121184652_devise_create_users.rb +0 -46
  186. data/spec/dummyapp/db/migrate/20121121184654_add_name_to_users.rb +0 -5
  187. data/spec/dummyapp/db/schema.rb +0 -35
  188. data/spec/dummyapp/db/seeds.rb +0 -12
  189. data/spec/dummyapp/lib/assets/.gitkeep +0 -0
  190. data/spec/dummyapp/public/404.html +0 -26
  191. data/spec/dummyapp/public/422.html +0 -26
  192. data/spec/dummyapp/public/500.html +0 -25
  193. data/spec/dummyapp/public/favicon.ico +0 -0
  194. data/spec/dummyapp/script/rails +0 -6
  195. data/spec/fixtures/file1 +0 -1
  196. data/spec/fixtures/file2 +0 -1
  197. data/spec/fixtures/payloads/message.json +0 -25
  198. data/spec/fixtures/payloads/sample.trace.json +0 -275
  199. data/spec/fixtures/payloads/sample.trace_chain.json +0 -530
  200. data/spec/generators/rollbar/rollbar_generator_spec.rb +0 -24
  201. data/spec/requests/home_spec.rb +0 -49
  202. data/spec/rollbar/active_job_spec.rb +0 -33
  203. data/spec/rollbar/configuration_spec.rb +0 -24
  204. data/spec/rollbar/delay/girl_friday_spec.rb +0 -41
  205. data/spec/rollbar/delay/resque_spec.rb +0 -37
  206. data/spec/rollbar/delay/thread_spec.rb +0 -27
  207. data/spec/rollbar/delayed_job/job_data.rb +0 -35
  208. data/spec/rollbar/delayed_job_spec.rb +0 -90
  209. data/spec/rollbar/encoding/encoder_spec.rb +0 -63
  210. data/spec/rollbar/js/frameworks/rails_spec.rb +0 -19
  211. data/spec/rollbar/js/middleware_spec.rb +0 -162
  212. data/spec/rollbar/json/oj_spec.rb +0 -18
  213. data/spec/rollbar/json_spec.rb +0 -110
  214. data/spec/rollbar/lazy_store_spec.rb +0 -99
  215. data/spec/rollbar/logger_proxy_spec.rb +0 -34
  216. data/spec/rollbar/middleware/rack/builder_spec.rb +0 -151
  217. data/spec/rollbar/middleware/sinatra_spec.rb +0 -197
  218. data/spec/rollbar/rake_spec.rb +0 -34
  219. data/spec/rollbar/request_data_extractor_spec.rb +0 -82
  220. data/spec/rollbar/scrubbers/url_spec.rb +0 -111
  221. data/spec/rollbar/sidekiq_spec.rb +0 -90
  222. data/spec/rollbar/truncation/frames_strategy_spec.rb +0 -70
  223. data/spec/rollbar/truncation/min_body_strategy_spec.rb +0 -57
  224. data/spec/rollbar/truncation/strings_strategy_spec.rb +0 -89
  225. data/spec/rollbar/truncation_spec.rb +0 -27
  226. data/spec/rollbar/util/hash_spec.rb +0 -22
  227. data/spec/rollbar/util_spec.rb +0 -19
  228. data/spec/rollbar_bc_spec.rb +0 -380
  229. data/spec/rollbar_spec.rb +0 -2067
  230. data/spec/spec_helper.rb +0 -49
  231. data/spec/support/cause_exception.rb +0 -1
  232. data/spec/support/encoding_helpers.rb +0 -8
  233. data/spec/support/encodings/iso_8859_9 +0 -1
  234. data/spec/support/fixture_helpers.rb +0 -10
  235. data/spec/support/get_ip_raising.rb +0 -7
  236. data/spec/support/helpers.rb +0 -5
  237. data/spec/support/notifier_helpers.rb +0 -36
  238. data/spec/support/shared_contexts.rb +0 -12
@@ -1,15 +1,86 @@
1
+ require 'timeout'
2
+
1
3
  module Rollbar
2
4
  module Delay
3
5
  class Thread
4
- def self.call(payload)
5
- new.call(payload)
6
+ EXIT_SIGNAL = :exit
7
+ EXIT_TIMEOUT = 6
8
+
9
+ Error = Class.new(StandardError)
10
+ TimeoutError = Class.new(Error)
11
+
12
+ DEFAULT_PRIORITY = 1
13
+
14
+ class << self
15
+ attr_writer :options
16
+ attr_reader :reaper
17
+
18
+ def call(payload)
19
+ spawn_threads_reaper
20
+
21
+ thread = new.call(payload)
22
+ threads << thread
23
+ thread
24
+ end
25
+
26
+ def options
27
+ @options || {}
28
+ end
29
+
30
+ private
31
+
32
+ def threads
33
+ @threads ||= Queue.new
34
+ end
35
+
36
+ def spawn_threads_reaper
37
+ return if @spawned
38
+
39
+ @spawned = true
40
+
41
+ @reaper ||= build_reaper_thread
42
+ configure_exit_handler
43
+ end
44
+
45
+ def build_reaper_thread
46
+ ::Thread.start do
47
+ loop do
48
+ thread = threads.pop
49
+
50
+ break if thread == EXIT_SIGNAL
51
+
52
+ thread.join
53
+ end
54
+ end
55
+ end
56
+
57
+ def configure_exit_handler
58
+ at_exit do
59
+ begin
60
+ Timeout.timeout(EXIT_TIMEOUT) do
61
+ threads << EXIT_SIGNAL
62
+ reaper.join
63
+ end
64
+ rescue Timeout::Error
65
+ raise TimeoutError,
66
+ "unable to reap all threads within #{EXIT_TIMEOUT} seconds"
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def priority
73
+ self.class.options[:priority] || DEFAULT_PRIORITY
6
74
  end
7
75
 
8
76
  def call(payload)
77
+ priority = self.priority
78
+
9
79
  ::Thread.new do
10
80
  begin
81
+ ::Thread.current.priority = priority
11
82
  Rollbar.process_from_async_handler(payload)
12
- rescue
83
+ rescue StandardError
13
84
  # Here we swallow the exception:
14
85
  # 1. The original report wasn't sent.
15
86
  # 2. An internal error was sent and logged
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+
3
+ module Rollbar
4
+ # Deploy Tracking API wrapper module
5
+ module Deploy
6
+ ENDPOINT = 'https://api.rollbar.com/api/1/deploy/'.freeze
7
+
8
+ def self.report(opts, access_token, environment, revision)
9
+ return {} unless access_token && !access_token.empty?
10
+
11
+ opts[:status] ||= :started
12
+
13
+ uri = ::URI.parse(::Rollbar::Deploy::ENDPOINT)
14
+
15
+ request_data = {
16
+ :access_token => access_token,
17
+ :environment => environment,
18
+ :revision => revision
19
+ }.merge(opts)
20
+ request_data.delete(:proxy)
21
+ request_data.delete(:dry_run)
22
+
23
+ request = ::Net::HTTP::Post.new(uri.request_uri)
24
+ request.body = ::JSON.dump(request_data)
25
+
26
+ send_request(opts, uri, request)
27
+ end
28
+
29
+ def self.update(opts, access_token, deploy_id, status)
30
+ return {} unless access_token && !access_token.empty?
31
+
32
+ uri = ::URI.parse(
33
+ "#{::Rollbar::Deploy::ENDPOINT}#{deploy_id}?access_token=#{access_token}"
34
+ )
35
+
36
+ request = ::Net::HTTP::Patch.new(uri.request_uri)
37
+ request.body = ::JSON.dump(:status => status.to_s, :comment => opts[:comment])
38
+
39
+ send_request(opts, uri, request)
40
+ end
41
+
42
+ class << self
43
+ private
44
+
45
+ def send_request(opts, uri, request)
46
+ ::Net::HTTP.start(uri.host, uri.port, opts[:proxy], :use_ssl => true) do |http|
47
+ build_result(
48
+ uri,
49
+ request,
50
+ opts[:dry_run] ? nil : http.request(request),
51
+ opts[:dry_run]
52
+ )
53
+ end
54
+ end
55
+
56
+ def build_result(uri, request, response = nil, dry_run = false)
57
+ result = {}
58
+ result.merge!(request_result(uri, request))
59
+ result.merge!(response_result(response)) unless response.nil?
60
+ result[:success] = success?(result, dry_run)
61
+ result
62
+ end
63
+
64
+ def success?(result, dry_run = false)
65
+ return true if dry_run
66
+
67
+ result[:response] &&
68
+ result[:response].is_a?(::Net::HTTPSuccess) &&
69
+ result[:response].code == '200' &&
70
+ (result.key?('err') ? result['err'].to_i.zero? : true)
71
+ end
72
+
73
+ def request_result(uri, request)
74
+ {
75
+ :request_info => "#{uri.inspect}: #{request.body}",
76
+ :request => request
77
+ }
78
+ end
79
+
80
+ def response_result(response)
81
+ code = response.code
82
+ message = response.message
83
+ body = response.body.delete("\n")
84
+ {
85
+ :response => response,
86
+ :response_info => "#{code}; #{message}; #{body}"
87
+ }.merge(::JSON.parse(response.body, :symbolize_names => true))
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,9 +1,10 @@
1
1
  module Rollbar
2
2
  module Encoding
3
3
  class Encoder
4
- ALL_ENCODINGS = [::Encoding::UTF_8, ::Encoding::ISO_8859_1, ::Encoding::ASCII_8BIT, ::Encoding::US_ASCII]
5
- ASCII_ENCODINGS = [::Encoding::US_ASCII, ::Encoding::ASCII_8BIT, ::Encoding::ISO_8859_1]
6
- ENCODING_OPTIONS = { :invalid => :replace, :undef => :replace, :replace => '' }
4
+ ALL_ENCODINGS = [::Encoding::UTF_8, ::Encoding::ISO_8859_1, ::Encoding::ASCII_8BIT,
5
+ ::Encoding::US_ASCII].freeze
6
+ ASCII_ENCODINGS = [::Encoding::US_ASCII, ::Encoding::ASCII_8BIT,
7
+ ::Encoding::ISO_8859_1].freeze
7
8
  UTF8 = 'UTF-8'.freeze
8
9
  BINARY = 'binary'.freeze
9
10
 
@@ -18,13 +19,22 @@ module Rollbar
18
19
  encoding = value.encoding
19
20
 
20
21
  # This will be most of cases so avoid force anything for them
21
- if encoding == ::Encoding::UTF_8 && value.valid_encoding?
22
- encoded_value = value
23
- else
24
- encoded_value = force_encoding(value).encode(*encoding_args(value))
25
- end
22
+ encoded_value = if encoding == ::Encoding::UTF_8 && value.valid_encoding?
23
+ value
24
+ else
25
+ force_encoding(value).encode(
26
+ *encoding_args(value),
27
+ # Ruby 2.7 requires this to look like keyword args,
28
+ # and Ruby 1.9.3 doesn't understand keyword args, so
29
+ # don't use hash rockets here and both will be happy.
30
+ invalid: :replace, undef: :replace, replace: '' # rubocop:disable Style/HashSyntax
31
+ )
32
+ end
26
33
 
27
34
  object.is_a?(Symbol) ? encoded_value.to_sym : encoded_value
35
+ rescue StandardError => e
36
+ # If encoding fails for any reason, replace the string with a diagnostic error.
37
+ "error encoding string: #{e.class}: #{e.message}"
28
38
  end
29
39
 
30
40
  private
@@ -32,7 +42,9 @@ module Rollbar
32
42
  def force_encoding(value)
33
43
  return value if value.frozen?
34
44
 
35
- value.force_encoding(detect_encoding(value)) if value.encoding == ::Encoding::UTF_8
45
+ if value.encoding == ::Encoding::UTF_8
46
+ value.force_encoding(detect_encoding(value))
47
+ end
36
48
 
37
49
  value
38
50
  end
@@ -45,7 +57,7 @@ module Rollbar
45
57
  # Seems #codepoints is faster than #valid_encoding?
46
58
  value.force_encoding(encoding).encode(::Encoding::UTF_8).codepoints
47
59
  true
48
- rescue
60
+ rescue StandardError
49
61
  false
50
62
  end
51
63
  end
@@ -54,7 +66,6 @@ module Rollbar
54
66
  def encoding_args(value)
55
67
  args = [UTF8]
56
68
  args << BINARY if ASCII_ENCODINGS.include?(value.encoding)
57
- args << ENCODING_OPTIONS
58
69
 
59
70
  args
60
71
  end
@@ -5,13 +5,8 @@ module Rollbar
5
5
  end
6
6
 
7
7
  def self.setup
8
- if String.instance_methods.include?(:encode)
9
- require 'rollbar/encoding/encoder'
10
- self.encoding_class = Rollbar::Encoding::Encoder
11
- else
12
- require 'rollbar/encoding/legacy_encoder'
13
- self.encoding_class = Rollbar::Encoding::LegacyEncoder
14
- end
8
+ require 'rollbar/encoding/encoder'
9
+ self.encoding_class = Rollbar::Encoding::Encoder
15
10
  end
16
11
 
17
12
  def self.encode(object)
@@ -1,21 +1,45 @@
1
1
  module Rollbar
2
- module ExceptionReporter
2
+ module ExceptionReporter # :nodoc:
3
3
  def report_exception_to_rollbar(env, exception)
4
- exception_message = exception.respond_to?(:message) ? exception.message : 'No Exception Message'
5
- Rollbar.log_debug "[Rollbar] Reporting exception: #{exception_message}"
4
+ return unless capture_uncaught?
5
+
6
+ log_exception_message(exception)
6
7
 
7
- exception_data = Rollbar.log(Rollbar.configuration.uncaught_exception_level, exception, :use_exception_level_filters => true)
8
+ exception_data = exception_data(exception)
8
9
 
9
- if exception_data.is_a?(Hash)
10
+ case exception_data
11
+ when Hash
10
12
  env['rollbar.exception_uuid'] = exception_data[:uuid]
11
- Rollbar.log_debug "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
12
- elsif exception_data == 'disabled'
13
- Rollbar.log_debug "[Rollbar] Exception not reported because Rollbar is disabled"
14
- elsif exception_data == 'ignored'
15
- Rollbar.log_debug "[Rollbar] Exception not reported because it was ignored"
13
+ Rollbar.log_debug(
14
+ "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
15
+ )
16
+ when 'disabled'
17
+ Rollbar.log_debug(
18
+ '[Rollbar] Exception not reported because Rollbar is disabled'
19
+ )
20
+ when 'ignored'
21
+ Rollbar.log_debug '[Rollbar] Exception not reported because it was ignored'
16
22
  end
17
- rescue => e
18
- Rollbar.log_warning "[Rollbar] Exception while reporting exception to Rollbar: #{e.message}"
23
+ rescue StandardError => e
24
+ Rollbar.log_warning(
25
+ "[Rollbar] Exception while reporting exception to Rollbar: #{e.message}"
26
+ )
27
+ end
28
+
29
+ def capture_uncaught?
30
+ Rollbar.configuration.capture_uncaught != false &&
31
+ !Rollbar.configuration.enable_rails_error_subscriber
32
+ end
33
+
34
+ def log_exception_message(exception)
35
+ exception_message = exception.message if exception.respond_to?(:message)
36
+ exception_message ||= 'No Exception Message'
37
+ Rollbar.log_debug "[Rollbar] Reporting exception: #{exception_message}"
38
+ end
39
+
40
+ def exception_data(exception)
41
+ Rollbar.log(Rollbar.configuration.uncaught_exception_level, exception,
42
+ :use_exception_level_filters => true)
19
43
  end
20
44
  end
21
45
  end
@@ -0,0 +1,118 @@
1
+ require 'rollbar/item/frame'
2
+
3
+ module Rollbar
4
+ class Item
5
+ class Backtrace
6
+ attr_reader :exception, :message, :extra, :configuration, :files
7
+
8
+ private :files
9
+
10
+ def initialize(exception, options = {})
11
+ @exception = exception
12
+ @message = options[:message]
13
+ @extra = options[:extra]
14
+ @configuration = options[:configuration]
15
+ @files = {}
16
+ end
17
+
18
+ def to_h
19
+ traces = trace_chain
20
+
21
+ traces[0][:exception][:description] = message if message
22
+ traces[0][:extra] = extra if extra
23
+
24
+ if traces.size > 1
25
+ { :trace_chain => traces }
26
+ elsif traces.size == 1
27
+ { :trace => traces[0] }
28
+ end
29
+ end
30
+
31
+ alias build to_h
32
+
33
+ def get_file_lines(filename)
34
+ files[filename] ||= read_file(filename)
35
+ end
36
+
37
+ private
38
+
39
+ def read_file(filename)
40
+ return unless File.exist?(filename)
41
+
42
+ File.read(filename).split("\n")
43
+ rescue StandardError
44
+ nil
45
+ end
46
+
47
+ def trace_chain
48
+ traces = [trace_data(exception)]
49
+ visited = [exception]
50
+
51
+ current_exception = exception
52
+
53
+ while current_exception.respond_to?(:cause) &&
54
+ (cause = current_exception.cause) &&
55
+ cause.is_a?(Exception) &&
56
+ !visited.include?(cause)
57
+ traces << trace_data(cause)
58
+ visited << cause
59
+ current_exception = cause
60
+ end
61
+
62
+ traces
63
+ end
64
+
65
+ def trace_data(current_exception)
66
+ {
67
+ :frames => map_frames(current_exception),
68
+ :exception => {
69
+ :class => current_exception.class.name,
70
+ :message => current_exception.message
71
+ }
72
+ }
73
+ end
74
+
75
+ def map_frames(current_exception)
76
+ frames = cleaned_backtrace(current_exception).map do |frame|
77
+ Rollbar::Item::Frame.new(self, frame,
78
+ :configuration => configuration).to_h
79
+ end
80
+ frames.reverse!
81
+ end
82
+
83
+ def cleaned_backtrace(current_exception)
84
+ normalized_backtrace = exception_backtrace(current_exception)
85
+ if configuration.backtrace_cleaner
86
+ configuration.backtrace_cleaner.clean(normalized_backtrace)
87
+ else
88
+ normalized_backtrace
89
+ end
90
+ end
91
+
92
+ # Returns the backtrace to be sent to our API. There are 3 options:
93
+ #
94
+ # 1. The exception received has a backtrace, then that backtrace is returned.
95
+ # 2. configuration.populate_empty_backtraces is disabled, we return [] here
96
+ # 3. The user has configuration.populate_empty_backtraces is enabled, then:
97
+ #
98
+ # We want to send the caller as backtrace, but the first lines of that array
99
+ # are those from the user's Rollbar.error line until this method. We want
100
+ # to remove those lines.
101
+ def exception_backtrace(current_exception)
102
+ if current_exception.backtrace.respond_to?(:map)
103
+ return current_exception.backtrace
104
+ end
105
+ return [] unless configuration.populate_empty_backtraces
106
+
107
+ caller_backtrace = caller
108
+ caller_backtrace.shift while caller_backtrace[0].include?(rollbar_lib_gem_dir)
109
+ caller_backtrace
110
+ end
111
+
112
+ def rollbar_lib_gem_dir
113
+ gem_dir = Gem::Specification.find_by_name('rollbar').gem_dir
114
+ "#{gem_dir}/lib"
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,121 @@
1
+ # We want to use Gem.path
2
+ require 'rubygems'
3
+ require 'rollbar/item/locals'
4
+
5
+ module Rollbar
6
+ class Item
7
+ # Representation of the trace data per frame in the payload
8
+ class Frame
9
+ attr_reader :backtrace, :frame, :configuration
10
+
11
+ MAX_CONTEXT_LENGTH = 4
12
+
13
+ def initialize(backtrace, frame, options = {})
14
+ @backtrace = backtrace
15
+ @frame = frame
16
+ @configuration = options[:configuration]
17
+ end
18
+
19
+ def to_h
20
+ # parse the line
21
+ match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)
22
+
23
+ return unknown_frame unless match
24
+
25
+ filename = match[1]
26
+ lineno = match[2].to_i
27
+ frame_data = {
28
+ :filename => filename,
29
+ :lineno => lineno,
30
+ :method => match[3]
31
+ }
32
+
33
+ frame_data.merge(extra_frame_data(filename, lineno))
34
+ end
35
+
36
+ private
37
+
38
+ def unknown_frame
39
+ { :filename => '<unknown>', :lineno => 0, :method => frame }
40
+ end
41
+
42
+ def extra_frame_data(filename, lineno)
43
+ file_lines = backtrace.get_file_lines(filename)
44
+
45
+ return {} if skip_extra_frame_data?(filename, file_lines)
46
+
47
+ {
48
+ :code => code_data(file_lines, lineno),
49
+ :context => context_data(file_lines, lineno),
50
+ :locals => locals_data(filename, lineno)
51
+ }
52
+ end
53
+
54
+ def skip_extra_frame_data?(filename, file_lines)
55
+ config = configuration.send_extra_frame_data
56
+ missing_file_lines = !file_lines || file_lines.empty?
57
+
58
+ return false if !missing_file_lines && config == :all
59
+
60
+ missing_file_lines ||
61
+ config == :none ||
62
+ config == :app && outside_project?(filename)
63
+ end
64
+
65
+ def outside_project?(filename)
66
+ project_gem_paths = configuration.project_gem_paths
67
+ inside_project_gem_paths = project_gem_paths.any? do |path|
68
+ filename.start_with?(path)
69
+ end
70
+
71
+ # The file is inside the configuration.project_gem_paths,
72
+ return false if inside_project_gem_paths
73
+
74
+ root = configuration.root
75
+ inside_root = root && filename.start_with?(root.to_s)
76
+
77
+ # The file is outside the configuration.root
78
+ return true unless inside_root
79
+
80
+ # At this point, the file is inside the configuration.root.
81
+ # Since it's common to have gems installed in {root}/vendor/bundle,
82
+ # let's check it's in any of the Gem.path paths
83
+ Gem.path.any? { |path| filename.start_with?(path) }
84
+ end
85
+
86
+ def code_data(file_lines, lineno)
87
+ file_lines[lineno - 1]
88
+ end
89
+
90
+ def context_data(file_lines, lineno)
91
+ {
92
+ :pre => pre_data(file_lines, lineno),
93
+ :post => post_data(file_lines, lineno)
94
+ }
95
+ end
96
+
97
+ def locals_data(filename, lineno)
98
+ return unless configuration.locals[:enabled]
99
+
100
+ ::Rollbar::Item::Locals.locals_for_location(filename, lineno)
101
+ end
102
+
103
+ def post_data(file_lines, lineno)
104
+ from_line = lineno
105
+ number_of_lines = [from_line + MAX_CONTEXT_LENGTH,
106
+ file_lines.size].min - from_line
107
+
108
+ file_lines[from_line, number_of_lines]
109
+ end
110
+
111
+ def pre_data(file_lines, lineno)
112
+ to_line = lineno - 2
113
+ from_line = [to_line - MAX_CONTEXT_LENGTH + 1, 0].max
114
+
115
+ file_lines[from_line, (to_line - from_line + 1)].select do |line|
116
+ line && !line.empty?
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,103 @@
1
+ require 'rollbar/scrubbers/params'
2
+ require 'rollbar/util'
3
+
4
+ module Rollbar
5
+ class Item
6
+ class Locals # :nodoc:
7
+ class << self
8
+ def exception_frames
9
+ Rollbar.notifier.exception_bindings
10
+ end
11
+
12
+ def locals_for_location(filename, lineno)
13
+ if (frame = frame_for_location(filename, lineno))
14
+ scrub(locals_for(frame[:binding]))
15
+ else
16
+ {}
17
+ end
18
+ end
19
+
20
+ def frame_for_location(filename, lineno)
21
+ while (frame = exception_frames.pop)
22
+ return nil unless frame
23
+ return frame if matching_frame?(frame, filename, lineno)
24
+ end
25
+ nil
26
+ end
27
+
28
+ private
29
+
30
+ def matching_frame?(frame, filename, lineno)
31
+ frame[:path] == filename && frame[:lineno].to_i <= lineno.to_i
32
+ end
33
+
34
+ def locals_for(frame)
35
+ return {} unless frame
36
+
37
+ {}.tap do |hash|
38
+ frame.local_variables.map do |var|
39
+ hash[var] = prepare_value(frame.local_variable_get(var))
40
+ end
41
+ end
42
+ end
43
+
44
+ # Prepare objects to be handled by the payload serializer.
45
+ #
46
+ # Hashes and Arrays are traversed. Then all types execpt strings and
47
+ # immediates are exported using #inspect. Sending the object itself to the
48
+ # serializer can result in large recursive expansions, especially in Rails
49
+ # environments with ActiveRecord, ActiveSupport, etc. on the stack.
50
+ # Other export options could be #to_s, #to_h, and #as_json. Several of these
51
+ # will omit the class name, or are not implemented for many types.
52
+ #
53
+ # #inspect has the advantage that it is specifically intended for debugging
54
+ # output. If the user wants more or different information in the payload
55
+ # about a specific type, #inspect is the correct place to implement it.
56
+ # Likewise the default implementation should be oriented toward usefulness
57
+ # in debugging.
58
+ #
59
+ # Because #inspect outputs a string, it can be handled well by the string
60
+ # truncation strategy for large payloads.
61
+ #
62
+ def prepare_value(value)
63
+ unless value.is_a?(Hash) || value.is_a?(Array)
64
+ return simple_value?(value) ? value : value.inspect
65
+ end
66
+
67
+ cloned_value = ::Rollbar::Util.deep_copy(value)
68
+ ::Rollbar::Util.iterate_and_update_with_block(cloned_value) do |v|
69
+ simple_value?(v) ? v : v.inspect
70
+ end
71
+
72
+ cloned_value
73
+ end
74
+
75
+ def simple_classes
76
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
77
+ [String, Symbol, Integer, Float, TrueClass, FalseClass, NilClass]
78
+ else
79
+ [String, Symbol, Fixnum, Bignum, Float, TrueClass, FalseClass, NilClass] # rubocop:disable Lint/UnifiedInteger
80
+ end
81
+ end
82
+
83
+ def simple_value?(value)
84
+ simple_classes.each do |klass|
85
+ # Use instance_of? so that subclasses and module containers will
86
+ # be treated like complex object types, not simple values.
87
+ return true if value.instance_of?(klass)
88
+ end
89
+
90
+ false
91
+ end
92
+
93
+ def scrub(hash)
94
+ Rollbar::Scrubbers::Params.call(
95
+ :params => hash,
96
+ :config => Rollbar.configuration.scrub_fields,
97
+ :whitelist => Rollbar.configuration.scrub_whitelist
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end