airbrake-ruby 4.8.0 → 4.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +101 -25
  3. data/lib/airbrake-ruby/async_sender.rb +3 -3
  4. data/lib/airbrake-ruby/backtrace.rb +2 -2
  5. data/lib/airbrake-ruby/benchmark.rb +1 -1
  6. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  7. data/lib/airbrake-ruby/config.rb +1 -1
  8. data/lib/airbrake-ruby/config/validator.rb +3 -3
  9. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  10. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  11. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -2
  12. data/lib/airbrake-ruby/filters/keys_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/sql_filter.rb +3 -3
  14. data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
  15. data/lib/airbrake-ruby/grouppable.rb +12 -0
  16. data/lib/airbrake-ruby/inspectable.rb +2 -2
  17. data/lib/airbrake-ruby/mergeable.rb +12 -0
  18. data/lib/airbrake-ruby/notice.rb +7 -7
  19. data/lib/airbrake-ruby/notice_notifier.rb +3 -2
  20. data/lib/airbrake-ruby/performance_breakdown.rb +12 -6
  21. data/lib/airbrake-ruby/performance_notifier.rb +69 -22
  22. data/lib/airbrake-ruby/query.rb +15 -11
  23. data/lib/airbrake-ruby/queue.rb +56 -0
  24. data/lib/airbrake-ruby/request.rb +14 -12
  25. data/lib/airbrake-ruby/stat.rb +1 -1
  26. data/lib/airbrake-ruby/version.rb +1 -1
  27. data/spec/airbrake_spec.rb +135 -45
  28. data/spec/async_sender_spec.rb +4 -4
  29. data/spec/backtrace_spec.rb +18 -18
  30. data/spec/code_hunk_spec.rb +9 -9
  31. data/spec/config/validator_spec.rb +5 -5
  32. data/spec/config_spec.rb +5 -9
  33. data/spec/deploy_notifier_spec.rb +2 -2
  34. data/spec/filter_chain_spec.rb +1 -1
  35. data/spec/filters/dependency_filter_spec.rb +1 -1
  36. data/spec/filters/gem_root_filter_spec.rb +5 -5
  37. data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
  38. data/spec/filters/git_repository_filter.rb +1 -1
  39. data/spec/filters/git_revision_filter_spec.rb +10 -10
  40. data/spec/filters/keys_blacklist_spec.rb +22 -22
  41. data/spec/filters/keys_whitelist_spec.rb +21 -21
  42. data/spec/filters/root_directory_filter_spec.rb +5 -5
  43. data/spec/filters/sql_filter_spec.rb +53 -55
  44. data/spec/filters/system_exit_filter_spec.rb +1 -1
  45. data/spec/filters/thread_filter_spec.rb +28 -28
  46. data/spec/fixtures/project_root/code.rb +9 -9
  47. data/spec/notice_notifier/options_spec.rb +12 -12
  48. data/spec/notice_notifier_spec.rb +18 -18
  49. data/spec/notice_spec.rb +5 -5
  50. data/spec/performance_breakdown_spec.rb +11 -0
  51. data/spec/performance_notifier_spec.rb +243 -72
  52. data/spec/query_spec.rb +11 -1
  53. data/spec/queue_spec.rb +21 -0
  54. data/spec/request_spec.rb +11 -1
  55. data/spec/response_spec.rb +8 -8
  56. data/spec/spec_helper.rb +2 -2
  57. data/spec/stat_spec.rb +2 -2
  58. data/spec/sync_sender_spec.rb +12 -12
  59. data/spec/tdigest_spec.rb +6 -6
  60. data/spec/thread_pool_spec.rb +5 -5
  61. data/spec/timed_trace_spec.rb +1 -1
  62. data/spec/truncator_spec.rb +12 -12
  63. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb6fd7fd474a31d89d2b532d467f5c319399b183a5805488bbb57789bc502bef
