airbrake-ruby 4.15.0 → 6.0.2

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby/async_sender.rb +4 -2
  3. data/lib/airbrake-ruby/backtrace.rb +6 -5
  4. data/lib/airbrake-ruby/config/processor.rb +71 -0
  5. data/lib/airbrake-ruby/config/validator.rb +6 -0
  6. data/lib/airbrake-ruby/config.rb +44 -35
  7. data/lib/airbrake-ruby/context.rb +51 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +3 -0
  10. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  11. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  14. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -4
  15. data/lib/airbrake-ruby/filters/git_repository_filter.rb +4 -1
  16. data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
  17. data/lib/airbrake-ruby/filters/keys_filter.rb +23 -15
  18. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/sql_filter.rb +11 -11
  20. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  21. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -3
  22. data/lib/airbrake-ruby/grouppable.rb +1 -1
  23. data/lib/airbrake-ruby/ignorable.rb +1 -2
  24. data/lib/airbrake-ruby/mergeable.rb +1 -1
  25. data/lib/airbrake-ruby/monotonic_time.rb +1 -1
  26. data/lib/airbrake-ruby/notice.rb +1 -8
  27. data/lib/airbrake-ruby/notice_notifier.rb +4 -4
  28. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  29. data/lib/airbrake-ruby/performance_notifier.rb +40 -54
  30. data/lib/airbrake-ruby/promise.rb +1 -0
  31. data/lib/airbrake-ruby/query.rb +1 -6
  32. data/lib/airbrake-ruby/queue.rb +1 -8
  33. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  34. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  35. data/lib/airbrake-ruby/remote_settings.rb +128 -0
  36. data/lib/airbrake-ruby/request.rb +1 -8
  37. data/lib/airbrake-ruby/stat.rb +2 -13
  38. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  39. data/lib/airbrake-ruby/tdigest.rb +12 -9
  40. data/lib/airbrake-ruby/thread_pool.rb +9 -6
  41. data/lib/airbrake-ruby/time_truncate.rb +2 -2
  42. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  43. data/lib/airbrake-ruby/truncator.rb +8 -2
  44. data/lib/airbrake-ruby/version.rb +11 -1
  45. data/lib/airbrake-ruby.rb +44 -54
  46. data/spec/airbrake_spec.rb +178 -92
  47. data/spec/async_sender_spec.rb +10 -8
  48. data/spec/backtrace_spec.rb +39 -36
  49. data/spec/benchmark_spec.rb +5 -3
  50. data/spec/code_hunk_spec.rb +26 -17
  51. data/spec/config/processor_spec.rb +151 -0
  52. data/spec/config/validator_spec.rb +23 -3
  53. data/spec/config_spec.rb +40 -52
  54. data/spec/context_spec.rb +54 -0
  55. data/spec/deploy_notifier_spec.rb +6 -4
  56. data/spec/file_cache_spec.rb +1 -0
  57. data/spec/filter_chain_spec.rb +29 -24
  58. data/spec/filters/context_filter_spec.rb +14 -5
  59. data/spec/filters/dependency_filter_spec.rb +3 -1
  60. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  61. data/spec/filters/gem_root_filter_spec.rb +9 -6
  62. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  63. data/spec/filters/git_repository_filter.rb +9 -9
  64. data/spec/filters/git_revision_filter_spec.rb +20 -20
  65. data/spec/filters/keys_allowlist_spec.rb +26 -16
  66. data/spec/filters/keys_blocklist_spec.rb +35 -18
  67. data/spec/filters/root_directory_filter_spec.rb +7 -7
  68. data/spec/filters/sql_filter_spec.rb +28 -28
  69. data/spec/filters/system_exit_filter_spec.rb +4 -2
  70. data/spec/filters/thread_filter_spec.rb +16 -14
  71. data/spec/loggable_spec.rb +2 -2
  72. data/spec/monotonic_time_spec.rb +8 -6
  73. data/spec/nested_exception_spec.rb +46 -46
  74. data/spec/notice_notifier/options_spec.rb +25 -15
  75. data/spec/notice_notifier_spec.rb +54 -49
  76. data/spec/notice_spec.rb +7 -3
  77. data/spec/performance_breakdown_spec.rb +0 -12
  78. data/spec/performance_notifier_spec.rb +69 -87
  79. data/spec/promise_spec.rb +38 -32
  80. data/spec/query_spec.rb +1 -11
  81. data/spec/queue_spec.rb +1 -13
  82. data/spec/remote_settings/callback_spec.rb +162 -0
  83. data/spec/remote_settings/settings_data_spec.rb +348 -0
  84. data/spec/remote_settings_spec.rb +201 -0
  85. data/spec/request_spec.rb +1 -13
  86. data/spec/response_spec.rb +34 -12
  87. data/spec/spec_helper.rb +4 -4
  88. data/spec/stashable_spec.rb +5 -5
  89. data/spec/stat_spec.rb +7 -14
  90. data/spec/sync_sender_spec.rb +52 -17
  91. data/spec/tdigest_spec.rb +61 -56
  92. data/spec/thread_pool_spec.rb +65 -56
  93. data/spec/time_truncate_spec.rb +23 -6
  94. data/spec/timed_trace_spec.rb +32 -30
  95. data/spec/truncator_spec.rb +72 -43
  96. metadata +66 -50
