airbrake-ruby 4.1.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +5 -5
  2. data/lib/airbrake-ruby/async_sender.rb +22 -96
  3. data/lib/airbrake-ruby/backtrace.rb +8 -7
  4. data/lib/airbrake-ruby/benchmark.rb +39 -0
  5. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  6. data/lib/airbrake-ruby/config/processor.rb +84 -0
  7. data/lib/airbrake-ruby/config/validator.rb +9 -3
  8. data/lib/airbrake-ruby/config.rb +76 -20
  9. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  10. data/lib/airbrake-ruby/file_cache.rb +6 -0
  11. data/lib/airbrake-ruby/filter_chain.rb +16 -1
  12. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  13. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  14. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  15. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +5 -5
  16. data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
  17. data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
  18. data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
  19. data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
  20. data/lib/airbrake-ruby/filters/keys_filter.rb +39 -20
  21. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  22. data/lib/airbrake-ruby/filters/sql_filter.rb +30 -6
  23. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  24. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -2
  25. data/lib/airbrake-ruby/grouppable.rb +12 -0
  26. data/lib/airbrake-ruby/ignorable.rb +1 -0
  27. data/lib/airbrake-ruby/inspectable.rb +2 -2
  28. data/lib/airbrake-ruby/loggable.rb +2 -2
  29. data/lib/airbrake-ruby/mergeable.rb +12 -0
  30. data/lib/airbrake-ruby/monotonic_time.rb +48 -0
  31. data/lib/airbrake-ruby/notice.rb +10 -20
  32. data/lib/airbrake-ruby/notice_notifier.rb +23 -42
  33. data/lib/airbrake-ruby/performance_breakdown.rb +52 -0
  34. data/lib/airbrake-ruby/performance_notifier.rb +126 -49
  35. data/lib/airbrake-ruby/promise.rb +1 -0
  36. data/lib/airbrake-ruby/query.rb +26 -11
  37. data/lib/airbrake-ruby/queue.rb +65 -0
  38. data/lib/airbrake-ruby/remote_settings/settings_data.rb +120 -0
  39. data/lib/airbrake-ruby/remote_settings.rb +145 -0
  40. data/lib/airbrake-ruby/request.rb +20 -6
  41. data/lib/airbrake-ruby/stashable.rb +15 -0
  42. data/lib/airbrake-ruby/stat.rb +34 -24
  43. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  44. data/lib/airbrake-ruby/tdigest.rb +43 -58
  45. data/lib/airbrake-ruby/thread_pool.rb +138 -0
  46. data/lib/airbrake-ruby/timed_trace.rb +58 -0
  47. data/lib/airbrake-ruby/truncator.rb +10 -4
  48. data/lib/airbrake-ruby/version.rb +11 -1
  49. data/lib/airbrake-ruby.rb +219 -53
  50. data/spec/airbrake_spec.rb +428 -9
  51. data/spec/async_sender_spec.rb +26 -110
  52. data/spec/backtrace_spec.rb +44 -44
  53. data/spec/benchmark_spec.rb +33 -0
  54. data/spec/code_hunk_spec.rb +11 -11
  55. data/spec/config/processor_spec.rb +209 -0
  56. data/spec/config/validator_spec.rb +23 -6
  57. data/spec/config_spec.rb +77 -7
  58. data/spec/deploy_notifier_spec.rb +2 -2
  59. data/spec/{file_cache.rb → file_cache_spec.rb} +2 -4
  60. data/spec/filter_chain_spec.rb +28 -1
  61. data/spec/filters/dependency_filter_spec.rb +1 -1
  62. data/spec/filters/gem_root_filter_spec.rb +9 -9
  63. data/spec/filters/git_last_checkout_filter_spec.rb +21 -4
  64. data/spec/filters/git_repository_filter.rb +1 -1
  65. data/spec/filters/git_revision_filter_spec.rb +13 -11
  66. data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +29 -28
  67. data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +39 -29
  68. data/spec/filters/root_directory_filter_spec.rb +9 -9
  69. data/spec/filters/sql_filter_spec.rb +110 -55
  70. data/spec/filters/system_exit_filter_spec.rb +1 -1
  71. data/spec/filters/thread_filter_spec.rb +33 -31
  72. data/spec/fixtures/project_root/code.rb +9 -9
  73. data/spec/loggable_spec.rb +17 -0
  74. data/spec/monotonic_time_spec.rb +23 -0
  75. data/spec/{notice_notifier_spec → notice_notifier}/options_spec.rb +19 -21
  76. data/spec/notice_notifier_spec.rb +20 -80
  77. data/spec/notice_spec.rb +9 -11
  78. data/spec/performance_breakdown_spec.rb +11 -0
  79. data/spec/performance_notifier_spec.rb +360 -85
  80. data/spec/query_spec.rb +11 -0
  81. data/spec/queue_spec.rb +18 -0
  82. data/spec/remote_settings/settings_data_spec.rb +365 -0
  83. data/spec/remote_settings_spec.rb +230 -0
  84. data/spec/request_spec.rb +9 -0
  85. data/spec/response_spec.rb +8 -8
  86. data/spec/spec_helper.rb +9 -13
  87. data/spec/stashable_spec.rb +23 -0
  88. data/spec/stat_spec.rb +17 -15
  89. data/spec/sync_sender_spec.rb +14 -12
  90. data/spec/tdigest_spec.rb +6 -6
  91. data/spec/thread_pool_spec.rb +187 -0
  92. data/spec/timed_trace_spec.rb +125 -0
  93. data/spec/truncator_spec.rb +12 -12
  94. metadata +55 -18