4
- data.tar.gz: 83493c7a39f6eea49668f7c94fc202c90d55efcc1d4822864a70cca03c96134f
3
+ metadata.gz: 6b9d78ba8789ccfa27f139a5ad0fef6b544da9bde4239dfda22cc543493bdb45
4
+ data.tar.gz: c32c7e64425037221491c64de8e22d548815929ec6e3a0289aad3c74f8ffedc7
5
5
  SHA512:
6
- metadata.gz: 149075c2e3a3cd9f43f19f7309f8b9e46aa36bde2994bc48b80c8b66a4c675d1fef2989e2020e5bc81dc55a8e8490f18aa445307dd2212f7c2467a3186fb1c8d
7
- data.tar.gz: 030edc9303bd0f1042a6aa3f3ec5b672ab58598fee90a71e6d7f6a6fed84716d2043afab9edf46b3bdd6ee9939ef6b979ad78dc37575be774663ac84e8af73a7
6
+ metadata.gz: 711d058e48af32390e7b92243171014122b5e454cea35afd19ca72e5fe39d04b919f0cbc95dfdf23685a3114cac14430310eb5a0c785c263d85a52ca26bab861
7
+ data.tar.gz: 300176c0bab2a6ddcb4b3453069be19ef5a477021ca10f68256edabc97c9d0d15b3b4a5659f2676e53071f89229b59c90ac6d0f1016d953342af8d9fc8b27581
@@ -9,6 +9,8 @@ require 'time'
9
9
  require 'airbrake-ruby/version'
10
10
  require 'airbrake-ruby/loggable'
11
11
  require 'airbrake-ruby/stashable'
12
+ require 'airbrake-ruby/mergeable'
13
+ require 'airbrake-ruby/grouppable'
12
14
  require 'airbrake-ruby/config'
13
15
  require 'airbrake-ruby/config/validator'
14
16
  require 'airbrake-ruby/promise'
@@ -52,6 +54,7 @@ require 'airbrake-ruby/performance_breakdown'
52
54
  require 'airbrake-ruby/benchmark'
53
55
  require 'airbrake-ruby/monotonic_time'
54
56
  require 'airbrake-ruby/timed_trace'
57
+ require 'airbrake-ruby/queue'
55
58
 
56
59
  # Airbrake is a thin wrapper around instances of the notifier classes (such as
57
60
  # notice, performance & deploy notifiers). It creates a way to access them via a
@@ -69,6 +72,7 @@ require 'airbrake-ruby/timed_trace'
69
72
  #
70
73
  # @since v1.0.0
71
74
  # @api public
75
+ # rubocop:disable Metrics/ModuleLength
72
76
  module Airbrake
73
77
  # The general error that this library uses when it wants to raise.
74
78
  Error = Class.new(StandardError)
@@ -131,7 +135,7 @@ module Airbrake
131
135
  # @return [Boolean] true if the notifier was configured, false otherwise
132
136
  # @since v2.3.0
133
137
  def configured?
134
- notice_notifier.configured?
138
+ @notice_notifier && @notice_notifier.configured?
135
139
  end
136
140
 
137
141
  # Sends an exception to Airbrake asynchronously.
@@ -251,10 +255,17 @@ module Airbrake
251
255
  # Airbrake.notify('App crashed!') #=> raises Airbrake::Error
252
256
  #
253
257
  # @return [void]
258
+ # rubocop:disable Style/GuardClause, Style/IfUnlessModifier
254
259
  def close
255
- notice_notifier.close
256
- performance_notifier.close
260
+ if defined?(@notice_notifier) && @notice_notifier
261
+ @notice_notifier.close
262
+ end
263
+
264
+ if defined?(@performance_notifier) && @performance_notifier
265
+ @performance_notifier.close
266
+ end
257
267
  end
268
+ # rubocop:enable Style/GuardClause, Style/IfUnlessModifier
258
269
 
259
270
  # Pings the Airbrake Deploy API endpoint about the occurred deploy.
260
271
  #
@@ -317,9 +328,8 @@ module Airbrake
317
328
  notice_notifier.merge_context(context)
318
329
  end
319
330
 