@@ -0,0 +1,128 @@
1
+ module Airbrake
2
+ # RemoteSettings polls the remote config of the passed project at fixed
3
+ # intervals. The fetched config is yielded as a callback parameter so that the
4
+ # invoker can define read config values. Supports proxies.
5
+ #
6
+ # @example Disable/enable error notifications based on the remote value
7
+ # RemoteSettings.poll do |data|
8
+ # config.error_notifications = data.error_notifications?
9
+ # end
10
+ #
11
+ # @since v5.0.0
12
+ # @api private
13
+ class RemoteSettings
14
+ include Airbrake::Loggable
15
+
16
+ # @return [Hash{Symbol=>String}] metadata to be attached to every GET
17
+ # request
18
+ QUERY_PARAMS = URI.encode_www_form(
19
+ notifier_name: Airbrake::NOTIFIER_INFO[:name],
20
+ notifier_version: Airbrake::NOTIFIER_INFO[:version],
21
+ os: RUBY_PLATFORM,
22
+ language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
23
+ ).freeze
24
+
25
+ # @return [String]
26
+ HTTP_OK = '200'.freeze
27
+
28
+ # Polls remote config of the given project.
29
+ #
30
+ # @param [Integer] project_id
31
+ # @param [String] host
32
+ # @yield [data]
33
+ # @yieldparam data [Airbrake::RemoteSettings::SettingsData]
34
+ # @return [Airbrake::RemoteSettings]
35
+ def self.poll(project_id, host, &block)
36
+ new(project_id, host, &block).poll
37
+ end
38
+
39
+ # @param [Integer] project_id
40
+ # @yield [data]
41
+ # @yieldparam data [Airbrake::RemoteSettings::SettingsData]
42
+ def initialize(project_id, host, &block)
43
+ @data = SettingsData.new(project_id, {})
44
+ @host = host
45
+ @block = block
46
+ @config = Airbrake::Config.instance
47
+ @poll = nil
48
+ end
49
+
50
+ # Polls remote config of the given project in background.
51
+ #
52
+ # @return [self]
53
+ def poll
54
+ @poll ||= Thread.new do
55
+ @block.call(@data)
56
+
57
+ loop do
58
+ @block.call(@data.merge!(fetch_config))
59
+ sleep(@data.interval)
60
+ end
61
+ end
62
+
63
+ self
64
+ end
65
+
66
+ # Stops the background poller thread.
67
+ #
68
+ # @return [void]
69
+ def stop_polling
70
+ @poll.kill if @poll
71
+ end
72
+
73
+ private
74
+
75
+ def fetch_config
76
+ uri = build_config_uri
77
+ https = build_https(uri)
78
+ req = Net::HTTP::Get.new(uri.request_uri)
79
+ response = nil
80
+
81
+ begin
82
+ response = https.request(req)
83
+ rescue StandardError => ex
84
+ reason = "#{LOG_LABEL} HTTP error: #{ex}"
85
+ logger.error(reason)
86
+ return {}
87
+ end
88
+
89
+ unless response.code == HTTP_OK
90
+ logger.error(response.body)
91
+ return {}
92
+ end
93
+
94
+ json = nil
95
+ begin
96
+ json = JSON.parse(response.body)
97
+ rescue JSON::ParserError => ex
98
+ logger.error(ex)
99
+ return {}
100
+ end
101
+
102
+ json
103
+ end
104
+
105
+ def build_config_uri
106
+ uri = URI(@data.config_route(@host))
107
+ uri.query = QUERY_PARAMS
108
+ uri
109
+ end
110
+
111
+ def build_https(uri)
112
+ Net::HTTP.new(uri.host, uri.port, *proxy_params).tap do |https|
113
+ https.use_ssl = uri.is_a?(URI::HTTPS)
114
+ if @config.timeout
115
+ https.open_timeout = @config.timeout
116
+ https.read_timeout = @config.timeout
117
+ end
118
+ end
119
+ end
120
+
121
+ def proxy_params
122
+ return unless @config.proxy.key?(:host)
123
+
124
+ [@config.proxy[:host], @config.proxy[:port], @config.proxy[:user],
125
+ @config.proxy[:password]]
126
+ end
127
+ end
128
+ end
@@ -4,7 +4,6 @@ module Airbrake
4
4
  # @see Airbrake.notify_request
