airbrake-ruby 4.1.0 → 5.0.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.
- checksums.yaml +5 -5
- data/lib/airbrake-ruby/async_sender.rb +22 -96
- data/lib/airbrake-ruby/backtrace.rb +8 -7
- data/lib/airbrake-ruby/benchmark.rb +39 -0
- data/lib/airbrake-ruby/code_hunk.rb +1 -1
- data/lib/airbrake-ruby/config/processor.rb +84 -0
- data/lib/airbrake-ruby/config/validator.rb +9 -3
- data/lib/airbrake-ruby/config.rb +76 -20
- data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
- data/lib/airbrake-ruby/file_cache.rb +6 -0
- data/lib/airbrake-ruby/filter_chain.rb +16 -1
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +5 -5
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
- data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
- data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
- data/lib/airbrake-ruby/filters/keys_filter.rb +39 -20
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +30 -6
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +4 -2
- data/lib/airbrake-ruby/grouppable.rb +12 -0
- data/lib/airbrake-ruby/ignorable.rb +1 -0
- data/lib/airbrake-ruby/inspectable.rb +2 -2
- data/lib/airbrake-ruby/loggable.rb +2 -2
- data/lib/airbrake-ruby/mergeable.rb +12 -0
- data/lib/airbrake-ruby/monotonic_time.rb +48 -0
- data/lib/airbrake-ruby/notice.rb +10 -20
- data/lib/airbrake-ruby/notice_notifier.rb +23 -42
- data/lib/airbrake-ruby/performance_breakdown.rb +52 -0
- data/lib/airbrake-ruby/performance_notifier.rb +126 -49
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +26 -11
- data/lib/airbrake-ruby/queue.rb +65 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +120 -0
- data/lib/airbrake-ruby/remote_settings.rb +145 -0
- data/lib/airbrake-ruby/request.rb +20 -6
- data/lib/airbrake-ruby/stashable.rb +15 -0
- data/lib/airbrake-ruby/stat.rb +34 -24
- data/lib/airbrake-ruby/sync_sender.rb +3 -2
- data/lib/airbrake-ruby/tdigest.rb +43 -58
- data/lib/airbrake-ruby/thread_pool.rb +138 -0
- data/lib/airbrake-ruby/timed_trace.rb +58 -0
- data/lib/airbrake-ruby/truncator.rb +10 -4
- data/lib/airbrake-ruby/version.rb +11 -1
- data/lib/airbrake-ruby.rb +219 -53
- data/spec/airbrake_spec.rb +428 -9
- data/spec/async_sender_spec.rb +26 -110
- data/spec/backtrace_spec.rb +44 -44
- data/spec/benchmark_spec.rb +33 -0
- data/spec/code_hunk_spec.rb +11 -11
- data/spec/config/processor_spec.rb +209 -0
- data/spec/config/validator_spec.rb +23 -6
- data/spec/config_spec.rb +77 -7
- data/spec/deploy_notifier_spec.rb +2 -2
- data/spec/{file_cache.rb → file_cache_spec.rb} +2 -4
- data/spec/filter_chain_spec.rb +28 -1
- data/spec/filters/dependency_filter_spec.rb +1 -1
- data/spec/filters/gem_root_filter_spec.rb +9 -9
- data/spec/filters/git_last_checkout_filter_spec.rb +21 -4
- data/spec/filters/git_repository_filter.rb +1 -1
- data/spec/filters/git_revision_filter_spec.rb +13 -11
- data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +29 -28
- data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +39 -29
- data/spec/filters/root_directory_filter_spec.rb +9 -9
- data/spec/filters/sql_filter_spec.rb +110 -55
- data/spec/filters/system_exit_filter_spec.rb +1 -1
- data/spec/filters/thread_filter_spec.rb +33 -31
- data/spec/fixtures/project_root/code.rb +9 -9
- data/spec/loggable_spec.rb +17 -0
- data/spec/monotonic_time_spec.rb +23 -0
- data/spec/{notice_notifier_spec → notice_notifier}/options_spec.rb +19 -21
- data/spec/notice_notifier_spec.rb +20 -80
- data/spec/notice_spec.rb +9 -11
- data/spec/performance_breakdown_spec.rb +11 -0
- data/spec/performance_notifier_spec.rb +360 -85
- data/spec/query_spec.rb +11 -0
- data/spec/queue_spec.rb +18 -0
- data/spec/remote_settings/settings_data_spec.rb +365 -0
- data/spec/remote_settings_spec.rb +230 -0
- data/spec/request_spec.rb +9 -0
- data/spec/response_spec.rb +8 -8
- data/spec/spec_helper.rb +9 -13
- data/spec/stashable_spec.rb +23 -0
- data/spec/stat_spec.rb +17 -15
- data/spec/sync_sender_spec.rb +14 -12
- data/spec/tdigest_spec.rb +6 -6
- data/spec/thread_pool_spec.rb +187 -0
- data/spec/timed_trace_spec.rb +125 -0
- data/spec/truncator_spec.rb +12 -12
- metadata +55 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e98183a856071ce3d105c36adc9aa22a8ee7071b93958fd70fb673d2735b3977
|
4
|
+
data.tar.gz: 80464931a1362c4d845ffee737ed7aa1e46693136023b1f08006c49de98e62eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c32fc631e3af6348a871094dde31452d4a6fdef834596933e179d2acdbbd4935751715007fa7c4dcfce3639ebbd55f61b15f06428015647e4cbcac590ef7aaf
|
7
|
+
data.tar.gz: 23968940c6f1dfb500be5ab479c1d524cdc9f80e40808b4ff08d7f4b03c97baad603d97504c58a5300ddfa7ec9b1a4b5bd44a24a6a4a15776a82778ef2a0c324
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Airbrake
|
2
|
-
# Responsible for sending notices to Airbrake asynchronously.
|
3
|
-
# supports an unlimited number of worker threads and an unlimited queue size
|
4
|
-
# (both values are configurable).
|
2
|
+
# Responsible for sending notices to Airbrake asynchronously.
|
5
3
|
#
|
6
4
|
# @see SyncSender
|
7
5
|
# @api private
|
@@ -9,123 +7,51 @@ module Airbrake
|
|
9
7
|
class AsyncSender
|
10
8
|
include Loggable
|
11
9
|
|
12
|
-
|
13
|
-
# @note This is exposed for eaiser unit testing
|
14
|
-
# @since v4.0.0
|
15
|
-
attr_reader :workers
|
16
|
-
|
17
|
-
# @return [Array<[Airbrake::Notice,Airbrake::Promise]>] the list of unsent
|
18
|
-
# payload
|
19
|
-
# @note This is exposed for eaiser unit testing
|
20
|
-
# @since v4.0.0
|
21
|
-
attr_reader :unsent
|
22
|
-
|
23
|
-
def initialize
|
10
|
+
def initialize(method = :post)
|
24
11
|
@config = Airbrake::Config.instance
|
25
|
-
@
|
26
|
-
@sender = SyncSender.new
|
27
|
-
@closed = false
|
28
|
-
@workers = ThreadGroup.new
|
29
|
-
@mutex = Mutex.new
|
30
|
-
@pid = nil
|
12
|
+
@method = method
|
31
13
|
end
|
32
14
|
|
33
15
|
# Asynchronously sends a notice to Airbrake.
|
34
16
|
#
|
35
|
-
# @param [
|
36
|
-
# library
|
17
|
+
# @param [Hash] payload Whatever needs to be sent
|
37
18
|
# @return [Airbrake::Promise]
|
38
|
-
def send(
|
39
|
-
|
19
|
+
def send(payload, promise, endpoint = @config.error_endpoint)
|
20
|
+
unless thread_pool << [payload, promise, endpoint]
|
21
|
+
return promise.reject(
|
22
|
+
"AsyncSender has reached its capacity of #{@config.queue_size}",
|
23
|
+
)
|
24
|
+
end
|
40
25
|
|
41
|
-
@unsent << [notice, promise]
|
42
26
|
promise
|
43
27
|
end
|
44
28
|
|
45
|
-
# Closes the instance making it a no-op (it shut downs all worker
|
46
|
-
# threads). Before closing, waits on all unsent notices to be sent.
|
47
|
-
#
|
48
29
|
# @return [void]
|
49
|
-
# @raise [Airbrake::Error] when invoked more than one time
|
50
30
|
def close
|
51
|
-
|
52
|
-
raise Airbrake::Error, 'attempted to close already closed sender' if closed?
|
53
|
-
|
54
|
-
unless @unsent.empty?
|
55
|
-
msg = "#{LOG_LABEL} waiting to send #{@unsent.size} unsent notice(s)..."
|
56
|
-
logger.debug(msg + ' (Ctrl-C to abort)')
|
57
|
-
end
|
58
|
-
|
59
|
-
@config.workers.times { @unsent << [:stop, Airbrake::Promise.new] }
|
60
|
-
@closed = true
|
61
|
-
@workers.list.dup
|
62
|
-
end
|
63
|
-
|
64
|
-
threads.each(&:join)
|
65
|
-
logger.debug("#{LOG_LABEL} closed")
|
31
|
+
thread_pool.close
|
66
32
|
end
|
67
33
|
|
68
|
-
# Checks whether the sender is closed and thus usable.
|
69
34
|
# @return [Boolean]
|
70
35
|
def closed?
|
71
|
-
|
36
|
+
thread_pool.closed?
|
72
37
|
end
|
73
38
|
|
74
|
-
#
|
75
|
-
# workers only in two cases: when it was closed or when all workers
|
76
|
-
# crashed. An *active* sender doesn't have any workers only when something
|
77
|
-
# went wrong.
|
78
|
-
#
|
79
|
-
# Workers are expected to crash when you +fork+ the process the workers are
|
80
|
-
# living in. In this case we detect a +fork+ and try to revive them here.
|
81
|
-
#
|
82
|
-
# Another possible scenario that crashes workers is when you close the
|
83
|
-
# instance on +at_exit+, but some other +at_exit+ hook prevents the process
|
84
|
-
# from exiting.
|
85
|
-
#
|
86
|
-
# @return [Boolean] true if an instance wasn't closed, but has no workers
|
87
|
-
# @see https://goo.gl/oydz8h Example of at_exit that prevents exit
|
39
|
+
# @return [Boolean]
|
88
40
|
def has_workers?
|
89
|
-
|
90
|
-
return false if @closed
|
91
|
-
|
92
|
-
if @pid != Process.pid && @workers.list.empty?
|
93
|
-
@pid = Process.pid
|
94
|
-
spawn_workers
|
95
|
-
end
|
96
|
-
|
97
|
-
!@closed && @workers.list.any?
|
98
|
-
end
|
41
|
+
thread_pool.has_workers?
|
99
42
|
end
|
100
43
|
|
101
44
|
private
|
102
45
|
|
103
|
-
def
|
104
|
-
@
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
while (message = @unsent.pop)
|
112
|
-
break if message.first == :stop
|
113
|
-
@sender.send(*message)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def will_not_deliver(notice)
|
119
|
-
backtrace = notice[:errors][0][:backtrace].map do |line|
|
120
|
-
"#{line[:file]}:#{line[:line]} in `#{line[:function]}'"
|
46
|
+
def thread_pool
|
47
|
+
@thread_pool ||= begin
|
48
|
+
sender = SyncSender.new(@method)
|
49
|
+
ThreadPool.new(
|
50
|
+
worker_size: @config.workers,
|
51
|
+
queue_size: @config.queue_size,
|
52
|
+
block: proc { |args| sender.send(*args) },
|
53
|
+
)
|
121
54
|
end
|
122
|
-
logger.error(
|
123
|
-
"#{LOG_LABEL} AsyncSender has reached its capacity of " \
|
124
|
-
"#{@unsent.max} and the following notice will not be delivered " \
|
125
|
-
"Error: #{notice[:errors][0][:type]} - #{notice[:errors][0][:message]}\n" \
|
126
|
-
"Backtrace: \n" + backtrace.join("\n")
|
127
|
-
)
|
128
|
-
nil
|
129
55
|
end
|
130
56
|
end
|
131
57
|
end
|
@@ -22,7 +22,7 @@ module Airbrake
|
|
22
22
|
(?<line>\d+) # Matches '43'
|
23
23
|
:in\s
|
24
24
|
`(?<function>.*)' # Matches "`block (3 levels) in <top (required)>'"
|
25
|
-
\z}x
|
25
|
+
\z}x.freeze
|
26
26
|
|
27
27
|
# @return [Regexp] the pattern that matches JRuby Java stack frames, such
|
28
28
|
# as org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
|
@@ -39,7 +39,7 @@ module Airbrake
|
|
39
39
|
:?
|
40
40
|
(?<line>\d+)? # Matches '105'
|
41
41
|
\)
|
42
|
-
\z}x
|
42
|
+
\z}x.freeze
|
43
43
|
|
44
44
|
# @return [Regexp] the pattern that tries to assume what a generic stack
|
45
45
|
# frame might look like, when exception's backtrace is set manually.
|
@@ -53,7 +53,7 @@ module Airbrake
|
|
53
53
|
|
|
54
54
|
:in\s(?<function>.+) # Matches ":in func"
|
55
55
|
)? # ... or nothing
|
56
|
-
\z}x
|
56
|
+
\z}x.freeze
|
57
57
|
|
58
58
|
# @return [Regexp] the pattern that matches exceptions from PL/SQL such as
|
59
59
|
# ORA-06512: at "STORE.LI_LICENSES_PACK", line 1945
|
@@ -67,7 +67,7 @@ module Airbrake
|
|
67
67
|
|
|
68
68
|
#{GENERIC}
|
69
69
|
)
|
70
|
-
\z/x
|
70
|
+
\z/x.freeze
|
71
71
|
|
72
72
|
# @return [Regexp] the pattern that matches CoffeeScript backtraces
|
73
73
|
# usually coming from Rails & ExecJS
|
@@ -82,7 +82,7 @@ module Airbrake
|
|
82
82
|
# Matches the Ruby part of the backtrace
|
83
83
|
#{RUBY}
|
84
84
|
)
|
85
|
-
\z/x
|
85
|
+
\z/x.freeze
|
86
86
|
end
|
87
87
|
|
88
88
|
# @return [Integer] how many first frames should include code hunks
|
@@ -95,6 +95,7 @@ module Airbrake
|
|
95
95
|
# @return [Array<Hash{Symbol=>String,Integer}>] the parsed backtrace
|
96
96
|
def self.parse(exception)
|
97
97
|
return [] if exception.backtrace.nil? || exception.backtrace.none?
|
98
|
+
|
98
99
|
parse_backtrace(exception)
|
99
100
|
end
|
100
101
|
|
@@ -147,13 +148,13 @@ module Airbrake
|
|
147
148
|
return {
|
148
149
|
file: match[:file],
|
149
150
|
line: (Integer(match[:line]) if match[:line]),
|
150
|
-
function: match[:function]
|
151
|
+
function: match[:function],
|
151
152
|
}
|
152
153
|
end
|
153
154
|
|
154
155
|
logger.error(
|
155
156
|
"can't parse '#{stackframe}' (please file an issue so we can fix " \
|
156
|
-
"it: https://github.com/airbrake/airbrake-ruby/issues/new)"
|
157
|
+
"it: https://github.com/airbrake/airbrake-ruby/issues/new)",
|
157
158
|
)
|
158
159
|
{ file: nil, line: nil, function: stackframe }
|
159
160
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Benchmark benchmarks Ruby code.
|
3
|
+
#
|
4
|
+
# @since v4.2.4
|
5
|
+
# @api public
|
6
|
+
class Benchmark
|
7
|
+
# Measures monotonic time for the given operation.
|
8
|
+
#
|
9
|
+
# @yieldreturn [void]
|
10
|
+
def self.measure
|
11
|
+
benchmark = new
|
12
|
+
|
13
|
+
yield
|
14
|
+
|
15
|
+
benchmark.stop
|
16
|
+
benchmark.duration
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Float]
|
20
|
+
attr_reader :duration
|
21
|
+
|
22
|
+
# @since v4.3.0
|
23
|
+
def initialize
|
24
|
+
@start = MonotonicTime.time_in_ms
|
25
|
+
@duration = 0.0
|
26
|
+
end
|
27
|
+
|
28
|
+
# Stops the benchmark and stores `duration`.
|
29
|
+
#
|
30
|
+
# @since v4.3.0
|
31
|
+
# @return [Boolean] true for the first invocation, false in all other cases
|
32
|
+
def stop
|
33
|
+
return false if @duration > 0.0
|
34
|
+
|
35
|
+
@duration = MonotonicTime.time_in_ms - @start
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
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
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Airbrake
|
2
|
+
class Config
|
3
|
+
# Processor is a helper class, which is responsible for setting default
|
4
|
+
# config values, default notifier filters and remote configuration changes.
|
5
|
+
#
|
6
|
+
# @since 5.0.0
|
7
|
+
# @api private
|
8
|
+
class Processor
|
9
|
+
# @param [Airbrake::Config] config
|
10
|
+
# @return [Airbrake::Config::Processor]
|
11
|
+
def self.process(config)
|
12
|
+
new(config).process
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Airbrake::Config] config
|
16
|
+
def initialize(config)
|
17
|
+
@config = config
|
18
|
+
@blocklist_keys = @config.blocklist_keys
|
19
|
+
@allowlist_keys = @config.allowlist_keys
|
20
|
+
@project_id = @config.project_id
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [Airbrake::NoticeNotifier] notifier
|
24
|
+
# @return [void]
|
25
|
+
def process_blocklist(notifier)
|
26
|
+
return if @blocklist_keys.none?
|
27
|
+
|
28
|
+
blocklist = Airbrake::Filters::KeysBlocklist.new(@blocklist_keys)
|
29
|
+
notifier.add_filter(blocklist)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [Airbrake::NoticeNotifier] notifier
|
33
|
+
# @return [void]
|
34
|
+
def process_allowlist(notifier)
|
35
|
+
return if @allowlist_keys.none?
|
36
|
+
|
37
|
+
allowlist = Airbrake::Filters::KeysAllowlist.new(@allowlist_keys)
|
38
|
+
notifier.add_filter(allowlist)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Airbrake::RemoteSettings]
|
42
|
+
def process_remote_configuration
|
43
|
+
return unless @project_id
|
44
|
+
|
45
|
+
RemoteSettings.poll(
|
46
|
+
@project_id,
|
47
|
+
@config.remote_config_host,
|
48
|
+
&method(:poll_callback)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [Airbrake::NoticeNotifier] notifier
|
53
|
+
# @return [void]
|
54
|
+
def add_filters(notifier)
|
55
|
+
return unless @config.root_directory
|
56
|
+
|
57
|
+
[
|
58
|
+
Airbrake::Filters::RootDirectoryFilter,
|
59
|
+
Airbrake::Filters::GitRevisionFilter,
|
60
|
+
Airbrake::Filters::GitRepositoryFilter,
|
61
|
+
Airbrake::Filters::GitLastCheckoutFilter,
|
62
|
+
].each do |filter|
|
63
|
+
next if notifier.has_filter?(filter)
|
64
|
+
|
65
|
+
notifier.add_filter(filter.new(@config.root_directory))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [Airbrake::RemoteSettings::SettingsData] data
|
70
|
+
# @return [void]
|
71
|
+
def poll_callback(data)
|
72
|
+
@config.logger.debug(
|
73
|
+
"#{LOG_LABEL} applying remote settings: #{data.to_h}",
|
74
|
+
)
|
75
|
+
|
76
|
+
@config.error_host = data.error_host if data.error_host
|
77
|
+
@config.apm_host = data.apm_host if data.apm_host
|
78
|
+
|
79
|
+
@config.error_notifications = data.error_notifications?
|
80
|
+
@config.performance_stats = data.performance_stats?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -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
|
|
@@ -44,9 +44,13 @@ module Airbrake
|
|
44
44
|
def check_notify_ability(config)
|
45
45
|
promise = Airbrake::Promise.new
|
46
46
|
|
47
|
+
unless config.error_notifications
|
48
|
+
return promise.reject('error notifications are disabled')
|
49
|
+
end
|
50
|
+
|
47
51
|
if ignored_environment?(config)
|
48
52
|
return promise.reject(
|
49
|
-
"current environment '#{config.environment}' is ignored"
|
53
|
+
"current environment '#{config.environment}' is ignored",
|
50
54
|
)
|
51
55
|
end
|
52
56
|
|
@@ -57,12 +61,14 @@ module Airbrake
|
|
57
61
|
|
58
62
|
def valid_project_id?(config)
|
59
63
|
return true if config.project_id.to_i > 0
|
64
|
+
|
60
65
|
false
|
61
66
|
end
|
62
67
|
|
63
68
|
def valid_project_key?(config)
|
64
69
|
return false unless config.project_key.is_a?(String)
|
65
70
|
return false if config.project_key.empty?
|
71
|
+
|
66
72
|
true
|
67
73
|
end
|
68
74
|
|
@@ -74,7 +80,7 @@ module Airbrake
|
|
74
80
|
if config.ignore_environments.any? && config.environment.nil?
|
75
81
|
config.logger.warn(
|
76
82
|
"#{LOG_LABEL} the 'environment' option is not set, " \
|
77
|
-
"'ignore_environments' has no effect"
|
83
|
+
"'ignore_environments' has no effect",
|
78
84
|
)
|
79
85
|
end
|
80
86
|
|
data/lib/airbrake-ruby/config.rb
CHANGED
@@ -46,6 +46,17 @@ module Airbrake
|
|
46
46
|
# @api public
|
47
47
|
attr_accessor :host
|
48
48
|
|
49
|
+
# @since v5.0.0
|
50
|
+
alias error_host host
|
51
|
+
# @since v5.0.0
|
52
|
+
alias error_host= host=
|
53
|
+
|
54
|
+
# @return [String] the host, which provides the API endpoint to which
|
55
|
+
# APM data should be sent
|
56
|
+
# @api public
|
57
|
+
# @since v5.0.0
|
58
|
+
attr_accessor :apm_host
|
59
|
+
|
49
60
|
# @return [String, Pathname] the working directory of your project
|
50
61
|
# @api public
|
51
62
|
attr_accessor :root_directory
|
@@ -68,14 +79,14 @@ module Airbrake
|
|
68
79
|
# @return [Array<String, Symbol, Regexp>] the keys, which should be
|
69
80
|
# filtered
|
70
81
|
# @api public
|
71
|
-
# @since
|
72
|
-
attr_accessor :
|
82
|
+
# @since v4.15.0
|
83
|
+
attr_accessor :allowlist_keys
|
73
84
|
|
74
|
-
# @return [Array<String, Symbol, Regexp>] the keys, which
|
85
|
+
# @return [Array<String, Symbol, Regexp>] the keys, which should be
|
75
86
|
# filtered
|
76
87
|
# @api public
|
77
|
-
# @since
|
78
|
-
attr_accessor :
|
88
|
+
# @since v4.15.0
|
89
|
+
attr_accessor :blocklist_keys
|
79
90
|
|
80
91
|
# @return [Boolean] true if the library should attach code hunks to each
|
81
92
|
# frame in a backtrace, false otherwise
|
@@ -83,20 +94,42 @@ module Airbrake
|
|
83
94
|
# @since v2.5.0
|
84
95
|
attr_accessor :code_hunks
|
85
96
|
|
86
|
-
# @return [Boolean] true if the library should send performance stats
|
87
|
-
#
|
97
|
+
# @return [Boolean] true if the library should send route performance stats
|
98
|
+
# to Airbrake, false otherwise
|
88
99
|
# @api public
|
89
100
|
# @since v3.2.0
|
90
101
|
attr_accessor :performance_stats
|
91
102
|
|
92
103
|
# @return [Integer] how many seconds to wait before sending collected route
|
93
104
|
# stats
|
94
|
-
# @api
|
105
|
+
# @api private
|
95
106
|
# @since v3.2.0
|
96
107
|
attr_accessor :performance_stats_flush_period
|
97
108
|
|
109
|
+
# @return [Boolean] true if the library should send SQL stats to Airbrake,
|
110
|
+
# false otherwise
|
111
|
+
# @api public
|
112
|
+
# @since v4.6.0
|
113
|
+
attr_accessor :query_stats
|
114
|
+
|
115
|
+
# @return [Boolean] true if the library should send job/queue/worker stats
|
116
|
+
# to Airbrake, false otherwise
|
117
|
+
# @api public
|
118
|
+
# @since v4.12.0
|
119
|
+
attr_accessor :job_stats
|
120
|
+
|
121
|
+
# @return [Boolean] true if the library should send error reports to
|
122
|
+
# Airbrake, false otherwise
|
123
|
+
# @api public
|
124
|
+
# @since 5.0.0
|
125
|
+
attr_accessor :error_notifications
|
126
|
+
|
127
|
+
# @return [String] the host such as which should be used for fetching remote
|
128
|
+
# configuration options (example: "https://bucket-name.s3.amazonaws.com")
|
129
|
+
attr_accessor :remote_config_host
|
130
|
+
|
98
131
|
class << self
|
99
|
-
# @
|
132
|
+
# @return [Config]
|
100
133
|
attr_writer :instance
|
101
134
|
|
102
135
|
# @return [Config]
|
@@ -107,43 +140,50 @@ module Airbrake
|
|
107
140
|
|
108
141
|
# @param [Hash{Symbol=>Object}] user_config the hash to be used to build the
|
109
142
|
# config
|
143
|
+
# rubocop:disable Metrics/AbcSize
|
110
144
|
def initialize(user_config = {})
|
111
145
|
self.proxy = {}
|
112
146
|
self.queue_size = 100
|
113
147
|
self.workers = 1
|
114
148
|
self.code_hunks = true
|
115
|
-
self.logger = ::Logger.new(File::NULL)
|
149
|
+
self.logger = ::Logger.new(File::NULL).tap { |l| l.level = Logger::WARN }
|
116
150
|
self.project_id = user_config[:project_id]
|
117
151
|
self.project_key = user_config[:project_key]
|
118
|
-
self.
|
152
|
+
self.error_host = 'https://api.airbrake.io'
|
153
|
+
self.apm_host = 'https://api.airbrake.io'
|
154
|
+
self.remote_config_host = 'https://v1-production-notifier-configs.s3.amazonaws.com'
|
119
155
|
|
120
156
|
self.ignore_environments = []
|
121
157
|
|
122
158
|
self.timeout = user_config[:timeout]
|
123
159
|
|
124
|
-
self.
|
125
|
-
self.
|
160
|
+
self.blocklist_keys = []
|
161
|
+
self.allowlist_keys = []
|
126
162
|
|
127
163
|
self.root_directory = File.realpath(
|
128
164
|
(defined?(Bundler) && Bundler.root) ||
|
129
|
-
Dir.pwd
|
165
|
+
Dir.pwd,
|
130
166
|
)
|
131
167
|
|
132
168
|
self.versions = {}
|
133
|
-
self.performance_stats =
|
169
|
+
self.performance_stats = true
|
134
170
|
self.performance_stats_flush_period = 15
|
171
|
+
self.query_stats = true
|
172
|
+
self.job_stats = true
|
173
|
+
self.error_notifications = true
|
135
174
|
|
136
175
|
merge(user_config)
|
137
176
|
end
|
177
|
+
# rubocop:enable Metrics/AbcSize
|
138
178
|
|
139
|
-
# The full URL to the Airbrake Notice API. Based on the +:
|
179
|
+
# The full URL to the Airbrake Notice API. Based on the +:error_host+ option.
|
140
180
|
# @return [URI] the endpoint address
|
141
|
-
def
|
142
|
-
@
|
181
|
+
def error_endpoint
|
182
|
+
@error_endpoint ||=
|
143
183
|
begin
|
144
|
-
self.
|
184
|
+
self.error_host = ('https://' << error_host) if error_host !~ %r{\Ahttps?://}
|
145
185
|
api = "api/v3/projects/#{project_id}/notices"
|
146
|
-
URI.join(
|
186
|
+
URI.join(error_host, api)
|
147
187
|
end
|
148
188
|
end
|
149
189
|
|
@@ -197,6 +237,22 @@ module Airbrake
|
|
197
237
|
check_notify_ability
|
198
238
|
end
|
199
239
|
|
240
|
+
# @return [Promise] resolved promise if neither of the performance options
|
241
|
+
# reject it, false otherwise
|
242
|
+
def check_performance_options(resource)
|
243
|
+
promise = Airbrake::Promise.new
|
244
|
+
|
245
|
+
if !performance_stats
|
246
|
+
promise.reject("The Performance Stats feature is disabled")
|
247
|
+
elsif resource.is_a?(Airbrake::Query) && !query_stats
|
248
|
+
promise.reject("The Query Stats feature is disabled")
|
249
|
+
elsif resource.is_a?(Airbrake::Queue) && !job_stats
|
250
|
+
promise.reject("The Job Stats feature is disabled")
|
251
|
+
else
|
252
|
+
promise
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
200
256
|
private
|
201
257
|
|
202
258
|
def set_option(option, value)
|
@@ -70,13 +70,14 @@ module Airbrake
|
|
70
70
|
def refine(notice)
|
71
71
|
@filters.each do |filter|
|
72
72
|
break if notice.ignored?
|
73
|
+
|
73
74
|
filter.call(notice)
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
77
78
|
# @return [String] customized inspect to lessen the amount of clutter
|
78
79
|
def inspect
|
79
|
-
|
80
|
+
filter_classes.to_s
|
80
81
|
end
|
81
82
|
|
82
83
|
# @return [String] {#inspect} for PrettyPrint
|
@@ -91,5 +92,19 @@ module Airbrake
|
|
91
92
|
end
|
92
93
|
q.text(']')
|
93
94
|
end
|
95
|
+
|
96
|
+
# @param [Class] filter_class
|
97
|
+
# @return [Boolean] true if the current chain has an instance of the given
|
98
|
+
# class, false otherwise
|
99
|
+
# @since v4.14.0
|
100
|
+
def includes?(filter_class)
|
101
|
+
filter_classes.include?(filter_class)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def filter_classes
|
107
|
+
@filters.map(&:class)
|
108
|
+
end
|
94
109
|
end
|
95
110
|
end
|
@@ -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
|