320
- # Increments request statistics of a certain +route+ that was invoked on
321
- # +start_time+ and ended on +end_time+ with +method+, and returned
322
- # +status_code+.
331
+ # Increments request statistics of a certain +route+ invoked with +method+,
332
+ # which returned +status_code+.
323
333
  #
324
334
  # After a certain amount of time (n seconds) the aggregated route
325
335
  # information will be sent to Airbrake.
@@ -332,8 +342,7 @@ module Airbrake
332
342
  # func: 'do_stuff',
333
343
  # file: 'app/models/foo.rb',
334
344
  # line: 452,
335
- # start_time: timestamp,
336
- # end_time: Time.now
345
+ # timing: 123.45 # ms
337
346
  # )
338
347
  #
339
348
  # @param [Hash{Symbol=>Object}] request_info
@@ -347,8 +356,8 @@ module Airbrake
347
356
  # called the query (optional)
348
357
  # @option request_info [Integer] :line The line that executes the query
349
358
  # (optional)
350
- # @option request_info [Date] :start_time When the request started
351
- # @option request_info [Time] :end_time When the request ended (optional)
359
+ # @option request_info [Float] :timing How much time it took to process the
360
+ # request (in ms)
352
361
  # @param [Hash] stash What needs to be appeneded to the stash, so it's
353
362
  # available in filters
354
363
  # @return [void]
@@ -360,9 +369,18 @@ module Airbrake
360
369
  performance_notifier.notify(request)
361
370
  end
362
371
 
363
- # Increments SQL statistics of a certain +query+ that was invoked on
364
- # +start_time+ and finished on +end_time+. When +method+ and +route+ are
365
- # provided, the query is grouped by these parameters.
372
+ # Synchronously increments request statistics of a certain +route+ invoked
373
+ # with +method+, which returned +status_code+.
374
+ # @since v4.10.0
375
+ # @see .notify_request
376
+ def notify_request_sync(request_info, stash = {})
377
+ request = Request.new(request_info)
378
+ request.stash.merge!(stash)
379
+ performance_notifier.notify_sync(request)
380
+ end
381
+
382
+ # Increments SQL statistics of a certain +query+. When +method+ and +route+
383
+ # are provided, the query is grouped by these parameters.
366
384
  #
367
385
  # After a certain amount of time (n seconds) the aggregated query
368
386
  # information will be sent to Airbrake.
@@ -372,18 +390,17 @@ module Airbrake
372
390
  # method: 'GET',
373
391
  # route: '/things',
374
392
  # query: 'SELECT * FROM things',
375
- # start_time: timestamp,
376
- # end_time: Time.now
393
+ # timing: 123.45 # ms
377
394
  # )
378
395
  #
379
396
  # @param [Hash{Symbol=>Object}] query_info
380
- # @option request_info [String] :method The HTTP method that triggered this
397
+ # @option query_info [String] :method The HTTP method that triggered this
381
398
  # SQL query (optional)
382
- # @option request_info [String] :route The route that triggered this SQL
399
+ # @option query_info [String] :route The route that triggered this SQL
383
400
  # query (optional)
384
- # @option request_info [String] :query The query that was executed
385
- # @option request_info [Date] :start_time When the query started executing
386
- # @option request_info [Time] :end_time When the query finished (optional)
401
+ # @option query_info [String] :query The query that was executed
402
+ # @option query_info [Float] :timing How much time it took to process the
403
+ # query (in ms)
387
404
  # @param [Hash] stash What needs to be appeneded to the stash, so it's
388
405
  # available in filters
389
406
  # @return [void]
@@ -395,6 +412,17 @@ module Airbrake
395
412
  performance_notifier.notify(query)
396
413
  end
397
414
 
415
+ # Synchronously increments SQL statistics of a certain +query+. When
416
+ # +method+ and +route+ are provided, the query is grouped by these
417
+ # parameters.
418
+ # @since v4.10.0
419
+ # @see .notify_query
420
+ def notify_query_sync(query_info, stash = {})
421
+ query = Query.new(query_info)
422
+ query.stash.merge!(stash)
423
+ performance_notifier.notify_sync(query)
424
+ end
425
+
398
426
  # Increments performance breakdown statistics of a certain route.