5
5
  # @api public
6
6
  # @since v3.2.0
7
- # rubocop:disable Metrics/ParameterLists
8
7
  class Request
9
8
  include HashKeyable
10
9
  include Ignorable
@@ -12,15 +11,12 @@ module Airbrake
12
11
  include Mergeable
13
12
  include Grouppable
14
13
 
15
- attr_accessor :method, :route, :status_code, :start_time, :end_time,
16
- :timing, :time
14
+ attr_accessor :method, :route, :status_code, :timing, :time
17
15
 
18
16
  def initialize(
19
17
  method:,
20
18
  route:,
21
19
  status_code:,
22
- start_time: Time.now,
23
- end_time: start_time + 1,
24
20
  timing: nil,
25
21
  time: Time.now
26
22
  )
@@ -28,8 +24,6 @@ module Airbrake
28
24
  @method = method
29
25
  @route = route
30
26
  @status_code = status_code
31
- @start_time = start_time
32
- @end_time = end_time
33
27
  @timing = timing
34
28
  @time = time
35
29
  end
@@ -51,5 +45,4 @@ module Airbrake
51
45
  }.delete_if { |_key, val| val.nil? }
52
46
  end
53
47
  end
54
- # rubocop:enable Metrics/ParameterLists
55
48
  end
@@ -4,12 +4,12 @@ module Airbrake
4
4
  # Stat is a data structure that allows accumulating performance data (route
5
5
  # performance, SQL query performance and such). It's powered by TDigests.
6
6
  #
7
- # Usually, one Stat corresponds to one resource (route or query,
7
+ # Usually, one Stat corresponds to one metric (route or query,
8
8
  # etc.). Incrementing a stat means pushing new performance statistics.
9
9
  #
10
10
  # @example
11
11
  # stat = Airbrake::Stat.new
12
- # stat.increment(Time.now - 200)
12
+ # stat.increment_ms(2000)
13
13
  # stat.to_h # Pack and serialize data so it can be transmitted.
14
14
  #
15
15
  # @since v3.2.0
@@ -41,17 +41,6 @@ module Airbrake
41
41
  end
42
42
  end
43
43
 
44
- # Increments tdigest timings and updates tdigest with the difference between
45
- # +end_time+ and +start_time+.
46
- #
47
- # @param [Date] start_time
48
- # @param [Date] end_time
49
- # @return [void]
50
- def increment(start_time, end_time = nil)
51
- end_time ||= Time.new
52
- increment_ms((end_time - start_time) * 1000)
53
- end
54
-
55
44
  # Increments tdigest timings and updates tdigest with given +ms+ value.
56
45
  #
57
46
  # @param [Float] ms
@@ -23,7 +23,7 @@ module Airbrake
23
23
  # @param [#to_json] data
24
24
  # @param [URI::HTTPS] endpoint
25
25
  # @return [Hash{String=>String}] the parsed HTTP response
26
- def send(data, promise, endpoint = @config.endpoint)
26
+ def send(data, promise, endpoint = @config.error_endpoint)
27
27
  return promise if rate_limited_ip?(promise)
28
28
 
29
29
  response = nil
@@ -47,6 +47,7 @@ module Airbrake
47
47
  end
48
48
 
49
49
  return promise.reject(parsed_resp['error']) if parsed_resp.key?('error')
50
+
50
51
  promise.resolve(parsed_resp)
51
52
  end