@@ -0,0 +1,58 @@
1
+ module Airbrake
2
+ # TimedTrace represents a chunk of code performance of which was measured and
3
+ # stored under a label. The chunk is called a "span".
4
+ #
5
+ # @example
6
+ # timed_trace = TimedTrace.new
7
+ # timed_trace.span('http request') do
8
+ # http.get('example.com')
9
+ # end
10
+ # timed_trace.spans #=> { 'http request' => 0.123 }
11
+ #
12
+ # @api public
13
+ # @since v4.3.0
14
+ class TimedTrace
15
+ # @param [String] label
16
+ # @return [Airbrake::TimedTrace]
17
+ def self.span(label, &block)
18
+ new.tap { |timed_trace| timed_trace.span(label, &block) }
19
+ end
20
+
21
+ def initialize
22
+ @spans = {}
23
+ end
24
+
25
+ # @param [String] label
26
+ # @return [Boolean]
27
+ def span(label)
28
+ start_span(label)
29
+ yield
30
+ stop_span(label)
31
+ end
32
+
33
+ # @param [String] label
34
+ # @return [Boolean]
35
+ def start_span(label)
36
+ return false if @spans.key?(label)
37
+
38
+ @spans[label] = Airbrake::Benchmark.new
39
+ true
40
+ end
41
+
42
+ # @param [String] label
43
+ # @return [Boolean]
44
+ def stop_span(label)
45
+ return false unless @spans.key?(label)
46
+
47
+ @spans[label].stop
48
+ true
49
+ end
50
+
51
+ # @return [Hash<String=>Float>]
52
+ def spans
53
+ @spans.each_with_object({}) do |(label, benchmark), new_spans|
54
+ new_spans[label] = benchmark.duration
55
+ end
56
+ end
57
+ end
58
+ end
@@ -12,6 +12,10 @@ module Airbrake
12
12
  # strings with +ENCODING_OPTIONS+
13
13
  TEMP_ENCODING = 'utf-16'.freeze
14
14
 
15
+ # @return [Array<Encoding>] encodings that are eligible for fixing invalid
16
+ # characters
17
+ SUPPORTED_ENCODINGS = [Encoding::UTF_8, Encoding::ASCII].freeze
18
+
15
19
  # @return [String] what to append when something is a circular reference
16
20
  CIRCULAR = '[Circular]'.freeze
17
21
 
@@ -35,6 +39,7 @@ module Airbrake
35
39
  def truncate(object, seen = Set.new)
36
40
  if seen.include?(object.object_id)
37
41
  return CIRCULAR if CIRCULAR_TYPES.any? { |t| object.is_a?(t) }
42
+
38
43
  return object
39
44
  end
40
45
  truncate_object(object, seen << object.object_id)
@@ -63,6 +68,7 @@ module Airbrake
63
68
  def truncate_string(str)
64
69
  fixed_str = replace_invalid_characters(str)
65
70
  return fixed_str if fixed_str.length <= @max_size
71
+
66
72
  (fixed_str.slice(0, @max_size) + TRUNCATED).freeze
67
73
  end
