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
@@ -0,0 +1,913 @@
1
+ require 'rollbar/configuration'
2
+ require 'rollbar/lazy_store'
3
+ require 'rollbar/util'
4
+ require 'rollbar/json'
5
+ require 'rollbar/exceptions'
6
+ require 'rollbar/language_support'
7
+ require 'rollbar/delay/girl_friday'
8
+ require 'rollbar/delay/thread'
9
+ require 'rollbar/logger_proxy'
10
+ require 'rollbar/item'
11
+ require 'rollbar/notifier/trace_with_bindings'
12
+ require 'ostruct'
13
+
14
+ module Rollbar
15
+ # The notifier class. It has the core functionality
16
+ # for sending reports to the API.
17
+ class Notifier
18
+ attr_accessor :configuration, :last_report, :scope_object
19
+
20
+ MUTEX = Mutex.new
21
+ EXTENSION_REGEXP = /.rollbar\z/.freeze
22
+ FAILSAFE_STRING_LENGTH = 10_000
23
+
24
+ def initialize(parent_notifier = nil, payload_options = nil, scope = nil)
25
+ if parent_notifier
26
+ self.configuration = parent_notifier.configuration.clone
27
+ self.scope_object = parent_notifier.scope_object.clone
28
+
29
+ Rollbar::Util.deep_merge(scope_object, scope) if scope
30
+ else
31
+ self.configuration = ::Rollbar::Configuration.new
32
+ self.scope_object = ::Rollbar::LazyStore.new(scope)
33
+ end
34
+
35
+ return unless payload_options
36
+
37
+ Rollbar::Util.deep_merge(configuration.payload_options, payload_options)
38
+ end
39
+
40
+ def reset!
41
+ self.scope_object = ::Rollbar::LazyStore.new({})
42
+ end
43
+
44
+ # Similar to configure below, but used only internally within the gem
45
+ # to configure it without initializing any of the third party hooks
46
+ def preconfigure
47
+ yield(configuration.configured_options)
48
+ end
49
+
50
+ # Configures the notifier instance
51
+ def configure
52
+ configuration.enabled = true if configuration.enabled.nil?
53
+
54
+ yield(configuration.configured_options)
55
+ end
56
+
57
+ def reconfigure
58
+ self.configuration = Configuration.new
59
+ configuration.enabled = true
60
+
61
+ yield(configuration.configured_options)
62
+ end
63
+
64
+ def unconfigure
65
+ self.configuration = nil
66
+ end
67
+
68
+ def scope(scope_overrides = {}, config_overrides = {})
69
+ new_notifier = self.class.new(self, nil, scope_overrides)
70
+ new_notifier.configuration = configuration.merge(config_overrides)
71
+
72
+ new_notifier
73
+ end
74
+
75
+ def scope!(options = {}, config_overrides = {})
76
+ Rollbar::Util.deep_merge(scope_object, options)
77
+ configuration.merge!(config_overrides)
78
+
79
+ self
80
+ end
81
+
82
+ # Returns a new notifier with same configuration options
83
+ # but it sets Configuration#safely to true.
84
+ # We are using this flag to avoid having inifite loops
85
+ # when evaluating some custom user methods.
86
+ def safely
87
+ new_notifier = scope
88
+ new_notifier.configuration.safely = true
89
+
90
+ new_notifier
91
+ end
92
+
93
+ # Turns off reporting for the given block.
94
+ #
95
+ # @example
96
+ # Rollbar.silenced { raise }
97
+ #
98
+ # @yield Block which exceptions won't be reported.
99
+ def silenced
100
+ yield
101
+ rescue StandardError => e
102
+ e.instance_variable_set(:@_rollbar_do_not_report, true)
103
+ raise
104
+ end
105
+
106
+ # Sends a report to Rollbar.
107
+ #
108
+ # Accepts a level string plus any number of arguments. The last String
109
+ # argument will become the message or description of the report. The last
110
+ # Exception argument will become the associated exception for the report.
111
+ # The last hash argument will be used as the extra data for the report.
112
+ #
113
+ # If the extra hash contains a symbol key :custom_data_method_context
114
+ # the value of the key will be used as the context for
115
+ # configuration.custom_data_method and will be removed from the extra
116
+ # hash.
117
+ #
118
+ # @example
119
+ # begin
120
+ # foo = bar
121
+ # rescue => e
122
+ # Rollbar.log('error', e)
123
+ # end
124
+ #
125
+ # @example
126
+ # Rollbar.log('info', 'This is a simple log message')
127
+ #
128
+ # @example
129
+ # Rollbar.log('error', e, 'This is a description of the exception')
130
+ #
131
+ def log(level, *args)
132
+ return 'disabled' unless enabled?
133
+
134
+ message, exception, extra, context = extract_arguments(args)
135
+ use_exception_level_filters = use_exception_level_filters?(extra)
136
+
137
+ return 'ignored' if ignored?(exception, use_exception_level_filters) ||
138
+ ignore_before_process?(level, exception, message, extra)
139
+
140
+ level = lookup_exception_level(level, exception,
141
+ use_exception_level_filters)
142
+
143
+ ret = report_with_rescue(level, message, exception, extra, context)
144
+
145
+ raise(exception) if configuration.raise_on_error && exception
146
+
147
+ ret
148
+ end
149
+
150
+ def ignore_before_process?(level, exception, message, extra)
151
+ status = call_before_process(:level => level,
152
+ :exception => exception,
153
+ :message => message,
154
+ :extra => extra)
155
+
156
+ status == 'ignored'
157
+ rescue Rollbar::Ignore
158
+ true
159
+ end
160
+
161
+ def report_with_rescue(level, message, exception, extra, context)
162
+ report(level, message, exception, extra, context)
163
+ rescue StandardError, SystemStackError => e
164
+ original_error = {
165
+ :message => message,
166
+ :exception => exception,
167
+ :configuration => configuration
168
+ }
169
+
170
+ report_internal_error(e, original_error)
171
+
172
+ 'error'
173
+ end
174
+
175
+ # See log() above
176
+ def debug(*args)
177
+ log('debug', *args)
178
+ end
179
+
180
+ # See log() above
181
+ def info(*args)
182
+ log('info', *args)
183
+ end
184
+
185
+ # See log() above
186
+ def warn(*args)
187
+ log('warning', *args)
188
+ end
189
+
190
+ # See log() above
191
+ def warning(*args)
192
+ log('warning', *args)
193
+ end
194
+
195
+ # See log() above
196
+ def error(*args)
197
+ log('error', *args)
198
+ end
199
+
200
+ # See log() above
201
+ def critical(*args)
202
+ log('critical', *args)
203
+ end
204
+
205
+ def enabled?
206
+ # Require access_token so we don't try to send events when unconfigured.
207
+ configuration.enabled &&
208
+ configuration.access_token &&
209
+ !configuration.access_token.empty?
210
+ end
211
+
212
+ def process_item(item)
213
+ return send_item(item) unless configuration.write_to_file
214
+
215
+ return do_write_item(item) unless configuration.use_async
216
+
217
+ MUTEX.synchronize { do_write_item(item) }
218
+ rescue StandardError => e
219
+ log_error '[Rollbar] Error processing the item: ' \
220
+ "#{e.class}, #{e.message}. Item: #{item.payload.inspect}"
221
+ raise e unless via_failsafe?(item)
222
+
223
+ log_error('[Rollbar] Item has already failed. Not re-raising')
224
+ end
225
+
226
+ # We will reraise exceptions in this method so async queues
227
+ # can retry the job or, in general, handle an error report some way.
228
+ #
229
+ # At same time that exception is silenced so we don't generate
230
+ # infinite reports. This example is what we want to avoid:
231
+ #
232
+ # 1. New exception in a the project is raised
233
+ # 2. That report enqueued to Sidekiq queue.
234
+ # 3. The Sidekiq job tries to send the report to our API
235
+ # 4. The report fails, for example cause a network failure,
236
+ # and a exception is raised
237
+ # 5. We report an internal error for that exception
238
+ # 6. We reraise the exception so Sidekiq job fails and
239
+ # Sidekiq can retry the job reporting the original exception
240
+ # 7. Because the job failed and Sidekiq can be managed by rollbar we'll
241
+ # report a new exception.
242
+ # 8. Go to point 2.
243
+ #
244
+ # We'll then push to Sidekiq queue indefinitely until the network failure
245
+ # is fixed.
246
+ #
247
+ # Using Rollbar.silenced we avoid the above behavior but Sidekiq
248
+ # will have a chance to retry the original job.
249
+ def process_from_async_handler(payload)
250
+ Rollbar.silenced do
251
+ begin
252
+ if payload.is_a?(String)
253
+ # The final payload has already been built.
254
+ send_body(payload)
255
+ else
256
+ item = build_item_with_payload(payload)
257
+
258
+ process_item(item)
259
+ end
260
+ rescue StandardError => e
261
+ report_internal_error(e)
262
+
263
+ raise
264
+ end
265
+ end
266
+ end
267
+
268
+ def build_item_with_payload(payload)
269
+ Item.build_with(payload, :notifier => self,
270
+ :configuration => configuration,
271
+ :logger => logger)
272
+ end
273
+
274
+ def failsafe_initial_data(exception_reason)
275
+ {
276
+ :level => 'error',
277
+ :environment => configuration.environment.to_s,
278
+ :body => {
279
+ :message => {
280
+ :body => failsafe_body(exception_reason)
281
+ }
282
+ },
283
+ :notifier => {
284
+ :name => 'rollbar-gem',
285
+ :version => VERSION
286
+ },
287
+ :internal => true,
288
+ 'failsafe' => true
289
+ }
290
+ end
291
+
292
+ def send_failsafe(message, exception, original_error = nil)
293
+ exception_reason = failsafe_reason(message, exception)
294
+
295
+ log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"
296
+
297
+ failsafe_data = failsafe_initial_data(exception_reason)
298
+
299
+ failsafe_add_original_error_data(failsafe_data[:notifier], original_error)
300
+
301
+ failsafe_payload = {
302
+ 'data' => failsafe_data
303
+ }
304
+
305
+ process_failsafe_item(failsafe_payload)
306
+
307
+ failsafe_payload
308
+ end
309
+
310
+ def process_failsafe_item(failsafe_payload)
311
+ item = build_item_with_payload(failsafe_payload)
312
+ process_item(item)
313
+ log_and_return_item_data(item)
314
+ rescue StandardError => e
315
+ log_error "[Rollbar] Error sending failsafe : #{e}"
316
+ end
317
+
318
+ def failsafe_add_original_error_data(payload_notifier, original_error)
319
+ return unless original_error
320
+
321
+ payload_notifier[:diagnostic] ||= {}
322
+
323
+ add_original_host(payload_notifier[:diagnostic], original_error)
324
+ add_original_uuid(payload_notifier[:diagnostic], original_error)
325
+ add_original_message(payload_notifier[:diagnostic], original_error)
326
+ add_original_error(payload_notifier[:diagnostic], original_error)
327
+ add_configured_options(payload_notifier, original_error)
328
+ end
329
+
330
+ def add_original_message(diagnostic, original_error)
331
+ if original_error[:message]
332
+ diagnostic[:original_message] =
333
+ original_error[:message].truncate(FAILSAFE_STRING_LENGTH)
334
+ end
335
+ rescue StandardError => e
336
+ diagnostic[:original_message] = "Failed: #{e.message}"
337
+ end
338
+
339
+ def add_original_error(diagnostic, original_error)
340
+ if original_error[:exception]
341
+ backtrace = original_error[:exception].backtrace
342
+ message = original_error[:exception].message
343
+ diagnostic[:original_error] = {
344
+ :message => message && message.truncate(FAILSAFE_STRING_LENGTH),
345
+ :stack => backtrace && backtrace.join(', ').truncate(FAILSAFE_STRING_LENGTH)
346
+ }
347
+ end
348
+ rescue StandardError => e
349
+ diagnostic[:original_error] = "Failed: #{e.message}"
350
+ end
351
+
352
+ def add_configured_options(payload_notifier, original_error)
353
+ if original_error[:configuration]
354
+ configured = original_error[:configuration].configured_options.configured
355
+ payload_notifier[:configured_options] =
356
+ ::JSON.generate(configured).truncate(FAILSAFE_STRING_LENGTH)
357
+ end
358
+ rescue StandardError => e
359
+ payload_notifier[:configured_options] = "Failed: #{e.message}"
360
+ end
361
+
362
+ def add_original_host(diagnostic, original_error)
363
+ diagnostic[:original_host] = original_error[:host] if original_error[:host]
364
+ end
365
+
366
+ def add_original_uuid(diagnostic, original_error)
367
+ diagnostic[:original_uuid] = original_error[:uuid] if original_error[:uuid]
368
+ end
369
+
370
+ ## Logging
371
+ %w[debug info warn error].each do |level|
372
+ define_method(:"log_#{level}") do |message|
373
+ logger.send(level, message)
374
+ end
375
+ end
376
+
377
+ def logger
378
+ @logger ||= LoggerProxy.new(configuration.logger)
379
+ end
380
+
381
+ def trace_with_bindings
382
+ @trace_with_bindings ||= TraceWithBindings.new
383
+ end
384
+
385
+ def exception_bindings
386
+ trace_with_bindings.exception_frames
387
+ end
388
+
389
+ def current_bindings
390
+ trace_with_bindings.frames
391
+ end
392
+
393
+ def enable_locals?
394
+ configuration.locals[:enabled] &&
395
+ [:app, :all].include?(configuration.send_extra_frame_data)
396
+ end
397
+
398
+ def enable_locals
399
+ trace_with_bindings.enable if enable_locals?
400
+ end
401
+
402
+ def disable_locals
403
+ trace_with_bindings.disable if enable_locals?
404
+ end
405
+
406
+ private
407
+
408
+ def use_exception_level_filters?(options)
409
+ option_value = options && options.delete(:use_exception_level_filters)
410
+
411
+ return option_value unless option_value.nil?
412
+
413
+ configuration.use_exception_level_filters_default
414
+ end
415
+
416
+ def call_before_process(options)
417
+ options = options_for_handler(options)
418
+ handlers = configuration.before_process
419
+
420
+ handlers.each do |handler|
421
+ begin
422
+ status = handler.call(options)
423
+ return 'ignored' if status == 'ignored'
424
+ rescue Rollbar::Ignore
425
+ raise
426
+ rescue StandardError => e
427
+ log_error("[Rollbar] Error calling the `before_process` hook: #{e}")
428
+
429
+ break
430
+ end
431
+ end
432
+ end
433
+
434
+ def options_for_handler(options)
435
+ {
436
+ :level => options[:level],
437
+ :scope => scope_object,
438
+ :exception => options[:exception],
439
+ :message => options[:message],
440
+ :extra => options[:extra]
441
+ }
442
+ end
443
+
444
+ def extract_arguments(args)
445
+ message = exception = extra = context = nil
446
+
447
+ args.each do |arg|
448
+ if arg.is_a?(String)
449
+ message = arg
450
+ elsif arg.is_a?(Exception)
451
+ exception = arg
452
+ elsif java_exception?(arg)
453
+ exception = arg
454
+ elsif arg.is_a?(Hash)
455
+ extra = arg
456
+
457
+ context = extra.delete :custom_data_method_context
458
+
459
+ extra = nil if extra.empty?
460
+ end
461
+ end
462
+
463
+ [message, exception, extra, context]
464
+ end
465
+
466
+ def java_exception?(obj)
467
+ RUBY_PLATFORM == 'java' && obj.is_a?(java.lang.Throwable)
468
+ end
469
+
470
+ def lookup_exception_level(orig_level, exception, use_exception_level_filters)
471
+ return orig_level unless use_exception_level_filters
472
+
473
+ exception_level = filtered_level(exception)
474
+ return exception_level if exception_level
475
+
476
+ orig_level
477
+ end
478
+
479
+ def ignored?(exception, use_exception_level_filters = false)
480
+ return false unless exception
481
+ return true if use_exception_level_filters && filtered_level(exception) == 'ignore'
482
+ return true if exception.instance_variable_get(:@_rollbar_do_not_report)
483
+
484
+ false
485
+ end
486
+
487
+ def filtered_level(exception)
488
+ return unless exception
489
+
490
+ filter = configuration.exception_level_filters[exception.class.name]
491
+ if filter.respond_to?(:call)
492
+ filter.call(exception)
493
+ else
494
+ filter
495
+ end
496
+ end
497
+
498
+ def report(level, message, exception, extra, context)
499
+ unless message || exception || extra
500
+ log_error(
501
+ '[Rollbar] Tried to send a report with no message, exception or extra data.'
502
+ )
503
+
504
+ return 'error'
505
+ end
506
+
507
+ item = build_item(level, message, exception, extra, context)
508
+
509
+ return 'ignored' if item.ignored?
510
+
511
+ schedule_item(item) if configuration.transmit
512
+
513
+ log_and_return_item_data(item)
514
+ end
515
+
516
+ def log_and_return_item_data(item)
517
+ data = item['data']
518
+ log_instance_link(data)
519
+ Rollbar.last_report = data
520
+ log_data(data) if configuration.log_payload
521
+
522
+ data
523
+ end
524
+
525
+ def log_data(data)
526
+ log_info "[Rollbar] Data: #{data}"
527
+ end
528
+
529
+ # Reports an internal error in the Rollbar library. This will be reported
530
+ # within the configured Rollbar project. We'll first attempt to provide a
531
+ # report including the exception traceback. If that fails, we'll fall back
532
+ # to a more static failsafe response.
533
+ def report_internal_error(exception, original_error = nil)
534
+ return if skip_reporting_internal_error(exception)
535
+
536
+ failsafe_message = ''
537
+ log_error(
538
+ '[Rollbar] Reporting internal error encountered while sending data to Rollbar.'
539
+ )
540
+
541
+ configuration.execute_hook(:on_report_internal_error, exception)
542
+
543
+ failsafe_message = 'build_item in exception_data'
544
+ item = build_item('error', nil, exception, { :internal => true }, nil)
545
+
546
+ failsafe_message = 'error in process_item'
547
+ process_item(item)
548
+
549
+ failsafe_message = 'error logging instance link'
550
+ log_instance_link(item['data'])
551
+ rescue StandardError => e
552
+ send_failsafe(failsafe_message, e, original_error)
553
+ log_error(item ? "[Rollbar] Item: #{item}" : "[Rollbar] Exception: #{exception}")
554
+ end
555
+
556
+ def skip_reporting_internal_error(exception)
557
+ return true if configuration.ignore_internal_errors == true
558
+
559
+ configuration.ignore_internal_errors.each do |error_name|
560
+ begin
561
+ error_cls = error_name.split('::').reduce(Module, :const_get)
562
+ return true if exception.class <= error_cls
563
+ rescue NameError
564
+ # Ignore errors and continue matching.
565
+ # It's possible for a class name in the list to not be resolvable,
566
+ # and this is ok.
567
+ end
568
+ end
569
+
570
+ false
571
+ end
572
+
573
+ ## Payload building functions
574
+
575
+ def build_item(level, message, exception, extra, context)
576
+ options = {
577
+ :level => level,
578
+ :message => message,
579
+ :exception => exception,
580
+ :extra => extra,
581
+ :configuration => configuration,
582
+ :logger => logger,
583
+ :scope => scope_object,
584
+ :notifier => self,
585
+ :context => context
586
+ }
587
+
588
+ item = Item.new(options)
589
+ item.build
590
+
591
+ item
592
+ end
593
+
594
+ ## Delivery functions
595
+
596
+ def send_using_eventmachine(body)
597
+ uri = URI.parse(configuration.endpoint)
598
+
599
+ headers = { 'X-Rollbar-Access-Token' => configuration.access_token }
600
+ options = http_proxy_for_em(uri)
601
+ req = EventMachine::HttpRequest.new(uri.to_s, options).post(:body => body,
602
+ :head => headers)
603
+
604
+ eventmachine_callback(req)
605
+ eventmachine_errback(req)
606
+ end
607
+
608
+ def eventmachine_callback(req)
609
+ req.callback do
610
+ if req.response_header.status == 200
611
+ log_info '[Rollbar] Success'
612
+ else
613
+ log_warning '[Rollbar] Got unexpected status code from Rollbar.io api: ' \
614
+ "#{req.response_header.status}"
615
+ log_info "[Rollbar] Response: #{req.response}"
616
+ end
617
+ end
618
+ end
619
+
620
+ def eventmachine_errback(req)
621
+ req.errback do
622
+ log_warning(
623
+ "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
624
+ )
625
+ log_info "[Rollbar] Error's response: #{req.response}"
626
+ end
627
+ end
628
+
629
+ def send_item(item)
630
+ log_info '[Rollbar] Sending item'
631
+
632
+ body = item.dump
633
+ return unless body
634
+
635
+ if configuration.use_eventmachine
636
+ send_using_eventmachine(body)
637
+ return
638
+ end
639
+
640
+ send_body(body)
641
+ end
642
+
643
+ def send_body(body)
644
+ log_info '[Rollbar] Sending json'
645
+
646
+ uri = URI.parse(configuration.endpoint)
647
+
648
+ handle_response(do_post(uri, body, configuration.access_token))
649
+ end
650
+
651
+ def do_post(uri, body, access_token)
652
+ http = init_http(uri)
653
+
654
+ request = Net::HTTP::Post.new(uri.request_uri)
655
+
656
+ request.body = pack_ruby260_bytes(body)
657
+
658
+ # Ensure the payload token will be used if the option is set.
659
+ unless configuration.use_payload_access_token
660
+ request.add_field('X-Rollbar-Access-Token', access_token)
661
+ end
662
+
663
+ handle_net_retries { http.request(request) }
664
+ end
665
+
666
+ def init_http(uri)
667
+ proxy = http_proxy(uri)
668
+ http = Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user,
669
+ proxy.password)
670
+
671
+ init_http_timeouts(http)
672
+
673
+ if uri.scheme == 'https'
674
+ http.use_ssl = true
675
+ http.verify_mode = ssl_verify_mode
676
+ end
677
+
678
+ http
679
+ end
680
+
681
+ def init_http_timeouts(http)
682
+ http.open_timeout = configuration.open_timeout
683
+ http.read_timeout = configuration.request_timeout
684
+ end
685
+
686
+ def pack_ruby260_bytes(body)
687
+ # Ruby 2.6.0 shipped with a bug affecting multi-byte body for Net::HTTP.
688
+ # Fix (committed one day after 2.6.0p0 shipped) is here:
689
+ # ruby/ruby/commit/1680a13a926b17661329beec1ded6b32aad16c1b
690
+ #
691
+ # We work around this by repacking the body as single byte chars if needed.
692
+ if RUBY_VERSION == '2.6.0' && multibyte?(body)
693
+ body.unpack('C*').pack('C*')
694
+ else
695
+ body
696
+ end
697
+ end
698
+
699
+ def multibyte?(str)
700
+ str.chars.length != str.bytes.length
701
+ end
702
+
703
+ def http_proxy_for_em(uri)
704
+ proxy = http_proxy(uri)
705
+ {
706
+ :proxy => {
707
+ :host => proxy.host,
708
+ :port => proxy.port,
709
+ :authorization => [proxy.user, proxy.password]
710
+ }
711
+ }
712
+ end
713
+
714
+ def http_proxy(uri)
715
+ @http_proxy ||= proxy_from_config || proxy_from_env(uri) || null_proxy
716
+ end
717
+
718
+ def proxy_from_config
719
+ proxy_settings = configuration.proxy
720
+ return nil unless proxy_settings
721
+
722
+ proxy = null_proxy
723
+ proxy.host = URI.parse(proxy_settings[:host]).host
724
+ proxy.port = proxy_settings[:port]
725
+ proxy.user = proxy_settings[:user]
726
+ proxy.password = proxy_settings[:password]
727
+ proxy
728
+ end
729
+
730
+ def proxy_from_env(uri)
731
+ uri.find_proxy
732
+ end
733
+
734
+ def null_proxy
735
+ Struct.new(:host, :port, :user, :password).new
736
+ end
737
+
738
+ def handle_net_retries
739
+ retries = configuration.net_retries - 1
740
+
741
+ begin
742
+ yield
743
+ rescue *LanguageSupport.timeout_exceptions
744
+ raise if retries <= 0
745
+
746
+ retries -= 1
747
+
748
+ retry
749
+ end
750
+ end
751
+
752
+ def handle_response(response)
753
+ if response.code == '200'
754
+ log_info '[Rollbar] Success'
755
+ else
756
+ log_warning(
757
+ "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
758
+ )
759
+ log_info "[Rollbar] Response: #{response.body}"
760
+ configuration.execute_hook(:on_error_response, response)
761
+ end
762
+ end
763
+
764
+ def ssl_verify_mode
765
+ if configuration.verify_ssl_peer
766
+ OpenSSL::SSL::VERIFY_PEER
767
+ else
768
+ OpenSSL::SSL::VERIFY_NONE
769
+ end
770
+ end
771
+
772
+ def do_write_item(item)
773
+ log_info '[Rollbar] Writing item to file'
774
+
775
+ body = item.dump
776
+ return unless body
777
+
778
+ file_name = file_name_with_pid(configuration)
779
+
780
+ begin
781
+ @file ||= File.open(file_name, 'a')
782
+
783
+ @file.puts(body)
784
+ @file.flush
785
+ update_file(@file, file_name)
786
+
787
+ log_info '[Rollbar] Success'
788
+ rescue IOError => e
789
+ log_error "[Rollbar] Error opening/writing to file: #{e}"
790
+ end
791
+ end
792
+
793
+ def file_name_with_pid(configuration)
794
+ if configuration.files_with_pid_name_enabled
795
+ configuration.filepath.gsub(EXTENSION_REGEXP, "_#{Process.pid}\\0")
796
+ else
797
+ configuration.filepath
798
+ end
799
+ end
800
+
801
+ def update_file(file, file_name)
802
+ return unless configuration.files_processed_enabled
803
+
804
+ time_now = Time.now
805
+ if configuration.files_processed_duration > time_now - file.birthtime &&
806
+ file.size < configuration.files_processed_size
807
+ return
808
+ end
809
+
810
+ new_file_name = file_name.gsub(EXTENSION_REGEXP, "_processed_#{time_now.to_i}\\0")
811
+ File.rename(file, new_file_name)
812
+ file.close
813
+ @file = File.open(file_name, 'a')
814
+ end
815
+
816
+ def failsafe_reason(message, exception)
817
+ return failsafe_exception_reason(message, exception) if exception
818
+
819
+ message.to_s
820
+ rescue StandardError
821
+ log_error('[Rollbar] Error building failsafe message')
822
+ ''
823
+ end
824
+
825
+ def failsafe_exception_reason(message, exception)
826
+ backtrace = exception.backtrace || []
827
+ nearest_frame = backtrace[0]
828
+
829
+ exception_info = exception.class.name
830
+ # #to_s and #message defaults to class.to_s.
831
+ # Add message only if add valuable info.
832
+ if exception.message != exception.class.to_s
833
+ exception_info += %[: "#{exception.message}"]
834
+ end
835
+ exception_info += " in #{nearest_frame}" if nearest_frame
836
+
837
+ "#{exception_info}: #{message}"
838
+ rescue StandardError
839
+ log_error('[Rollbar] Error building failsafe exception message')
840
+ ''
841
+ end
842
+
843
+ def failsafe_body(reason)
844
+ "Failsafe from rollbar-gem. #{reason}"
845
+ end
846
+
847
+ def schedule_item(item)
848
+ return unless item
849
+
850
+ log_info '[Rollbar] Scheduling item'
851
+
852
+ if configuration.use_async
853
+ process_async_item(item)
854
+ else
855
+ process_item(item)
856
+ end
857
+ end
858
+
859
+ def default_async_handler
860
+ return Rollbar::Delay::GirlFriday if defined?(GirlFriday)
861
+
862
+ Rollbar::Delay::Thread
863
+ end
864
+
865
+ def process_async_item(item)
866
+ # Send async payloads as JSON string when async_json_payload is set.
867
+ payload = configuration.async_json_payload ? item.dump : item.payload
868
+
869
+ configuration.async_handler ||= default_async_handler
870
+ configuration.async_handler.call(payload)
871
+ rescue StandardError
872
+ if configuration.failover_handlers.empty?
873
+ log_error '[Rollbar] Async handler failed, and there are no failover ' \
874
+ 'handlers configured. See the docs for "failover_handlers"'
875
+ return
876
+ end
877
+
878
+ async_failover(item)
879
+ end
880
+
881
+ def async_failover(item)
882
+ log_warning '[Rollbar] Primary async handler failed. Trying failovers...'
883
+
884
+ failover_handlers = configuration.failover_handlers
885
+
886
+ failover_handlers.each do |handler|
887
+ begin
888
+ handler.call(item.payload)
889
+ rescue StandardError
890
+ next unless handler == failover_handlers.last
891
+
892
+ log_error '[Rollbar] All failover handlers failed while processing ' \
893
+ "item: #{Rollbar::JSON.dump(item.payload)}"
894
+ end
895
+ end
896
+ end
897
+
898
+ alias log_warning log_warn
899
+
900
+ def log_instance_link(data)
901
+ return unless data[:uuid]
902
+
903
+ uuid_url = Util.uuid_rollbar_url(data, configuration)
904
+ log_info(
905
+ "[Rollbar] Details: #{uuid_url} (only available if report was successful)"
906
+ )
907
+ end
908
+
909
+ def via_failsafe?(item)
910
+ item.payload.fetch('data', {}).fetch('failsafe', false)
911
+ end
912
+ end
913
+ end