airbrake-ruby 4.8.0 → 4.11.1

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 (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