68
74
 
@@ -76,6 +82,7 @@ module Airbrake
76
82
  truncated_hash = {}
77
83
  hash.each_with_index do |(key, val), idx|
78
84
  break if idx + 1 > @max_size
85
+
79
86
  truncated_hash[key] = truncate(val, seen)
80
87
  end
81
88
 
@@ -103,13 +110,12 @@ module Airbrake
103
110
  # @return [String] a UTF-8 encoded string
104
111
  # @see https://github.com/flori/json/commit/3e158410e81f94dbbc3da6b7b35f4f64983aa4e3
105
112
  def replace_invalid_characters(str)
106
- encoding = str.encoding
107
- utf8_string = (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII)
113
+ utf8_string = SUPPORTED_ENCODINGS.include?(str.encoding)
108
114
  return str if utf8_string && str.valid_encoding?
109
115
 
110
116
  temp_str = str.dup
111
- temp_str.encode!(TEMP_ENCODING, ENCODING_OPTIONS) if utf8_string
112
- temp_str.encode!('utf-8', ENCODING_OPTIONS)
117
+ temp_str.encode!(TEMP_ENCODING, **ENCODING_OPTIONS) if utf8_string
118
+ temp_str.encode!('utf-8', **ENCODING_OPTIONS)
113
119
  end
114
120
  end
115
121
  end
@@ -2,5 +2,15 @@
2
2
  # More information: http://semver.org/
3
3
  module Airbrake
4
4
  # @return [String] the library version
5
- AIRBRAKE_RUBY_VERSION = '4.1.0'.freeze
5
+ # @api public
6
+ AIRBRAKE_RUBY_VERSION = '5.0.0'.freeze
7
+
8
+ # @return [Hash{Symbol=>String}] the information about the notifier library
9
+ # @since 5.0.0
10
+ # @api public
11
+ NOTIFIER_INFO = {
12
+ name: 'airbrake-ruby'.freeze,
13
+ version: Airbrake::AIRBRAKE_RUBY_VERSION,
14
+ url: 'https://github.com/airbrake/airbrake-ruby'.freeze,
15
+ }.freeze
6
16
  end
data/lib/airbrake-ruby.rb CHANGED
@@ -1,16 +1,22 @@
1
1
  require 'net/https'
2
2
  require 'logger'
3
3
  require 'json'
4
- require 'thread'
5
4
  require 'set'
6
5
  require 'socket'
7
6
  require 'time'
8
7
 
9
8
  require 'airbrake-ruby/version'
10
9
  require 'airbrake-ruby/loggable'
10
+ require 'airbrake-ruby/stashable'
11
+ require 'airbrake-ruby/mergeable'
12
+ require 'airbrake-ruby/grouppable'
11
13
  require 'airbrake-ruby/config'
12
14
  require 'airbrake-ruby/config/validator'
15
+ require 'airbrake-ruby/config/processor'
16
+ require 'airbrake-ruby/remote_settings/settings_data'
17
+ require 'airbrake-ruby/remote_settings'
13
18
  require 'airbrake-ruby/promise'
19
+ require 'airbrake-ruby/thread_pool'
14
20
  require 'airbrake-ruby/sync_sender'
15
21
  require 'airbrake-ruby/async_sender'
16
22
  require 'airbrake-ruby/response'
@@ -21,8 +27,8 @@ require 'airbrake-ruby/notice'
21
27
  require 'airbrake-ruby/backtrace'
22
28
  require 'airbrake-ruby/truncator'
23
29
  require 'airbrake-ruby/filters/keys_filter'
24
- require 'airbrake-ruby/filters/keys_whitelist'
25
- require 'airbrake-ruby/filters/keys_blacklist'
30
+ require 'airbrake-ruby/filters/keys_allowlist'
31
+ require 'airbrake-ruby/filters/keys_blocklist'
26
32
  require 'airbrake-ruby/filters/gem_root_filter'
27
33
  require 'airbrake-ruby/filters/system_exit_filter'
28
34
  require 'airbrake-ruby/filters/root_directory_filter'
@@ -46,6 +52,11 @@ require 'airbrake-ruby/time_truncate'
46
52
  require 'airbrake-ruby/tdigest'
47
53
  require 'airbrake-ruby/query'
48
54
  require 'airbrake-ruby/request'