52
53
 
@@ -79,7 +80,7 @@ module Airbrake
79
80
  req['Authorization'] = "Bearer #{@config.project_key}"
80
81
  req['Content-Type'] = CONTENT_TYPE
81
82
  req['User-Agent'] =
82
- "#{Airbrake::Notice::NOTIFIER[:name]}/#{Airbrake::AIRBRAKE_RUBY_VERSION}" \
83
+ "#{Airbrake::NOTIFIER_INFO[:name]}/#{Airbrake::AIRBRAKE_RUBY_VERSION}" \
83
84
  " Ruby/#{RUBY_VERSION}"
84
85
 
85
86
  req
@@ -24,6 +24,7 @@ module Airbrake
24
24
  # @since v3.2.0
25
25
  class Centroid
26
26
  attr_accessor :mean, :n, :cumn, :mean_cumn
27
+
27
28
  def initialize(mean, n, cumn, mean_cumn = nil)
28
29
  @mean = mean
29
30
  @n = n
@@ -130,7 +131,7 @@ module Airbrake
130
131
  points = to_a
131
132
  reset!
132
133
  push_centroid(points.shuffle)
133
- _cumulate(true, true)
134
+ _cumulate(exact: true, force: true)
134
135
  nil
135
136
  end
136
137
 
@@ -175,7 +176,7 @@ module Airbrake
175
176
  elsif item > max[1].mean
176
177
  1.0
177
178
  else
178
- _cumulate(true)
179
+ _cumulate(exact: true)
179
180
  bound = bound_mean(item)
180
181
  lower, upper = bound
181
182
  mean_cumn = lower.mean_cumn
@@ -200,10 +201,11 @@ module Airbrake
200
201
  unless (0..1).cover?(item)
201
202
  raise ArgumentError, "p should be in [0,1], got #{item}"
202
203
  end
204
+
203
205
  if size == 0
204
206
  nil
205
207
  else
206
- _cumulate(true)
208
+ _cumulate(exact: true)
207
209
  h = @size * item
208
210
  lower, upper = bound_mean_cumn(h)
209
211
  if lower.nil? && upper.nil?
@@ -254,7 +256,7 @@ module Airbrake
254
256
  array = bytes[start_idx..-1].unpack("G#{size}N#{size}")
255
257
  means, counts = array.each_slice(size).to_a if array.any?
256
258
  when SMALL_ENCODING
257
- means = bytes[start_idx..(start_idx + 4 * size)].unpack("g#{size}")
259
+ means = bytes[start_idx..(start_idx + (4 * size))].unpack("g#{size}")
258
260
  # Decode delta encoding of means
259
261
  x = 0
260
262
  means.map! do |m|
@@ -262,7 +264,7 @@ module Airbrake
262
264
  x = m
263
265
  m
264
266
  end
265
- counts_bytes = bytes[(start_idx + 4 * size)..-1].unpack('C*')
267
+ counts_bytes = bytes[(start_idx + (4 * size))..-1].unpack('C*')
266
268
  counts = []
267
269
  # Decode variable length integer bytes
268
270
  size.times do
@@ -271,6 +273,7 @@ module Airbrake
271
273
  shift = 7
272
274
  while (v & 0x80) != 0
273
275
  raise 'Shift too large in decode' if shift > 28
276
+
274
277
  v = counts_bytes.shift || 0
275
278
  z += (v & 0x7f) << shift
276
279
  shift += 7
@@ -304,7 +307,7 @@ module Airbrake
304
307
  centroid.mean += n * (x - centroid.mean) / (centroid.n + n)
305
308
  end
306
309
 
307
- _cumulate(false, true) if centroid.mean_cumn.nil?
310
+ _cumulate(exact: false, force: true) if centroid.mean_cumn.nil?
308
311
 
309
312
  centroid.cumn += n
310
313
  centroid.mean_cumn += n / 2.0
@@ -312,7 +315,7 @@ module Airbrake
312
315
  end
313
316
 
314
317
  # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
315
- def _cumulate(exact = false, force = false)
318
+ def _cumulate(exact: false, force: false)
316
319
  unless force
317
320
  factor = if @last_cumulate == 0
318
321
  Float::INFINITY
@@ -324,7 +327,7 @@ module Airbrake
324
327
 
325
328
  cumn = 0
326
329
  @centroids.each_value do |c|
