airbrakeV4rails5 4.3.8

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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1716 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +6 -0
  5. data/INSTALL +20 -0
  6. data/LICENSE +61 -0
  7. data/README.md +148 -0
  8. data/README_FOR_HEROKU_ADDON.md +102 -0
  9. data/Rakefile +179 -0
  10. data/TESTED_AGAINST +7 -0
  11. data/airbrake.gemspec +41 -0
  12. data/bin/airbrake +12 -0
  13. data/features/metal.feature +34 -0
  14. data/features/rack.feature +60 -0
  15. data/features/rails.feature +324 -0
  16. data/features/rake.feature +33 -0
  17. data/features/sinatra.feature +126 -0
  18. data/features/step_definitions/file_steps.rb +14 -0
  19. data/features/step_definitions/rack_steps.rb +27 -0
  20. data/features/step_definitions/rails_application_steps.rb +267 -0
  21. data/features/step_definitions/rake_steps.rb +22 -0
  22. data/features/support/airbrake_shim.rb.template +11 -0
  23. data/features/support/aruba.rb +5 -0
  24. data/features/support/env.rb +39 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +156 -0
  27. data/features/support/rake/Rakefile +77 -0
  28. data/features/user_informer.feature +57 -0
  29. data/generators/airbrake/airbrake_generator.rb +94 -0
  30. data/generators/airbrake/lib/insert_commands.rb +34 -0
  31. data/generators/airbrake/lib/rake_commands.rb +24 -0
  32. data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
  33. data/generators/airbrake/templates/capistrano_hook.rb +6 -0
  34. data/generators/airbrake/templates/initializer.rb +4 -0
  35. data/install.rb +1 -0
  36. data/lib/airbrake.rb +191 -0
  37. data/lib/airbrake/backtrace.rb +103 -0
  38. data/lib/airbrake/capistrano.rb +103 -0
  39. data/lib/airbrake/capistrano3.rb +3 -0
  40. data/lib/airbrake/cli/client.rb +76 -0
  41. data/lib/airbrake/cli/options.rb +45 -0
  42. data/lib/airbrake/cli/printer.rb +33 -0
  43. data/lib/airbrake/cli/project.rb +17 -0
  44. data/lib/airbrake/cli/project_factory.rb +33 -0
  45. data/lib/airbrake/cli/runner.rb +49 -0
  46. data/lib/airbrake/cli/validator.rb +8 -0
  47. data/lib/airbrake/configuration.rb +366 -0
  48. data/lib/airbrake/jobs/send_job.rb +7 -0
  49. data/lib/airbrake/notice.rb +411 -0
  50. data/lib/airbrake/rack.rb +64 -0
  51. data/lib/airbrake/rails.rb +45 -0
  52. data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
  53. data/lib/airbrake/rails/controller_methods.rb +146 -0
  54. data/lib/airbrake/rails/error_lookup.rb +35 -0
  55. data/lib/airbrake/rails/middleware.rb +63 -0
  56. data/lib/airbrake/rails3_tasks.rb +126 -0
  57. data/lib/airbrake/railtie.rb +44 -0
  58. data/lib/airbrake/rake_handler.rb +75 -0
  59. data/lib/airbrake/response.rb +29 -0
  60. data/lib/airbrake/sender.rb +213 -0
  61. data/lib/airbrake/shared_tasks.rb +59 -0
  62. data/lib/airbrake/sidekiq.rb +8 -0
  63. data/lib/airbrake/sinatra.rb +40 -0
  64. data/lib/airbrake/tasks.rb +81 -0
  65. data/lib/airbrake/tasks/airbrake.cap +28 -0
  66. data/lib/airbrake/user_informer.rb +36 -0
  67. data/lib/airbrake/utils/params_cleaner.rb +141 -0
  68. data/lib/airbrake/utils/rack_filters.rb +45 -0
  69. data/lib/airbrake/version.rb +3 -0
  70. data/lib/airbrake_tasks.rb +62 -0
  71. data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
  72. data/lib/templates/rescue.erb +91 -0
  73. data/rails/init.rb +1 -0
  74. data/resources/README.md +34 -0
  75. data/resources/airbrake_2_4.xsd +89 -0
  76. data/resources/airbrake_3_0.json +52 -0
  77. data/resources/ca-bundle.crt +3376 -0
  78. data/script/integration_test.rb +35 -0
  79. data/test/airbrake_tasks_test.rb +161 -0
  80. data/test/backtrace_test.rb +215 -0
  81. data/test/capistrano_test.rb +44 -0
  82. data/test/configuration_test.rb +303 -0
  83. data/test/controller_methods_test.rb +230 -0
  84. data/test/helper.rb +233 -0
  85. data/test/integration.rb +13 -0
  86. data/test/integration/catcher_test.rb +371 -0
  87. data/test/logger_test.rb +79 -0
  88. data/test/notice_test.rb +494 -0
  89. data/test/notifier_test.rb +288 -0
  90. data/test/params_cleaner_test.rb +204 -0
  91. data/test/rack_test.rb +62 -0
  92. data/test/rails_initializer_test.rb +36 -0
  93. data/test/recursion_test.rb +10 -0
  94. data/test/response_test.rb +18 -0
  95. data/test/sender_test.rb +335 -0
  96. data/test/support/response_shim.xml +4 -0
  97. data/test/user_informer_test.rb +29 -0
  98. metadata +469 -0