55
+ require 'airbrake-ruby/performance_breakdown'
56
+ require 'airbrake-ruby/benchmark'
57
+ require 'airbrake-ruby/monotonic_time'
58
+ require 'airbrake-ruby/timed_trace'
59
+ require 'airbrake-ruby/queue'
49
60
 
50
61
  # Airbrake is a thin wrapper around instances of the notifier classes (such as
51
62
  # notice, performance & deploy notifiers). It creates a way to access them via a
@@ -63,6 +74,7 @@ require 'airbrake-ruby/request'
63
74
  #
64
75
  # @since v1.0.0
65
76
  # @api public
77
+ # rubocop:disable Metrics/ModuleLength
66
78
  module Airbrake
67
79
  # The general error that this library uses when it wants to raise.
68
80
  Error = Class.new(StandardError)
@@ -74,14 +86,26 @@ module Airbrake
74
86
  # special cases where we need to work around older implementations
75
87
  JRUBY = (RUBY_ENGINE == 'jruby')
76
88
 
77
- # @!macro see_public_api_method
78
- # @see Airbrake.$0
79
-
80
- @performance_notifier = PerformanceNotifier.new
81
- @notice_notifier = NoticeNotifier.new
82
- @deploy_notifier = DeployNotifier.new
89
+ # @return [Boolean] true if this Ruby supports safe levels and tainting,
90
+ # to guard against using deprecated or unsupported features.
91
+ HAS_SAFE_LEVEL = (
92
+ RUBY_ENGINE == 'ruby' &&
93
+ Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7')
94
+ )
83
95
 
84
96
  class << self
97
+ # @since v4.2.3
98
+ # @api private
99
+ attr_writer :performance_notifier
100
+
101
+ # @since v4.2.3
102
+ # @api private
103
+ attr_writer :notice_notifier
104
+
105
+ # @since v4.2.3
106
+ # @api private
107
+ attr_writer :deploy_notifier
108
+
85
109
  # Configures the Airbrake notifier.
86
110
  #
87
111
  # @example
@@ -90,28 +114,45 @@ module Airbrake
90
114
  # c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
91
115
  # end
92
116
  #
93
- # @yield [config] The configuration object
117
+ # @yield [config]
94
118
  # @yieldparam config [Airbrake::Config]
95
119
  # @return [void]
96
- # @raise [Airbrake::Error] when trying to reconfigure already
97
- # existing notifier
98
- # @raise [Airbrake::Error] when either +project_id+ or +project_key+
99
- # is missing (or both)
100
- # @note There's no way to read config values outside of this library
101
120
  def configure
102
121
  yield config = Airbrake::Config.instance
122
+ Airbrake::Loggable.instance = config.logger
103
123
 
104
- if (result = config.validate).rejected?
105
- raise Airbrake::Error, result.value['error']
106
- end
124
+ config_processor = Airbrake::Config::Processor.new(config)
107
125
 
108
- Airbrake::Loggable.instance = config.logger
126
+ config_processor.process_blocklist(notice_notifier)
127
+ config_processor.process_allowlist(notice_notifier)
128
+
129
+ @remote_settings ||= config_processor.process_remote_configuration
130
+
131
+ config_processor.add_filters(notice_notifier)
132
+ end
133
+
134
+ # @since v4.2.3
135
+ # @api private
136
+ def performance_notifier
137
+ @performance_notifier ||= PerformanceNotifier.new
138
+ end
139
+
140
+ # @since v4.2.3
141
+ # @api private
142
+ def notice_notifier
143
+ @notice_notifier ||= NoticeNotifier.new
144
+ end
145
+
146
+ # @since v4.2.3
147
+ # @api private
148
+ def deploy_notifier
149
+ @deploy_notifier ||= DeployNotifier.new
109
150
  end
110
151
 
111
152
  # @return [Boolean] true if the notifier was configured, false otherwise
112
153
  # @since v2.3.0
113
154
  def configured?
114
- @notice_notifier.configured?
155
+ @notice_notifier && @notice_notifier.configured?
115
156
  end
116
157
 
117
158
  # Sends an exception to Airbrake asynchronously.
@@ -136,7 +177,7 @@ module Airbrake
136
177
  # @return [Airbrake::Promise]
137
178
  # @see .notify_sync
138
179
  def notify(exception, params = {}, &block)
139
- @notice_notifier.notify(exception, params, &block)
180
+ notice_notifier.notify(exception, params, &block)
140
181
  end