327
- c.mean_cumn = cumn + c.n / 2.0
330
+ c.mean_cumn = cumn + (c.n / 2.0)
328
331
  cumn = c.cumn = cumn + c.n
329
332
  end
330
333
  @size = @last_cumulate = cumn
@@ -359,7 +362,7 @@ module Airbrake
359
362
  end
360
363
  end
361
364
 
362
- _cumulate(false)
365
+ _cumulate(exact: false)
363
366
 
364
367
  # If the number of centroids has grown to a very large size,
365
368
  # it may be due to values being inserted in sorted order.
@@ -6,6 +6,7 @@ module Airbrake
6
6
  # # Initialize a new thread pool with 5 workers and a queue size of 100. Set
7
7
  # # the block to be run concurrently.
8
8
  # thread_pool = ThreadPool.new(
9
+ # name: 'performance-notifier',
9
10
  # worker_size: 5,
10
11
  # queue_size: 100,
11
12
  # block: proc { |message| print "ECHO: #{message}..."}
@@ -24,7 +25,8 @@ module Airbrake
24
25
  # @note This is exposed for eaiser unit testing
25
26
  attr_reader :workers
26
27
 
27
- def initialize(worker_size:, queue_size:, block:)
28
+ def initialize(worker_size:, queue_size:, block:, name: nil)
29
+ @name = name
28
30
  @worker_size = worker_size
29
31
  @queue_size = queue_size
30
32
  @block = block
@@ -46,11 +48,11 @@ module Airbrake
46
48
  # false if the queue is full
47
49
  def <<(message)
48
50
  if backlog >= @queue_size
49
- logger.error(
51
+ logger.info do
50
52
  "#{LOG_LABEL} ThreadPool has reached its capacity of " \
51
53
  "#{@queue_size} and the following message will not be " \
52
- "processed: #{message.inspect}",
53
- )
54
+ "processed: #{message.inspect}"
55
+ end
54
56
  return false
55
57
  end
56
58
 
@@ -102,7 +104,7 @@ module Airbrake
102
104
 
103
105
  unless @queue.empty?
104
106
  msg = "#{LOG_LABEL} waiting to process #{@queue.size} task(s)..."
105
- logger.debug(msg + ' (Ctrl-C to abort)')
107
+ logger.debug("#{msg} (Ctrl-C to abort)")
106
108
  end
107
109
 
108
110
  @worker_size.times { @queue << :stop }
@@ -111,7 +113,7 @@ module Airbrake
111
113
  end
112
114
 
113
115
  threads.each(&:join)
114
- logger.debug("#{LOG_LABEL} thread pool closed")
116
+ logger.debug("#{LOG_LABEL} #{@name} thread pool closed")
115
117
  end
116
118
 
117
119
  def closed?
@@ -129,6 +131,7 @@ module Airbrake
129
131
  Thread.new do
130
132
  while (message = @queue.pop)
131
133
  break if message == :stop
134
+
132
135
  @block.call(message)
133
136
  end
134
137
  end
@@ -6,10 +6,10 @@ module Airbrake
6
6
  module TimeTruncate
7
7
  # Truncate +time+ to floor minute and turn it into an RFC3339 timestamp.
8
8
  #
9
- # @param [Time] time
9
+ # @param [Time, Integer, Float] time
10
10
  # @return [String]
11
11
  def self.utc_truncate_minutes(time)
12
- tm = time.getutc
12
+ tm = Time.at(time).getutc
13
13
 
14
14
  Time.utc(tm.year, tm.month, tm.day, tm.hour, tm.min).to_datetime.rfc3339
15
15
  end
@@ -50,9 +50,7 @@ module Airbrake
50
50
 
51
51
  # @return [Hash<String=>Float>]
52
52
  def spans
53
- @spans.each_with_object({}) do |(label, benchmark), new_spans|
54
- new_spans[label] = benchmark.duration
55
- end
53
+ @spans.transform_values(&:duration)
56
54
  end
57
55
  end
58
56
  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,8 +110,7 @@ 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
@@ -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.15.0'.freeze
5
+ # @api public
6
+ AIRBRAKE_RUBY_VERSION = '6.0.2'.freeze
7
+
8
+ # @return [Hash{Symbol=>String}] the information about the notifier library
9
+ # @since v5.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
@@ -12,6 +12,10 @@ require 'airbrake-ruby/mergeable'
12
12
  require 'airbrake-ruby/grouppable'