399
427
  #
400
428
  # @example
@@ -403,8 +431,7 @@ module Airbrake
403
431
  # route: '/thing/:id/create',
404
432
  # response_type: 'json',
405
433
  # groups: { db: 24.0, view: 0.4 }, # ms
406
- # start_time: timestamp,
407
- # end_time: Time.now
434
+ # timing: 123.45 # ms
408
435
  # )
409
436
  #
410
437
  # @param [Hash{Symbol=>Object}] breakdown_info
@@ -412,7 +439,8 @@ module Airbrake
412
439
  # @option breakdown_info [String] :route
413
440
  # @option breakdown_info [String] :response_type
414
441
  # @option breakdown_info [Array<Hash{Symbol=>Float}>] :groups
415
- # @option breakdown_info [Date] :start_time
442
+ # @option breakdown_info [Float] :timing How much time it took to process
443
+ # the performance breakdown (in ms)
416
444
  # @param [Hash] stash What needs to be appeneded to the stash, so it's
417
445
  # available in filters
418
446
  # @return [void]
@@ -423,6 +451,53 @@ module Airbrake
423
451
  performance_notifier.notify(performance_breakdown)
424
452
  end
425
453
 
454
+ # Increments performance breakdown statistics of a certain route
455
+ # synchronously.
456
+ # @since v4.10.0
457
+ # @see .notify_performance_breakdown
458
+ def notify_performance_breakdown_sync(breakdown_info, stash = {})
459
+ performance_breakdown = PerformanceBreakdown.new(breakdown_info)
460
+ performance_breakdown.stash.merge!(stash)
461
+ performance_notifier.notify_sync(performance_breakdown)
462
+ end
463
+
464
+ # Increments statistics of a certain queue (worker).
465
+ #
466
+ # @example
467
+ # Airbrake.notify_queue(
468
+ # queue: 'emails',
469
+ # error_count: 1,
470
+ # groups: { redis: 24.0, sql: 0.4 } # ms
471
+ # )
472
+ #
473
+ # @param [Hash{Symbol=>Object}] queue_info
474
+ # @option queue_info [String] :queue The name of the queue/worker
475
+ # @option queue_info [Integer] :error_count How many times this worker
476
+ # failed
477
+ # @option queue_info [Array<Hash{Symbol=>Float}>] :groups Where the job
478
+ # spent its time
479
+ # @option breakdown_info [Float] :timing How much time it took to process
480
+ # the queue (in ms)
481
+ # @param [Hash] stash What needs to be appended to the stash, so it's
482
+ # available in filters
483
+ # @return [void]
484
+ # @since v4.9.0
485
+ # @see .notify_queue_sync
486
+ def notify_queue(queue_info, stash = {})
487
+ queue = Queue.new(queue_info)
488
+ queue.stash.merge!(stash)
489
+ performance_notifier.notify(queue)
490
+ end
491
+
492
+ # Increments statistics of a certain queue (worker) synchronously.
493
+ # @since v4.10.0
494
+ # @see .notify_queue
495
+ def notify_queue_sync(queue_info, stash = {})
496
+ queue = Queue.new(queue_info)
497
+ queue.stash.merge!(stash)
498
+ performance_notifier.notify_sync(queue)
499
+ end
500
+
426
501
  # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
427
502
  # is useful if you want to ignore specific resources or filter the data the
428
503
  # resource contains.
@@ -480,7 +555,7 @@ module Airbrake
480
555
  # @return [void]
481
556
  # @since v4.2.2
482
557
  def reset
483
- close if notice_notifier && configured?
558
+ close
484
559
 
485
560
  self.performance_notifier = PerformanceNotifier.new
486
561
  self.notice_notifier = NoticeNotifier.new
@@ -506,10 +581,11 @@ module Airbrake
506
581
  Airbrake::Filters::RootDirectoryFilter,
507
582
  Airbrake::Filters::GitRevisionFilter,
508
583
  Airbrake::Filters::GitRepositoryFilter,