141
182
 
142
183
  # Sends an exception to Airbrake synchronously.
@@ -156,7 +197,7 @@ module Airbrake
156
197
  # @return [Airbrake::Promise] the reponse from the server
157
198
  # @see .notify
158
199
  def notify_sync(exception, params = {}, &block)
159
- @notice_notifier.notify_sync(exception, params, &block)
200
+ notice_notifier.notify_sync(exception, params, &block)
160
201
  end
161
202
 
162
203
  # Runs a callback before {.notify} or {.notify_sync} kicks in. This is
@@ -184,7 +225,7 @@ module Airbrake
184
225
  # @yieldreturn [void]
185
226
  # @return [void]
186
227
  def add_filter(filter = nil, &block)
187
- @notice_notifier.add_filter(filter, &block)
228
+ notice_notifier.add_filter(filter, &block)
188
229
  end
189
230
 
190
231
  # Deletes a filter added via {Airbrake#add_filter}.
@@ -201,7 +242,7 @@ module Airbrake
201
242
  # @since v3.1.0
202
243
  # @note This method cannot delete filters assigned via the Proc form.
203
244
  def delete_filter(filter_class)
204
- @notice_notifier.delete_filter(filter_class)
245
+ notice_notifier.delete_filter(filter_class)
205
246
  end
206
247
 
207
248
  # Builds an Airbrake notice. This is useful, if you want to add or modify a
@@ -219,7 +260,7 @@ module Airbrake
219
260
  # @return [Airbrake::Notice] the notice built with help of the given
220
261
  # arguments
221
262
  def build_notice(exception, params = {})
222
- @notice_notifier.build_notice(exception, params)
263
+ notice_notifier.build_notice(exception, params)
223
264
  end
224
265
 
225
266
  # Makes the notice notifier a no-op, which means you cannot use the
@@ -230,10 +271,24 @@ module Airbrake
230
271
  # Airbrake.close
231
272
  # Airbrake.notify('App crashed!') #=> raises Airbrake::Error
232
273
  #
233
- # @return [void]
274
+ # @return [nil]
275
+ # rubocop:disable Style/IfUnlessModifier, Metrics/CyclomaticComplexity
234
276
  def close
235
- @notice_notifier.close
277
+ if defined?(@notice_notifier) && @notice_notifier
278
+ @notice_notifier.close
279
+ end
280
+
281
+ if defined?(@performance_notifier) && @performance_notifier
282
+ @performance_notifier.close
283
+ end
284
+
285
+ if defined?(@remote_settings) && @remote_settings
286
+ @remote_settings.stop_polling
287
+ end
288
+
289
+ nil
236
290
  end
291
+ # rubocop:enable Style/IfUnlessModifier, Metrics/CyclomaticComplexity
237
292
 
238
293
  # Pings the Airbrake Deploy API endpoint about the occurred deploy.
239
294
  #
@@ -245,7 +300,7 @@ module Airbrake
245
300
  # @option deploy_info [Symbol] :version
246
301
  # @return [void]
247
302
  def notify_deploy(deploy_info)
248
- @deploy_notifier.notify(deploy_info)
303
+ deploy_notifier.notify(deploy_info)
249
304
  end
250
305
 
251
306
  # Merges +context+ with the current context.
@@ -293,12 +348,11 @@ module Airbrake
293
348
  # @param [Hash{Symbol=>Object}] context
294
349
  # @return [void]
295
350
  def merge_context(context)
296
- @notice_notifier.merge_context(context)
351
+ notice_notifier.merge_context(context)
297
352
  end
298
353
 
299
- # Increments request statistics of a certain +route+ that was invoked on
300
- # +start_time+ and ended on +end_time+ with +method+, and returned
301
- # +status_code+.
354
+ # Increments request statistics of a certain +route+ invoked with +method+,
355
+ # which returned +status_code+.
302
356
  #
303
357
  # After a certain amount of time (n seconds) the aggregated route
304
358
  # information will be sent to Airbrake.
@@ -311,8 +365,7 @@ module Airbrake
311
365
  # func: 'do_stuff',
312
366
  # file: 'app/models/foo.rb',
313
367
  # line: 452,
314
- # start_time: timestamp,
315
- # end_time: Time.now
368
+ # timing: 123.45 # ms
316
369
  # )
317
370
  #