13
13
  require 'airbrake-ruby/config'
14
14
  require 'airbrake-ruby/config/validator'
15
+ require 'airbrake-ruby/config/processor'
16
+ require 'airbrake-ruby/remote_settings/callback'
17
+ require 'airbrake-ruby/remote_settings/settings_data'
18
+ require 'airbrake-ruby/remote_settings'
15
19
  require 'airbrake-ruby/promise'
16
20
  require 'airbrake-ruby/thread_pool'
17
21
  require 'airbrake-ruby/sync_sender'
@@ -54,6 +58,7 @@ require 'airbrake-ruby/benchmark'
54
58
  require 'airbrake-ruby/monotonic_time'
55
59
  require 'airbrake-ruby/timed_trace'
56
60
  require 'airbrake-ruby/queue'
61
+ require 'airbrake-ruby/context'
57
62
 
58
63
  # Airbrake is a thin wrapper around instances of the notifier classes (such as
59
64
  # notice, performance & deploy notifiers). It creates a way to access them via a
@@ -117,7 +122,15 @@ module Airbrake
117
122
  def configure
118
123
  yield config = Airbrake::Config.instance
119
124
  Airbrake::Loggable.instance = config.logger
120
- process_config_options(config)
125
+
126
+ config_processor = Airbrake::Config::Processor.new(config)
127
+
128
+ config_processor.process_blocklist(notice_notifier)
129
+ config_processor.process_allowlist(notice_notifier)
130
+
131
+ @remote_settings ||= config_processor.process_remote_configuration
132
+
133
+ config_processor.add_filters(notice_notifier)
121
134
  end
122
135
 
123
136
  # @since v4.2.3
@@ -260,8 +273,8 @@ module Airbrake
260
273
  # Airbrake.close
261
274
  # Airbrake.notify('App crashed!') #=> raises Airbrake::Error
262
275
  #
263
- # @return [void]
264
- # rubocop:disable Style/GuardClause, Style/IfUnlessModifier
276
+ # @return [nil]
277
+ # rubocop:disable Style/IfUnlessModifier
265
278
  def close
266
279
  if defined?(@notice_notifier) && @notice_notifier
267
280
  @notice_notifier.close
@@ -270,8 +283,14 @@ module Airbrake
270
283
  if defined?(@performance_notifier) && @performance_notifier
271
284
  @performance_notifier.close
272
285
  end
286
+
287
+ if defined?(@remote_settings) && @remote_settings
288
+ @remote_settings.stop_polling
289
+ end
290
+
291
+ nil
273
292
  end
274
- # rubocop:enable Style/GuardClause, Style/IfUnlessModifier
293
+ # rubocop:enable Style/IfUnlessModifier
275
294
 
276
295
  # Pings the Airbrake Deploy API endpoint about the occurred deploy.
277
296
  #
@@ -345,23 +364,14 @@ module Airbrake
345
364
  # method: 'POST',
346
365
  # route: '/thing/:id/create',
347
366
  # status_code: 200,
348
- # func: 'do_stuff',
349
- # file: 'app/models/foo.rb',
350
- # line: 452,
351
367
  # timing: 123.45 # ms
352
368
  # )
353
369
  #
354
370
  # @param [Hash{Symbol=>Object}] request_info
355
371
  # @option request_info [String] :method The HTTP method that was invoked
356
372
  # @option request_info [String] :route The route that was invoked
357
- # @option request_info [Integer] :status_code The respose code that the
373
+ # @option request_info [Integer] :status_code The response code that the
358
374
  # route returned
359
- # @option request_info [String] :func The function that called the query
360
- # (optional)
361
- # @option request_info [String] :file The file that has the function that
362
- # called the query (optional)
363
- # @option request_info [Integer] :line The line that executes the query
364
- # (optional)
365
375
  # @option request_info [Float] :timing How much time it took to process the
366
376
  # request (in ms)
367
377
  # @param [Hash] stash What needs to be appeneded to the stash, so it's
@@ -396,6 +406,9 @@ module Airbrake
396
406
  # method: 'GET',
397
407
  # route: '/things',
398
408
  # query: 'SELECT * FROM things',
409
+ # func: 'do_stuff',
410
+ # file: 'app/models/foo.rb',
411
+ # line: 452,
399
412
  # timing: 123.45 # ms