509
- Airbrake::Filters::GitLastCheckoutFilter
584
+ Airbrake::Filters::GitLastCheckoutFilter,
510
585
  ].each do |filter|
511
586
  notice_notifier.add_filter(filter.new(config.root_directory))
512
587
  end
513
588
  end
514
589
  end
515
590
  end
591
+ # rubocop:enable Metrics/ModuleLength
@@ -54,7 +54,7 @@ module Airbrake
54
54
  ThreadPool.new(
55
55
  worker_size: @config.workers,
56
56
  queue_size: @config.queue_size,
57
- block: proc { |args| sender.send(*args) }
57
+ block: proc { |args| sender.send(*args) },
58
58
  )
59
59
  end
60
60
  end
@@ -71,8 +71,8 @@ module Airbrake
71
71
  message: error[:message],
72
72
  backtrace: error[:backtrace].map do |line|
73
73
  "#{line[:file]}:#{line[:line]} in `#{line[:function]}'"
74
- end.join("\n")
75
- )
74
+ end.join("\n"),
75
+ ),
76
76
  )
77
77
  promise.reject("AsyncSender has reached its capacity of #{@config.queue_size}")
78
78
  end
@@ -147,13 +147,13 @@ module Airbrake
147
147
  return {
148
148
  file: match[:file],
149
149
  line: (Integer(match[:line]) if match[:line]),
150
- function: match[:function]
150
+ function: match[:function],
151
151
  }
152
152
  end
153
153
 
154
154
  logger.error(
155
155
  "can't parse '#{stackframe}' (please file an issue so we can fix " \
156
- "it: https://github.com/airbrake/airbrake-ruby/issues/new)"
156
+ "it: https://github.com/airbrake/airbrake-ruby/issues/new)",
157
157
  )
158
158
  { file: nil, line: nil, function: stackframe }
159
159
  end
@@ -2,7 +2,7 @@ module Airbrake
2
2
  # Benchmark benchmarks Ruby code.
3
3
  #
4
4
  # @since v4.2.4
5
- # @api private
5
+ # @api public
6
6
  class Benchmark
7
7
  # Measures monotonic time for the given operation.
8
8
  #
@@ -30,7 +30,7 @@ module Airbrake
30
30
  Airbrake::FileCache[file] ||= File.foreach(file)
31
31
  rescue StandardError => ex
32
32
  logger.error(
33
- "#{self.class.name}: can't read code hunk for #{file}: #{ex}"
33
+ "#{self.class.name}: can't read code hunk for #{file}: #{ex}",
34
34
  )
35
35
  nil
36
36
  end
@@ -132,7 +132,7 @@ module Airbrake
132
132
 
133
133
  self.root_directory = File.realpath(
134
134
  (defined?(Bundler) && Bundler.root) ||
135
- Dir.pwd
135
+ Dir.pwd,
136
136
  )
137
137
 
138
138
  self.versions = {}
@@ -29,7 +29,7 @@ module Airbrake
29
29
  return promise.reject(
30
30
  "the 'environment' option must be configured " \
31
31
  "with a Symbol (or String), but '#{config.environment.class}' was " \
32
- "provided: #{config.environment}"
32
+ "provided: #{config.environment}",
33
33
  )
34
34
  end
35
35
 
@@ -46,7 +46,7 @@ module Airbrake
46
46
 
47
47
  if ignored_environment?(config)
48
48
  return promise.reject(
49
- "current environment '#{config.environment}' is ignored"
49
+ "current environment '#{config.environment}' is ignored",
50
50
  )
51
51
  end
52
52
 
@@ -74,7 +74,7 @@ module Airbrake
74
74
  if config.ignore_environments.any? && config.environment.nil?
75
75
  config.logger.warn(
76
76
  "#{LOG_LABEL} the 'environment' option is not set, " \
77
- "'ignore_environments' has no effect"
77
+ "'ignore_environments' has no effect",
78
78
  )
79
79
  end
80
80
 