318
371
  # @param [Hash{Symbol=>Object}] request_info
@@ -326,18 +379,31 @@ module Airbrake
326
379
  # called the query (optional)
327
380
  # @option request_info [Integer] :line The line that executes the query
328
381
  # (optional)
329
- # @option request_info [Date] :start_time When the request started
330
- # @option request_info [Time] :end_time When the request ended (optional)
382
+ # @option request_info [Float] :timing How much time it took to process the
383
+ # request (in ms)
384
+ # @param [Hash] stash What needs to be appeneded to the stash, so it's
385
+ # available in filters
331
386
  # @return [void]
332
387
  # @since v3.0.0
333
388
  # @see Airbrake::PerformanceNotifier#notify
334
- def notify_request(request_info)
335
- @performance_notifier.notify(Request.new(request_info))
389
+ def notify_request(request_info, stash = {})
390
+ request = Request.new(**request_info)
391
+ request.stash.merge!(stash)
392
+ performance_notifier.notify(request)
393
+ end
394
+
395
+ # Synchronously increments request statistics of a certain +route+ invoked
396
+ # with +method+, which returned +status_code+.
397
+ # @since v4.10.0
398
+ # @see .notify_request
399
+ def notify_request_sync(request_info, stash = {})
400
+ request = Request.new(**request_info)
401
+ request.stash.merge!(stash)
402
+ performance_notifier.notify_sync(request)
336
403
  end
337
404
 
338
- # Increments SQL statistics of a certain +query+ that was invoked on
339
- # +start_time+ and finished on +end_time+. When +method+ and +route+ are
340
- # provided, the query is grouped by these parameters.
405
+ # Increments SQL statistics of a certain +query+. When +method+ and +route+
406
+ # are provided, the query is grouped by these parameters.
341
407
  #
342
408
  # After a certain amount of time (n seconds) the aggregated query
343
409
  # information will be sent to Airbrake.
@@ -347,24 +413,112 @@ module Airbrake
347
413
  # method: 'GET',
348
414
  # route: '/things',
349
415
  # query: 'SELECT * FROM things',
350
- # start_time: timestamp,
351
- # end_time: Time.now
416
+ # timing: 123.45 # ms
352
417
  # )
353
418
  #
354
419
  # @param [Hash{Symbol=>Object}] query_info
355
- # @option request_info [String] :environment (optional)
356
- # @option request_info [String] :method The HTTP method that triggered this
420
+ # @option query_info [String] :method The HTTP method that triggered this
357
421
  # SQL query (optional)
358
- # @option request_info [String] :route The route that triggered this SQL
422
+ # @option query_info [String] :route The route that triggered this SQL
359
423
  # query (optional)
360
- # @option request_info [String] :query The query that was executed
361
- # @option request_info [Date] :start_time When the query started executing
362
- # @option request_info [Time] :end_time When the query finished (optional)
424
+ # @option query_info [String] :query The query that was executed
425
+ # @option query_info [Float] :timing How much time it took to process the
426
+ # query (in ms)
427
+ # @param [Hash] stash What needs to be appeneded to the stash, so it's
428
+ # available in filters
363
429
  # @return [void]
364
430
  # @since v3.2.0
365
431
  # @see Airbrake::PerformanceNotifier#notify