@@ -0,0 +1,7 @@
1
+ class SendJob
2
+ include SuckerPunch::Job if defined?(SuckerPunch)
3
+
4
+ def perform(notice)
5
+ Airbrake.sender.send_to_airbrake(notice)
6
+ end
7
+ end
@@ -0,0 +1,411 @@
1
+ require 'builder'
2
+ require 'socket'
3
+ require 'multi_json'
4
+
5
+ module Airbrake
6
+ class Notice
7
+
8
+ class << self
9
+ def attr_reader_with_tracking(*names)
10
+ attr_readers.concat(names)
11
+ attr_reader_without_tracking(*names)
12
+ end
13
+
14
+ alias_method :attr_reader_without_tracking, :attr_reader
15
+ alias_method :attr_reader, :attr_reader_with_tracking
16
+
17
+
18
+ def attr_readers
19
+ @attr_readers ||= []
20
+ end
21
+ end
22
+
23
+ # The exception that caused this notice, if any
24
+ attr_reader :exception
25
+
26
+ # The API key for the project to which this notice should be sent
27
+ attr_reader :api_key
28
+
29
+ # The backtrace from the given exception or hash.
30
+ attr_reader :backtrace
31
+
32
+ # The name of the class of error (such as RuntimeError)
33
+ attr_reader :error_class
34
+
35
+ # The name of the server environment (such as "production")
36
+ attr_reader :environment_name
37
+
38
+ # CGI variables such as HTTP_METHOD
39
+ attr_reader :cgi_data
40
+
41
+ # The message from the exception, or a general description of the error
42
+ attr_reader :error_message
43
+
44
+ # See Configuration#backtrace_filters
45
+ attr_reader :backtrace_filters
46
+
47
+ # See Configuration#params_filters
48
+ attr_reader :params_filters
49
+
50
+ # See Configuration#params_whitelist_filters
51
+ attr_reader :params_whitelist_filters
52
+
53
+ # A hash of parameters from the query string or post body.
54
+ attr_reader :parameters
55
+ alias_method :params, :parameters
56
+
57
+ # The component (if any) which was used in this request (usually the controller)
58
+ attr_reader :component
59
+ alias_method :controller, :component
60
+
61
+ # The action (if any) that was called in this request
62
+ attr_reader :action
63
+
64
+ # A hash of session data from the request
65
+ attr_reader :session_data
66
+
67
+ # The path to the project that caused the error (usually Rails.root)
68
+ attr_reader :project_root
69
+
70
+ # The URL at which the error occurred (if any)
71
+ attr_reader :url
72
+
73
+ # See Configuration#ignore
74
+ attr_reader :ignore
75
+
76
+ # See Configuration#ignore_by_filters
77
+ attr_reader :ignore_by_filters
78
+
79
+ # The name of the notifier library sending this notice, such as "Airbrake Notifier"
80
+ attr_reader :notifier_name
81
+
82
+ # The version number of the notifier library sending this notice, such as "2.1.3"
83
+ attr_reader :notifier_version
84
+
85
+ # A URL for more information about the notifier library sending this notice
86
+ attr_reader :notifier_url
87
+
88
+ # The host name where this error occurred (if any)
89
+ attr_reader :hostname
90
+
91
+ # Details about the user who experienced the error
92
+ attr_reader :user
93
+
94
+ # Instance that's used for cleaning out data that should be filtered out, should respond to #clean
95
+ attr_accessor :cleaner
96
+
97
+ # An array of the exception classes for this error (including wrapped ones)
98
+ attr_reader :exception_classes
99
+
100
+ public
101
+
102
+ def initialize(args)
103
+ @args = args
104
+ @exception = args[:exception]
105
+ @api_key = args[:api_key]
106
+ @project_root = args[:project_root]
107
+ @url = args[:url] || rack_env(:url)
108
+ @notifier_name = args[:notifier_name]
109
+ @notifier_version = args[:notifier_version]
110
+ @notifier_url = args[:notifier_url]
111
+
112
+ @ignore = args[:ignore] || []
113
+ @ignore_by_filters = args[:ignore_by_filters] || []
114
+ @backtrace_filters = args[:backtrace_filters] || []
115
+ @params_filters = args[:params_filters] || []
116
+ @params_whitelist_filters = args[:params_whitelist_filters] || []
117
+
118
+ @parameters = args[:parameters] ||
119
+ action_dispatch_params ||
120
+ rack_env(:params) ||
121
+ {}
122
+ @component = args[:component] || args[:controller] || parameters['controller']
123
+ @action = args[:action] || parameters['action']
124
+
125
+ @environment_name = args[:environment_name]
126
+ @cgi_data = (args[:cgi_data].respond_to?(:to_hash) && args[:cgi_data].to_hash.dup) || args[:rack_env] || {}
127
+ @backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => @backtrace_filters)
128
+ @error_class = exception_attribute(:error_class) {|exception| exception.class.name }
129
+ @error_message = exception_attribute(:error_message, 'Notification') do |exception|
130
+ "#{exception.class.name}: #{args[:error_message] || exception.message}"
131
+ end
132
+
133
+ @hostname = local_hostname
134
+ @user = args[:user] || {}
135
+
136
+ @exception_classes= Array(args[:exception_classes])
137
+ if @exception
138
+ @exception_classes << @exception.class
139
+ end
140
+ if @error_class
141
+ @exception_classes << @error_class
142
+ end
143
+
144
+
145
+ also_use_rack_params_filters
146
+ find_session_data
147
+
148
+ @cleaner = args[:cleaner] ||
149
+ Airbrake::Utils::ParamsCleaner.new(:blacklist_filters => params_filters,
150
+ :whitelist_filters => params_whitelist_filters,
151
+ :to_clean => data_to_clean)
152
+
153
+ clean_data!
154
+ end
155
+
156
+ # Converts the given notice to XML
157
+ def to_xml
158
+ builder = Builder::XmlMarkup.new
159
+ builder.instruct!
160
+ xml = builder.notice(:version => Airbrake::API_VERSION) do |notice|
161
+ notice.tag!("api-key", api_key)
162
+ notice.notifier do |notifier|
163
+ notifier.name(notifier_name)
164
+ notifier.version(notifier_version)
165
+ notifier.url(notifier_url)
166
+ end
167
+ notice.tag!('error') do |error|
168
+ error.tag!('class', error_class)
169
+ error.message(error_message)
170
+ error.backtrace do |backtrace|
171
+ self.backtrace.lines.each do |line|
172
+ backtrace.line(
173
+ :number => line.number,
174
+ :file => line.file,
175
+ :method => line.method_name
176
+ )
177
+ end
178
+ end
179
+ end
180
+ if request_present?
181
+ notice.request do |request|
182
+ request.url(url)
183
+ request.component(controller)
184
+ request.action(action)
185
+ unless parameters.empty?
186
+ request.params do |params|
187
+ xml_vars_for(params, parameters)
188
+ end
189
+ end
190
+ unless session_data.empty?
191
+ request.session do |session|
192
+ xml_vars_for(session, session_data)
193
+ end
194
+ end
195
+ unless cgi_data.empty?
196
+ request.tag!("cgi-data") do |cgi_datum|
197
+ xml_vars_for(cgi_datum, cgi_data)
198
+ end
199
+ end
200
+ end
201
+ end
202
+ notice.tag!("server-environment") do |env|
203
+ env.tag!("project-root", project_root)
204
+ env.tag!("environment-name", environment_name)
205
+ env.tag!("hostname", hostname)
206
+ end
207
+ unless user.empty?
208
+ notice.tag!("current-user") do |u|
209
+ user.each do |attr, value|
210
+ u.tag!(attr.to_s, value)
211
+ end
212
+ end
213
+ end
214
+ if framework =~ /\S/
215
+ notice.tag!("framework", framework)
216
+ end
217
+ end
218
+ xml.to_s
219
+ end
220
+
221
+ def to_json
222
+ MultiJson.dump({
223
+ 'notifier' => {
224
+ 'name' => 'airbrake',
225
+ 'version' => Airbrake::VERSION,
226
+ 'url' => 'https://github.com/airbrake/airbrake'
227
+ },
228
+ 'errors' => [{
229
+ 'type' => error_class,
230
+ 'message' => error_message,
231
+ 'backtrace' => backtrace.lines.map do |line|
232
+ {
233
+ 'file' => line.file,
234
+ 'line' => line.number.to_i,
235
+ 'function' => line.method_name
236
+ }
237
+ end
238
+ }],
239
+ 'context' => {}.tap do |hash|
240
+ if request_present?
241
+ hash['url'] = url
242
+ hash['component'] = controller
243
+ hash['action'] = action
244
+ hash['rootDirectory'] = File.dirname(project_root)
245
+ hash['environment'] = environment_name
246
+ end
247
+ end.tap do |hash|
248
+ next if user.empty?
249
+
250
+ hash['userId'] = user[:id]
251
+ hash['userName'] = user[:name]
252
+ hash['userEmail'] = user[:email]
253
+ end
254
+
255
+ }.tap do |hash|
256
+ hash['environment'] = cgi_data unless cgi_data.empty?
257
+ hash['params'] = parameters unless parameters.empty?
258
+ hash['session'] = session_data unless session_data.empty?
259
+ end)
260
+ end
261
+
262
+ # Determines if this notice should be ignored
263
+ def ignore?
264
+ exception_classes.each do |klass|
265
+ if ignored_class_names.include?(klass)
266
+ return true
267
+ end
268
+ end
269
+
270
+ ignore_by_filters.any? {|filter| filter.call(self) }
271
+ end
272
+
273
+ # Allows properties to be accessed using a hash-like syntax
274
+ #
275
+ # @example
276
+ # notice[:error_message]
277
+ # @param [String] method The given key for an attribute
278
+ # @return The attribute value, or self if given +:request+
279
+ def [](method)
280
+ case method
281
+ when :request
282
+ self
283
+ else
284
+ send(method)
285
+ end
286
+ end
287
+
288
+ private
289
+
290
+ def request_present?
291
+ url ||
292
+ controller ||
293
+ action ||
294
+ !parameters.empty? ||
295
+ !cgi_data.empty? ||
296
+ !session_data.empty?
297
+ end
298
+
299
+ # Gets a property named +attribute+ of an exception, either from an actual
300
+ # exception or a hash.
301
+ #
302
+ # If an exception is available, #from_exception will be used. Otherwise,
303
+ # a key named +attribute+ will be used from the #args.
304
+ #
305
+ # If no exception or hash key is available, +default+ will be used.
306
+ def exception_attribute(attribute, default = nil, &block)
307
+ (exception && from_exception(attribute, &block)) || @args[attribute] || default
308
+ end
309
+
310
+ # Gets a property named +attribute+ from an exception.
311
+ #
312
+ # If a block is given, it will be used when getting the property from an
313
+ # exception. The block should accept and exception and return the value for
314
+ # the property.
315
+ #
316
+ # If no block is given, a method with the same name as +attribute+ will be
317
+ # invoked for the value.
318
+ def from_exception(attribute)
319
+ if block_given?
320
+ yield(exception)
321
+ else
322
+ exception.send(attribute)
323
+ end
324
+ end
325
+
326
+ # Replaces the contents of params that match params_filters.
327
+ def clean_data!
328
+ cleaner.clean.tap do |c|
329
+ @parameters = c.parameters
330
+ @cgi_data = c.cgi_data
331
+ @session_data = c.session_data
332
+ end
333
+ end
334
+
335
+ def data_to_clean
336
+ {:parameters => parameters,
337
+ :cgi_data => cgi_data,
338
+ :session_data => session_data}
339
+ end
340
+
341
+ def find_session_data
342
+ @session_data = @args[:session_data] || @args[:session] || rack_session || {}
343
+ @session_data = session_data[:data] if session_data[:data]
344
+ end
345
+
346
+ # Converts the mixed class instances and class names into just names
347
+ # TODO: move this into Configuration or another class
348
+ def ignored_class_names
349
+ ignore.collect do |string_or_class|
350
+ if string_or_class.respond_to?(:name)
351
+ string_or_class.name
352
+ else
353
+ string_or_class
354
+ end
355
+ end
356
+ end
357
+
358
+ def xml_vars_for(builder, hash)
359
+ hash.each do |key, value|
360
+ if value.respond_to?(:to_hash)
361
+ builder.var(:key => key){|b| xml_vars_for(b, value.to_hash) }
362
+ else
363
+ builder.var(value.to_s, :key => key)
364
+ end
365
+ end
366
+ end
367
+
368
+ def rack_env(method)
369
+ rack_request.send(method) if rack_request
370
+ rescue
371
+ {:message => "failed to call #{method} on Rack::Request -- #{$!.message}"}
372
+ end
373
+
374
+ def rack_request
375
+ @rack_request ||= if @args[:rack_env]
376
+ ::Rack::Request.new(@args[:rack_env])
377
+ end
378
+ end
379
+
380
+ def action_dispatch_params
381
+ @args[:rack_env]['action_dispatch.request.parameters'] if @args[:rack_env]
382
+ end
383
+
384
+ def rack_session
385
+ @args[:rack_env]['rack.session'] if @args[:rack_env]
386
+ end
387
+
388
+ def also_use_rack_params_filters
389
+ if cgi_data
390
+ @params_filters ||= []
391
+ @params_filters += cgi_data["action_dispatch.parameter_filter"] || []
392
+ end
393
+ end
394
+
395
+ def local_hostname
396
+ Socket.gethostname
397
+ end
398
+
399
+ def framework
400
+ Airbrake.configuration.framework
401
+ end
402
+
403
+ def to_s
404
+ content = []
405
+ self.class.attr_readers.each do |attr|
406
+ content << " #{attr}: #{send(attr)}"
407
+ end
408
+ content.join("\n")
409
+ end
410
+ end
411
+ end
@@ -0,0 +1,64 @@
1
+ module Airbrake
2
+ # Middleware for Rack applications. Any errors raised by the upstream
3
+ # application will be delivered to Airbrake and re-raised.
4
+ #
5
+ # Synopsis:
6
+ #
7
+ # require 'rack'
8
+ # require 'airbrake'
9
+ #
10
+ # Airbrake.configure do |config|
11
+ # config.api_key = 'my_api_key'
12
+ # #can also set the environment over here
13
+ # config.environment_name = ENV['RACK_ENV'] || "development"
14
+ # end
15
+ #
16
+ # app = Rack::Builder.app do
17
+ # run lambda { |env| raise "Rack down" }
18
+ # end
19
+ #
20
+ # use Airbrake::Rack
21
+ # run app
22
+ #
23
+ # Use a standard Airbrake.configure call to configure your api key.
24
+ class Rack
25
+ def initialize(app)
26
+ @app = app
27
+ Airbrake.configuration.framework = "Rack: #{::Rack.release}"
28
+ end
29
+
30
+ def ignored_user_agent?(env)
31
+ true if Airbrake.
32
+ configuration.
33
+ ignore_user_agent.
34
+ flatten.
35
+ any? { |ua| ua === env['HTTP_USER_AGENT'] }
36
+ end
37
+
38
+ def notify_airbrake(exception, env)
39
+ unless ignored_user_agent?(env)
40
+ Airbrake.notify_or_ignore(exception, :rack_env => env)
41
+ end
42
+ end
43
+
44
+ def call(env)
45
+ begin
46
+ response = @app.call(env)
47
+ rescue Exception => raised
48
+ env['airbrake.error_id'] = notify_airbrake(raised, env)
49
+ raise raised
50
+ end
51
+
52
+ if framework_exception(env)
53
+ env['airbrake.error_id'] = notify_airbrake(framework_exception(env), env)
54
+ end
55
+
56
+ response
57
+ end
58
+
59
+ def framework_exception(env)
60
+ env['rack.exception'] || env['sinatra.error']
61
+ end
62
+
63
+ end
64
+ end