@@ -27,7 +27,7 @@ module Airbrake
27
27
  @sender.send(
28
28
  deploy_info,
29
29
  promise,
30
- URI.join(@config.host, "api/v4/projects/#{@config.project_id}/deploys")
30
+ URI.join(@config.host, "api/v4/projects/#{@config.project_id}/deploys"),
31
31
  )
32
32
 
33
33
  promise
@@ -22,13 +22,13 @@ module Airbrake
22
22
  attributes = exception.to_airbrake
23
23
  rescue StandardError => ex
24
24
  logger.error(
25
- "#{LOG_LABEL} #{exception.class}#to_airbrake failed. #{ex.class}: #{ex}"
25
+ "#{LOG_LABEL} #{exception.class}#to_airbrake failed. #{ex.class}: #{ex}",
26
26
  )
27
27
  end
28
28
 
29
29
  unless attributes.is_a?(Hash)
30
30
  logger.error(
31
- "#{LOG_LABEL} #{self.class}: wanted Hash, got #{attributes.class}"
31
+ "#{LOG_LABEL} #{self.class}: wanted Hash, got #{attributes.class}",
32
32
  )
33
33
  return
34
34
  end
@@ -52,7 +52,7 @@ module Airbrake
52
52
  parts = line.chomp.split("\t").first.split(' ')
53
53
  if parts.size < MIN_HEAD_COLS
54
54
  logger.error(
55
- "#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}"
55
+ "#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}",
56
56
  )
57
57
  return
58
58
  end
@@ -62,7 +62,7 @@ module Airbrake
62
62
  username: author[0..1].join(' '),
63
63
  email: parts[-3][1..-2],
64
64
  revision: parts[1],
65
- time: timestamp(parts[-2].to_i)
65
+ time: timestamp(parts[-2].to_i),
66
66
  }
67
67
  end
68
68
  # rubocop:enable Metrics/AbcSize
@@ -124,7 +124,7 @@ module Airbrake
124
124
 
125
125
  logger.error(
126
126
  "#{LOG_LABEL} one of the patterns in #{self.class} is invalid. " \
127
- "Known patterns: #{@patterns}"
127
+ "Known patterns: #{@patterns}",
128
128
  )
129
129
  end
130
130
 
@@ -64,7 +64,7 @@ module Airbrake
64
64
  cassandra: %i[
65
65
  single_quotes uuids numeric_literals boolean_literals
66
66
  hexadecimal_literals comments multi_line_comments
67
- ].freeze
67
+ ].freeze,
68
68
  }.freeze
69
69
 
70
70
  # @return [Hash{Symbol=>Regexp}] a set of regexps to check for unmatches
@@ -76,7 +76,7 @@ module Airbrake
76
76
  sqlite: %r{'|/\*|\*/},
77
77
  cassandra: %r{'|/\*|\*/},
78
78
  oracle: %r{'|/\*|\*/},
79
- oracle_enhanced: %r{'|/\*|\*/}
79
+ oracle_enhanced: %r{'|/\*|\*/},
80
80
  }.freeze
81
81
 
82
82
  # @return [Array<Regexp>] the list of queries to be ignored
@@ -89,7 +89,7 @@ module Airbrake
89
89
  /FROM pg_attribute/i,
90
90
  /FROM pg_index/i,
91
91
  /FROM pg_class/i,
92
- /FROM pg_type/i
92
+ /FROM pg_type/i,
93
93
  ].freeze
94
94
 
95
95
  def initialize(dialect)
@@ -16,7 +16,7 @@ module Airbrake
16
16
  String,
17
17
  Symbol,
18
18
  Regexp,
19
- Numeric
19
+ Numeric,
20
20
  ].freeze
21
21
 
22
22
  # Variables starting with this prefix are not attached to a notice.
@@ -0,0 +1,12 @@
1
+ module Airbrake
2
+ # Grouppable adds the `#groups` method, so that we don't need to define it in
3
+ # all of performance models every time we add a model without groups.
4
+ #
5
+ # @since 4.9.0
6
+ # @api private
7
+ module Grouppable
8
+ def groups
9
+ {}
10
+ end
11
+ end
12
+ end