366
- def notify_query(query_info)
367
- @performance_notifier.notify(Query.new(query_info))
432
+ def notify_query(query_info, stash = {})
433
+ query = Query.new(**query_info)
434
+ query.stash.merge!(stash)
435
+ performance_notifier.notify(query)
436
+ end
437
+
438
+ # Synchronously increments SQL statistics of a certain +query+. When
439
+ # +method+ and +route+ are provided, the query is grouped by these
440
+ # parameters.
441
+ # @since v4.10.0
442
+ # @see .notify_query
443
+ def notify_query_sync(query_info, stash = {})
444
+ query = Query.new(**query_info)
445
+ query.stash.merge!(stash)
446
+ performance_notifier.notify_sync(query)
447
+ end
448
+
449
+ # Increments performance breakdown statistics of a certain route.
450
+ #
451
+ # @example
452
+ # Airbrake.notify_request(
453
+ # method: 'POST',
454
+ # route: '/thing/:id/create',
455
+ # response_type: 'json',
456
+ # groups: { db: 24.0, view: 0.4 }, # ms
457
+ # timing: 123.45 # ms
458
+ # )
459
+ #
460
+ # @param [Hash{Symbol=>Object}] breakdown_info
461
+ # @option breakdown_info [String] :method HTTP method
462
+ # @option breakdown_info [String] :route
463
+ # @option breakdown_info [String] :response_type
464
+ # @option breakdown_info [Array<Hash{Symbol=>Float}>] :groups
465
+ # @option breakdown_info [Float] :timing How much time it took to process
466
+ # the performance breakdown (in ms)
467
+ # @param [Hash] stash What needs to be appeneded to the stash, so it's
468
+ # available in filters
469
+ # @return [void]
470
+ # @since v4.2.0
471
+ def notify_performance_breakdown(breakdown_info, stash = {})
472
+ performance_breakdown = PerformanceBreakdown.new(**breakdown_info)
473
+ performance_breakdown.stash.merge!(stash)
474
+ performance_notifier.notify(performance_breakdown)
475
+ end
476
+
477
+ # Increments performance breakdown statistics of a certain route
478
+ # synchronously.
479
+ # @since v4.10.0
480
+ # @see .notify_performance_breakdown
481
+ def notify_performance_breakdown_sync(breakdown_info, stash = {})
482
+ performance_breakdown = PerformanceBreakdown.new(**breakdown_info)
483
+ performance_breakdown.stash.merge!(stash)
484
+ performance_notifier.notify_sync(performance_breakdown)
485
+ end
486
+
487
+ # Increments statistics of a certain queue (worker).
488
+ #
489
+ # @example
490
+ # Airbrake.notify_queue(
491
+ # queue: 'emails',
492
+ # error_count: 1,
493
+ # groups: { redis: 24.0, sql: 0.4 } # ms
494
+ # )
495
+ #
496
+ # @param [Hash{Symbol=>Object}] queue_info
497
+ # @option queue_info [String] :queue The name of the queue/worker
498
+ # @option queue_info [Integer] :error_count How many times this worker
499
+ # failed
500
+ # @option queue_info [Array<Hash{Symbol=>Float}>] :groups Where the job
501
+ # spent its time
502
+ # @option breakdown_info [Float] :timing How much time it took to process
503
+ # the queue (in ms)
504
+ # @param [Hash] stash What needs to be appended to the stash, so it's
505
+ # available in filters
506
+ # @return [void]
507
+ # @since v4.9.0
508
+ # @see .notify_queue_sync
509
+ def notify_queue(queue_info, stash = {})
510
+ queue = Queue.new(**queue_info)
511
+ queue.stash.merge!(stash)
512
+ performance_notifier.notify(queue)
513
+ end
514
+
515
+ # Increments statistics of a certain queue (worker) synchronously.
516
+ # @since v4.10.0
517
+ # @see .notify_queue
518
+ def notify_queue_sync(queue_info, stash = {})
519
+ queue = Queue.new(**queue_info)
520
+ queue.stash.merge!(stash)
521
+ performance_notifier.notify_sync(queue)
368
522
  end
369
523
 
370
524
  # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
@@ -399,7 +553,7 @@ module Airbrake
399
553
  # @since v3.2.0
400
554
  # @see Airbrake::PerformanceNotifier#add_filter
401
555
  def add_performance_filter(filter = nil, &block)
402
- @performance_notifier.add_filter(filter, &block)
556
+ performance_notifier.add_filter(filter, &block)
403
557
  end
404
558
 
405
559
  # Deletes a filter added via {Airbrake#add_performance_filter}.
@@ -417,7 +571,19 @@ module Airbrake
417
571
  # @note This method cannot delete filters assigned via the Proc form.
418
572
  # @see Airbrake::PerformanceNotifier#delete_filter
419
573
  def delete_performance_filter(filter_class)
420
- @performance_notifier.delete_filter(filter_class)
574
+ performance_notifier.delete_filter(filter_class)
575
+ end
576
+
577
+ # Resets all notifiers, including its filters
578
+ # @return [void]
579
+ # @since v4.2.2
580
+ def reset
581
+ close
582
+
583
+ self.performance_notifier = PerformanceNotifier.new
584
+ self.notice_notifier = NoticeNotifier.new
585
+ self.deploy_notifier = DeployNotifier.new
421
586
  end
422
587
  end
423
588
  end
589
+ # rubocop:enable Metrics/ModuleLength