airbrake-ruby 4.8.0 → 4.10.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +84 -5
  3. data/lib/airbrake-ruby/async_sender.rb +3 -3
  4. data/lib/airbrake-ruby/backtrace.rb +2 -2
  5. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  6. data/lib/airbrake-ruby/config.rb +1 -1
  7. data/lib/airbrake-ruby/config/validator.rb +3 -3
  8. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  9. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  10. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -2
  11. data/lib/airbrake-ruby/filters/keys_filter.rb +1 -1
  12. data/lib/airbrake-ruby/filters/sql_filter.rb +3 -3
  13. data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
  14. data/lib/airbrake-ruby/grouppable.rb +12 -0
  15. data/lib/airbrake-ruby/inspectable.rb +2 -2
  16. data/lib/airbrake-ruby/mergeable.rb +12 -0
  17. data/lib/airbrake-ruby/notice.rb +7 -7
  18. data/lib/airbrake-ruby/notice_notifier.rb +1 -1
  19. data/lib/airbrake-ruby/performance_breakdown.rb +2 -1
  20. data/lib/airbrake-ruby/performance_notifier.rb +42 -21
  21. data/lib/airbrake-ruby/query.rb +3 -5
  22. data/lib/airbrake-ruby/queue.rb +52 -0
  23. data/lib/airbrake-ruby/request.rb +3 -5
  24. data/lib/airbrake-ruby/stat.rb +1 -1
  25. data/lib/airbrake-ruby/version.rb +1 -1
  26. data/spec/airbrake_spec.rb +135 -45
  27. data/spec/async_sender_spec.rb +4 -4
  28. data/spec/backtrace_spec.rb +18 -18
  29. data/spec/code_hunk_spec.rb +9 -9
  30. data/spec/config/validator_spec.rb +5 -5
  31. data/spec/config_spec.rb +5 -5
  32. data/spec/deploy_notifier_spec.rb +2 -2
  33. data/spec/filter_chain_spec.rb +1 -1
  34. data/spec/filters/dependency_filter_spec.rb +1 -1
  35. data/spec/filters/gem_root_filter_spec.rb +5 -5
  36. data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
  37. data/spec/filters/git_repository_filter.rb +1 -1
  38. data/spec/filters/git_revision_filter_spec.rb +10 -10
  39. data/spec/filters/keys_blacklist_spec.rb +22 -22
  40. data/spec/filters/keys_whitelist_spec.rb +21 -21
  41. data/spec/filters/root_directory_filter_spec.rb +5 -5
  42. data/spec/filters/sql_filter_spec.rb +53 -53
  43. data/spec/filters/system_exit_filter_spec.rb +1 -1
  44. data/spec/filters/thread_filter_spec.rb +28 -28
  45. data/spec/fixtures/project_root/code.rb +9 -9
  46. data/spec/notice_notifier/options_spec.rb +12 -12
  47. data/spec/notice_notifier_spec.rb +17 -17
  48. data/spec/notice_spec.rb +5 -5
  49. data/spec/performance_notifier_spec.rb +171 -60
  50. data/spec/query_spec.rb +1 -1
  51. data/spec/queue_spec.rb +11 -0
  52. data/spec/request_spec.rb +1 -1
  53. data/spec/response_spec.rb +8 -8
  54. data/spec/spec_helper.rb +2 -2
  55. data/spec/stat_spec.rb +2 -2
  56. data/spec/sync_sender_spec.rb +12 -12
  57. data/spec/tdigest_spec.rb +6 -6
  58. data/spec/thread_pool_spec.rb +5 -5
  59. data/spec/timed_trace_spec.rb +1 -1
  60. data/spec/truncator_spec.rb +12 -12
  61. 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: f7d68430114320da9313ddd9a3dbfd05d3610701bcbb4156c5a36afba295de5f
4
+ data.tar.gz: ecd8983a7b37efb831faa440f294db56c8a59de9b9a05c1d014493d3c3da1e94
5
5
  SHA512:
6
- metadata.gz: 149075c2e3a3cd9f43f19f7309f8b9e46aa36bde2994bc48b80c8b66a4c675d1fef2989e2020e5bc81dc55a8e8490f18aa445307dd2212f7c2467a3186fb1c8d
7
- data.tar.gz: 030edc9303bd0f1042a6aa3f3ec5b672ab58598fee90a71e6d7f6a6fed84716d2043afab9edf46b3bdd6ee9939ef6b979ad78dc37575be774663ac84e8af73a7
6
+ metadata.gz: 95a4ae5fe3b38a8aaaaec88a65524144f509058091d414ec1d10e7c741767c00a59c5d046ba9c4861c960bde14555d58d937e3aafee983f9fdfeb1476b5a1f77
7
+ data.tar.gz: adba5aecb6240193c212a6f7a4011adde53fcaf87238060d4474eaca012daf66b59d0361b4c982fa5b103a28ea7f4fcc70a5bd75f57f908a08ccdbc2062a56b3
@@ -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
  #