400
413
  # )
401
414
  #
@@ -405,6 +418,12 @@ module Airbrake
405
418
  # @option query_info [String] :route The route that triggered this SQL
406
419
  # query (optional)
407
420
  # @option query_info [String] :query The query that was executed
421
+ # @option request_info [String] :func The function that called the query
422
+ # (optional)
423
+ # @option request_info [String] :file The file that has the function that
424
+ # called the query (optional)
425
+ # @option request_info [Integer] :line The line that executes the query
426
+ # (optional)
408
427
  # @option query_info [Float] :timing How much time it took to process the
409
428
  # query (in ms)
410
429
  # @param [Hash] stash What needs to be appeneded to the stash, so it's
@@ -432,7 +451,7 @@ module Airbrake
432
451
  # Increments performance breakdown statistics of a certain route.
433
452
  #
434
453
  # @example
435
- # Airbrake.notify_request(
454
+ # Airbrake.notify_performance_breakdown(
436
455
  # method: 'POST',
437
456
  # route: '/thing/:id/create',
438
457
  # response_type: 'json',
@@ -504,24 +523,24 @@ module Airbrake
504
523
  performance_notifier.notify_sync(queue)
505
524
  end
506
525
 
507
- # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
508
- # is useful if you want to ignore specific resources or filter the data the
509
- # resource contains.
526
+ # Runs a callback before {.notify_request}, {.notify_query}, {.notify_queue}
527
+ # or {.notify_performance_breakdown} kicks in. This is useful if you want to
528
+ # ignore specific metrics or filter the data the metric contains.
510
529
  #
511
- # @example Ignore all resources
530
+ # @example Ignore all metrics
512
531
  # Airbrake.add_performance_filter(&:ignore!)
513
532
  # @example Filter sensitive data
514
- # Airbrake.add_performance_filter do |resource|
515
- # case resource
533
+ # Airbrake.add_performance_filter do |metric|
534
+ # case metric
516
535
  # when Airbrake::Query
517
- # resource.route = '[Filtered]'
536
+ # metric.route = '[Filtered]'
518
537
  # when Airbrake::Request
519
- # resource.query = '[Filtered]'
538
+ # metric.query = '[Filtered]'
520
539
  # end
521
540
  # end
522
541
  # @example Filter with help of a class
523
542
  # class MyFilter
524
- # def call(resource)
543
+ # def call(metric)
525
544
  # # ...
526
545
  # end
527
546
  # end
@@ -529,7 +548,7 @@ module Airbrake
529
548
  # Airbrake.add_performance_filter(MyFilter.new)
530
549
  #
531
550
  # @param [#call] filter The filter object
532
- # @yield [resource] The resource to filter
551
+ # @yield [metric] The metric to filter
533
552
  # @yieldparam [Airbrake::Query, Airbrake::Request]
534
553
  # @yieldreturn [void]
535
554
  # @return [void]
@@ -567,35 +586,6 @@ module Airbrake
567
586
  self.notice_notifier = NoticeNotifier.new
568
587
  self.deploy_notifier = DeployNotifier.new
569
588
  end
570
-
571
- private
572
-
573
- # rubocop:disable Metrics/AbcSize
574
- def process_config_options(config)
575
- if config.blocklist_keys.any?
576
- blocklist = Airbrake::Filters::KeysBlocklist.new(config.blocklist_keys)
577
- notice_notifier.add_filter(blocklist)
578
- end
579
-
580
- if config.allowlist_keys.any?
581
- allowlist = Airbrake::Filters::KeysAllowlist.new(config.allowlist_keys)
582
- notice_notifier.add_filter(allowlist)
583
- end
584
-
585
- return unless config.root_directory
586
-
587
- [
588
- Airbrake::Filters::RootDirectoryFilter,
589
- Airbrake::Filters::GitRevisionFilter,
590
- Airbrake::Filters::GitRepositoryFilter,
591
- Airbrake::Filters::GitLastCheckoutFilter,
592
- ].each do |filter|
593
- next if notice_notifier.has_filter?(filter)
594
-
595
- notice_notifier.add_filter(filter.new(config.root_directory))
596
- end
597
- end
598
- # rubocop:enable Metrics/AbcSize
599
589
  end
600
590
  end
601
591
  # rubocop:enable Metrics/ModuleLength