@@ -360,6 +371,17 @@ module Airbrake
360
371
  performance_notifier.notify(request)
361
372
  end
362
373
 
374
+ # Synchronously Increments request statistics of a certain +route+ that was
375
+ # invoked on +start_time+ and ended on +end_time+ with +method+, and
376
+ # returned +status_code+.
377
+ # @since v4.10.0
378
+ # @see .notify_request
379
+ def notify_request_sync(request_info, stash = {})
380
+ request = Request.new(request_info)
381
+ request.stash.merge!(stash)
382
+ performance_notifier.notify_sync(request)
383
+ end
384
+
363
385
  # Increments SQL statistics of a certain +query+ that was invoked on
364
386
  # +start_time+ and finished on +end_time+. When +method+ and +route+ are
365
387
  # provided, the query is grouped by these parameters.
@@ -395,6 +417,17 @@ module Airbrake
395
417
  performance_notifier.notify(query)
396
418
  end
397
419
 
420
+ # Synchronously increments SQL statistics of a certain +query+ that was
421
+ # invoked on +start_time+ and finished on +end_time+. When +method+ and
422
+ # +route+ are provided, the query is grouped by these parameters.
423
+ # @since v4.10.0
424
+ # @see .notify_query
425
+ def notify_query_sync(query_info, stash = {})
426
+ query = Query.new(query_info)
427
+ query.stash.merge!(stash)
428
+ performance_notifier.notify_sync(query)
429
+ end
430
+
398
431
  # Increments performance breakdown statistics of a certain route.
399
432
  #
400
433
  # @example
@@ -423,6 +456,51 @@ module Airbrake
423
456
  performance_notifier.notify(performance_breakdown)
424
457
  end
425
458
 
459
+ # Increments performance breakdown statistics of a certain route
460
+ # synchronously.
461
+ # @since v4.10.0
462
+ # @see .notify_performance_breakdown
463
+ def notify_performance_breakdown_sync(breakdown_info, stash = {})
464
+ performance_breakdown = PerformanceBreakdown.new(breakdown_info)
465
+ performance_breakdown.stash.merge!(stash)
466
+ performance_notifier.notify_sync(performance_breakdown)
467
+ end
468
+
469
+ # Increments statistics of a certain queue (worker).
470
+ #
471
+ # @example
472
+ # Airbrake.notify_queue(
473
+ # queue: 'emails',
474
+ # error_count: 1,
475
+ # groups: { redis: 24.0, sql: 0.4 } # ms
476
+ # )
477
+ #
478
+ # @param [Hash{Symbol=>Object}] queue_info
479
+ # @option queue_info [String] :queue The name of the queue/worker
480
+ # @option queue_info [Integer] :error_count How many times this worker
481
+ # failed
482
+ # @option queue_info [Array<Hash{Symbol=>Float}>] :groups Where the job
483
+ # spent its time
484
+ # @param [Hash] stash What needs to be appended to the stash, so it's
485
+ # available in filters
486
+ # @return [void]
487
+ # @since v4.9.0
488
+ # @see .notify_queue_sync
489
+ def notify_queue(queue_info, stash = {})
490
+ queue = Queue.new(queue_info)
491
+ queue.stash.merge!(stash)
492
+ performance_notifier.notify(queue)
493
+ end
494
+
495
+ # Increments statistics of a certain queue (worker) synchronously.
496
+ # @since v4.10.0
497
+ # @see .notify_queue
498
+ def notify_queue_sync(queue_info, stash = {})
499
+ queue = Queue.new(queue_info)
500
+ queue.stash.merge!(stash)
501
+ performance_notifier.notify_sync(queue)
502
+ end
503
+
426
504
  # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
427
505
  # is useful if you want to ignore specific resources or filter the data the
428
506
  # resource contains.
@@ -480,7 +558,7 @@ module Airbrake
480
558
  # @return [void]
481
559
  # @since v4.2.2
482
560
  def reset
483
- close if notice_notifier && configured?
561
+ close
484
562
 
485
563
  self.performance_notifier = PerformanceNotifier.new
486
564
  self.notice_notifier = NoticeNotifier.new
@@ -506,10 +584,11 @@ module Airbrake
506
584
  Airbrake::Filters::RootDirectoryFilter,
507
585
  Airbrake::Filters::GitRevisionFilter,
508
586
  Airbrake::Filters::GitRepositoryFilter,
509
- Airbrake::Filters::GitLastCheckoutFilter
587
+ Airbrake::Filters::GitLastCheckoutFilter,
510
588
  ].each do |filter|
511
589
  notice_notifier.add_filter(filter.new(config.root_directory))
512
590
  end
513
591
  end
514
592
  end
515
593
  end
594
+ # 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
@@ -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
@@ -21,7 +21,7 @@ module Airbrake
21
21
  project_id: @config.project_id,
22
22
  project_key: @config.project_key,
23
23
  host: @config.host,
24
- filter_chain: @filter_chain.inspect
24
+ filter_chain: @filter_chain.inspect,
25
25
  )
26
26
  end
27
27
 
@@ -30,7 +30,7 @@ module Airbrake
30
30
  q.text("#<#{self.class}:0x#{(object_id << 1).to_s(16).rjust(16, '0')} ")
31
31
  q.text(
32
32
  "project_id=\"#{@config.project_id}\" project_key=\"#{@config.project_key}\" " \
33
- "host=\"#{@config.host}\" filter_chain="
33
+ "host=\"#{@config.host}\" filter_chain=",
34
34
  )
35
35
  q.pp(@filter_chain)
36
36
  q.text('>')
@@ -0,0 +1,12 @@
1
+ module Airbrake
2
+ # Mergeable adds the `#merge` method, so that we don't need to define it in
3
+ # all of performance models every time we add a model.
4
+ #
5
+ # @since 4.9.0
6
+ # @api private
7
+ module Mergeable
8
+ def merge(_other)
9
+ nil
10
+ end
11
+ end
12
+ end
@@ -8,7 +8,7 @@ module Airbrake
8
8
  NOTIFIER = {
9
9
  name: 'airbrake-ruby'.freeze,
10
10
  version: Airbrake::AIRBRAKE_RUBY_VERSION,
11
- url: 'https://github.com/airbrake/airbrake-ruby'.freeze
11
+ url: 'https://github.com/airbrake/airbrake-ruby'.freeze,
12
12
  }.freeze
13
13
 
14
14
  # @return [Hash{Symbol=>String,Hash}] the information to be displayed in the
@@ -16,7 +16,7 @@ module Airbrake
16
16
  CONTEXT = {
17
17
  os: RUBY_PLATFORM,
18
18
  language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
19
- notifier: NOTIFIER
19
+ notifier: NOTIFIER,
20
20
  }.freeze
21
21
 
22
22
  # @return [Integer] the maxium size of the JSON payload in bytes
@@ -32,7 +32,7 @@ module Airbrake
32
32
  IOError,
33
33
  NotImplementedError,
34
34
  JSON::GeneratorError,
35
- Encoding::UndefinedConversionError
35
+ Encoding::UndefinedConversionError,
36
36
  ].freeze
37
37
 
38
38
  # @return [Array<Symbol>] the list of keys that can be be overwritten with
@@ -60,10 +60,10 @@ module Airbrake
60
60
  errors: NestedException.new(exception).as_json,
61
61
  context: context,
62
62
  environment: {
63
- program_name: $PROGRAM_NAME
63
+ program_name: $PROGRAM_NAME,
64
64
  },
65
65
  session: {},
66
- params: params
66
+ params: params,
67
67
  }
68
68
  @truncator = Airbrake::Truncator.new(PAYLOAD_MAX_SIZE)
69
69
 
@@ -138,7 +138,7 @@ module Airbrake
138
138
  # Make sure we always send hostname.
139
139
  hostname: HOSTNAME,
140
140
 
141
- severity: DEFAULT_SEVERITY
141
+ severity: DEFAULT_SEVERITY,
142
142
  }.merge(CONTEXT).delete_if { |_key, val| val.nil? || val.empty? }
143
143
  end
144
144
 
@@ -152,7 +152,7 @@ module Airbrake
152
152
  logger.error(
153
153
  "#{LOG_LABEL} truncation failed. File an issue at " \
154
154
  "https://github.com/airbrake/airbrake-ruby " \
155
- "and attach the following payload: #{@payload}"
155
+ "and attach the following payload: #{@payload}",
156
156
  )
157
157
  end
158
158