airbrake-ruby 5.0.0.rc.2-java → 5.2.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +1 -0
- data/lib/airbrake-ruby/backtrace.rb +6 -5
- data/lib/airbrake-ruby/config.rb +19 -38
- data/lib/airbrake-ruby/config/processor.rb +8 -17
- data/lib/airbrake-ruby/config/validator.rb +2 -0
- data/lib/airbrake-ruby/file_cache.rb +1 -1
- data/lib/airbrake-ruby/filter_chain.rb +1 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +1 -2
- 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_filter.rb +21 -13
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +4 -4
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +3 -2
- data/lib/airbrake-ruby/grouppable.rb +1 -1
- data/lib/airbrake-ruby/ignorable.rb +1 -0
- data/lib/airbrake-ruby/mergeable.rb +1 -1
- data/lib/airbrake-ruby/notice_notifier.rb +1 -0
- data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
- data/lib/airbrake-ruby/performance_notifier.rb +1 -14
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +1 -6
- data/lib/airbrake-ruby/queue.rb +1 -8
- data/lib/airbrake-ruby/remote_settings.rb +16 -54
- data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +14 -14
- data/lib/airbrake-ruby/request.rb +1 -8
- data/lib/airbrake-ruby/stat.rb +1 -12
- data/lib/airbrake-ruby/sync_sender.rb +1 -0
- data/lib/airbrake-ruby/tdigest.rb +2 -0
- data/lib/airbrake-ruby/thread_pool.rb +1 -0
- data/lib/airbrake-ruby/truncator.rb +8 -2
- data/lib/airbrake-ruby/version.rb +2 -2
- data/spec/backtrace_spec.rb +26 -26
- data/spec/code_hunk_spec.rb +2 -2
- data/spec/config/processor_spec.rb +21 -101
- data/spec/config_spec.rb +5 -29
- data/spec/filters/gem_root_filter_spec.rb +4 -4
- data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
- data/spec/filters/keys_allowlist_spec.rb +1 -0
- data/spec/filters/keys_blocklist_spec.rb +10 -0
- data/spec/filters/root_directory_filter_spec.rb +4 -4
- data/spec/filters/sql_filter_spec.rb +2 -2
- data/spec/filters/thread_filter_spec.rb +1 -1
- data/spec/notice_notifier/options_spec.rb +2 -2
- data/spec/notice_notifier_spec.rb +2 -2
- data/spec/notice_spec.rb +1 -1
- data/spec/performance_breakdown_spec.rb +0 -12
- data/spec/performance_notifier_spec.rb +0 -25
- data/spec/query_spec.rb +1 -11
- data/spec/queue_spec.rb +1 -13
- data/spec/remote_settings/callback_spec.rb +143 -0
- data/spec/remote_settings/settings_data_spec.rb +34 -13
- data/spec/remote_settings_spec.rb +40 -83
- data/spec/request_spec.rb +1 -13
- data/spec/spec_helper.rb +4 -4
- data/spec/stat_spec.rb +0 -9
- data/spec/tdigest_spec.rb +1 -1
- metadata +8 -5
@@ -23,7 +23,7 @@ module Airbrake
|
|
23
23
|
|
24
24
|
# @return [Hash{Symbol=>Regexp}] matchers for certain features of SQL
|
25
25
|
ALL_FEATURES = {
|
26
|
-
# rubocop:disable
|
26
|
+
# rubocop:disable Layout/LineLength
|
27
27
|
single_quotes: /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
|
28
28
|
double_quotes: /"(?:[^"]|"")*?(?:\\".*|"(?!"))/,
|
29
29
|
dollar_quotes: /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/,
|
@@ -33,13 +33,13 @@ module Airbrake
|
|
33
33
|
hexadecimal_literals: /0x[0-9a-fA-F]+/,
|
34
34
|
comments: /(?:#|--).*?(?=\r|\n|$)/i,
|
35
35
|
multi_line_comments: %r{/\*(?:[^/]|/[^*])*?(?:\*/|/\*.*)},
|
36
|
-
oracle_quoted_strings: /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'\<.*?(?:\>'|$)|q'\(.*?(?:\)'|$)
|
37
|
-
# rubocop:enable
|
36
|
+
oracle_quoted_strings: /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'\<.*?(?:\>'|$)|q'\(.*?(?:\)'|$)/,
|
37
|
+
# rubocop:enable Layout/LineLength
|
38
38
|
}.freeze
|
39
39
|
|
40
40
|
# @return [Regexp] the regexp that is applied after the feature regexps
|
41
41
|
# were used
|
42
|
-
POST_FILTER = /(?<=[values|in ]\().+(?=\))/i
|
42
|
+
POST_FILTER = /(?<=[values|in ]\().+(?=\))/i.freeze
|
43
43
|
|
44
44
|
# @return [Hash{Symbol=>Array<Symbol>}] a set of features that corresponds
|
45
45
|
# to a certain dialect
|
@@ -41,8 +41,7 @@ module Airbrake
|
|
41
41
|
thread_info[:fiber_variables] = vars
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
if th.respond_to?(:name) && (name = th.name)
|
44
|
+
if (name = th.name)
|
46
45
|
thread_info[:name] = name
|
47
46
|
end
|
48
47
|
|
@@ -56,6 +55,7 @@ module Airbrake
|
|
56
55
|
def thread_variables(th)
|
57
56
|
th.thread_variables.map.with_object({}) do |var, h|
|
58
57
|
next if var.to_s.start_with?(IGNORE_PREFIX)
|
58
|
+
|
59
59
|
h[var] = sanitize_value(th.thread_variable_get(var))
|
60
60
|
end
|
61
61
|
end
|
@@ -63,6 +63,7 @@ module Airbrake
|
|
63
63
|
def fiber_variables(th)
|
64
64
|
th.keys.map.with_object({}) do |key, h|
|
65
65
|
next if key.to_s.start_with?(IGNORE_PREFIX)
|
66
|
+
|
66
67
|
h[key] = sanitize_value(th[key])
|
67
68
|
end
|
68
69
|
end
|
@@ -12,16 +12,13 @@ module Airbrake
|
|
12
12
|
include Stashable
|
13
13
|
include Mergeable
|
14
14
|
|
15
|
-
attr_accessor :method, :route, :response_type, :groups, :
|
16
|
-
:end_time, :timing, :time
|
15
|
+
attr_accessor :method, :route, :response_type, :groups, :timing, :time
|
17
16
|
|
18
17
|
def initialize(
|
19
18
|
method:,
|
20
19
|
route:,
|
21
20
|
response_type:,
|
22
21
|
groups:,
|
23
|
-
start_time: Time.now,
|
24
|
-
end_time: start_time + 1,
|
25
22
|
timing: nil,
|
26
23
|
time: Time.now
|
27
24
|
)
|
@@ -30,8 +27,6 @@ module Airbrake
|
|
30
27
|
@route = route
|
31
28
|
@response_type = response_type
|
32
29
|
@groups = groups
|
33
|
-
@start_time = start_time
|
34
|
-
@end_time = end_time
|
35
30
|
@timing = timing
|
36
31
|
@time = time
|
37
32
|
end
|
@@ -104,7 +104,7 @@ module Airbrake
|
|
104
104
|
@payload[resource] = { total: Airbrake::Stat.new }
|
105
105
|
end
|
106
106
|
|
107
|
-
|
107
|
+
@payload[resource][:total].increment_ms(resource.timing)
|
108
108
|
|
109
109
|
resource.groups.each do |name, ms|
|
110
110
|
@payload[resource][name] ||= Airbrake::Stat.new
|
@@ -112,19 +112,6 @@ module Airbrake
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
def update_total(resource, total)
|
116
|
-
if resource.timing
|
117
|
-
total.increment_ms(resource.timing)
|
118
|
-
else
|
119
|
-
loc = caller_locations(6..6).first
|
120
|
-
Kernel.warn(
|
121
|
-
"#{loc.path}:#{loc.lineno}: warning: :start_time and :end_time are " \
|
122
|
-
"deprecated. Use :timing & :time instead",
|
123
|
-
)
|
124
|
-
total.increment(resource.start_time, resource.end_time)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
115
|
def check_configuration(resource)
|
129
116
|
promise = @config.check_configuration
|
130
117
|
return promise if promise.rejected?
|
data/lib/airbrake-ruby/query.rb
CHANGED
@@ -12,8 +12,7 @@ module Airbrake
|
|
12
12
|
include Mergeable
|
13
13
|
include Grouppable
|
14
14
|
|
15
|
-
attr_accessor :method, :route, :query, :func, :file, :line, :
|
16
|
-
:end_time, :timing, :time
|
15
|
+
attr_accessor :method, :route, :query, :func, :file, :line, :timing, :time
|
17
16
|
|
18
17
|
def initialize(
|
19
18
|
method:,
|
@@ -22,8 +21,6 @@ module Airbrake
|
|
22
21
|
func: nil,
|
23
22
|
file: nil,
|
24
23
|
line: nil,
|
25
|
-
start_time: Time.now,
|
26
|
-
end_time: start_time + 1,
|
27
24
|
timing: nil,
|
28
25
|
time: Time.now
|
29
26
|
)
|
@@ -34,8 +31,6 @@ module Airbrake
|
|
34
31
|
@func = func
|
35
32
|
@file = file
|
36
33
|
@line = line
|
37
|
-
@start_time = start_time
|
38
|
-
@end_time = end_time
|
39
34
|
@timing = timing
|
40
35
|
@time = time
|
41
36
|
end
|
data/lib/airbrake-ruby/queue.rb
CHANGED
@@ -4,21 +4,17 @@ module Airbrake
|
|
4
4
|
# @see Airbrake.notify_queue
|
5
5
|
# @api public
|
6
6
|
# @since v4.9.0
|
7
|
-
# rubocop:disable Metrics/ParameterLists
|
8
7
|
class Queue
|
9
8
|
include HashKeyable
|
10
9
|
include Ignorable
|
11
10
|
include Stashable
|
12
11
|
|
13
|
-
attr_accessor :queue, :error_count, :groups, :
|
14
|
-
:timing, :time
|
12
|
+
attr_accessor :queue, :error_count, :groups, :timing, :time
|
15
13
|
|
16
14
|
def initialize(
|
17
15
|
queue:,
|
18
16
|
error_count:,
|
19
17
|
groups: {},
|
20
|
-
start_time: Time.now,
|
21
|
-
end_time: start_time + 1,
|
22
18
|
timing: nil,
|
23
19
|
time: Time.now
|
24
20
|
)
|
@@ -26,8 +22,6 @@ module Airbrake
|
|
26
22
|
@queue = queue
|
27
23
|
@error_count = error_count
|
28
24
|
@groups = groups
|
29
|
-
@start_time = start_time
|
30
|
-
@end_time = end_time
|
31
25
|
@timing = timing
|
32
26
|
@time = time
|
33
27
|
end
|
@@ -68,5 +62,4 @@ module Airbrake
|
|
68
62
|
''
|
69
63
|
end
|
70
64
|
end
|
71
|
-
# rubocop:enable Metrics/ParameterLists
|
72
65
|
end
|
@@ -8,22 +8,11 @@ module Airbrake
|
|
8
8
|
# config.error_notifications = data.error_notifications?
|
9
9
|
# end
|
10
10
|
#
|
11
|
-
#
|
12
|
-
# that it doesn't wait on the result from the API call.
|
13
|
-
#
|
14
|
-
# When {#stop_polling} is called, the current config will be dumped to disk.
|
15
|
-
#
|
16
|
-
# @since ?.?.?
|
11
|
+
# @since v5.0.0
|
17
12
|
# @api private
|
18
13
|
class RemoteSettings
|
19
14
|
include Airbrake::Loggable
|
20
15
|
|
21
|
-
# @return [String] the path to the persistent config
|
22
|
-
CONFIG_DUMP_PATH = File.join(
|
23
|
-
File.expand_path(__dir__),
|
24
|
-
'../../config/config.json',
|
25
|
-
).freeze
|
26
|
-
|
27
16
|
# @return [Hash{Symbol=>String}] metadata to be attached to every GET
|
28
17
|
# request
|
29
18
|
QUERY_PARAMS = URI.encode_www_form(
|
@@ -33,37 +22,35 @@ module Airbrake
|
|
33
22
|
language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
|
34
23
|
).freeze
|
35
24
|
|
25
|
+
# @return [String]
|
26
|
+
HTTP_OK = '200'.freeze
|
27
|
+
|
36
28
|
# Polls remote config of the given project.
|
37
29
|
#
|
38
30
|
# @param [Integer] project_id
|
31
|
+
# @param [String] host
|
39
32
|
# @yield [data]
|
40
33
|
# @yieldparam data [Airbrake::RemoteSettings::SettingsData]
|
41
34
|
# @return [Airbrake::RemoteSettings]
|
42
|
-
def self.poll(project_id, &block)
|
43
|
-
new(project_id, &block).poll
|
35
|
+
def self.poll(project_id, host, &block)
|
36
|
+
new(project_id, host, &block).poll
|
44
37
|
end
|
45
38
|
|
46
39
|
# @param [Integer] project_id
|
47
40
|
# @yield [data]
|
48
41
|
# @yieldparam data [Airbrake::RemoteSettings::SettingsData]
|
49
|
-
def initialize(project_id, &block)
|
42
|
+
def initialize(project_id, host, &block)
|
50
43
|
@data = SettingsData.new(project_id, {})
|
44
|
+
@host = host
|
51
45
|
@block = block
|
52
46
|
@poll = nil
|
53
47
|
end
|
54
48
|
|
55
|
-
# Polls remote config of the given project in background.
|
56
|
-
# first (if exists).
|
49
|
+
# Polls remote config of the given project in background.
|
57
50
|
#
|
58
51
|
# @return [self]
|
59
52
|
def poll
|
60
53
|
@poll ||= Thread.new do
|
61
|
-
begin
|
62
|
-
load_config
|
63
|
-
rescue StandardError => ex
|
64
|
-
logger.error("#{LOG_LABEL} config loading failed: #{ex}")
|
65
|
-
end
|
66
|
-
|
67
54
|
@block.call(@data)
|
68
55
|
|
69
56
|
loop do
|
@@ -75,17 +62,11 @@ module Airbrake
|
|
75
62
|
self
|
76
63
|
end
|
77
64
|
|
78
|
-
# Stops the background poller thread.
|
65
|
+
# Stops the background poller thread.
|
79
66
|
#
|
80
67
|
# @return [void]
|
81
68
|
def stop_polling
|
82
69
|
@poll.kill if @poll
|
83
|
-
|
84
|
-
begin
|
85
|
-
dump_config
|
86
|
-
rescue StandardError => ex
|
87
|
-
logger.error("#{LOG_LABEL} config dumping failed: #{ex}")
|
88
|
-
end
|
89
70
|
end
|
90
71
|
|
91
72
|
private
|
@@ -93,22 +74,20 @@ module Airbrake
|
|
93
74
|
def fetch_config
|
94
75
|
response = nil
|
95
76
|
begin
|
96
|
-
response = Net::HTTP.
|
77
|
+
response = Net::HTTP.get_response(build_config_uri)
|
97
78
|
rescue StandardError => ex
|
98
79
|
logger.error(ex)
|
99
80
|
return {}
|
100
81
|
end
|
101
82
|
|
102
|
-
|
103
|
-
|
104
|
-
if response.start_with?('<?xml ')
|
105
|
-
logger.error(response)
|
83
|
+
unless response.code == HTTP_OK
|
84
|
+
logger.error(response.body)
|
106
85
|
return {}
|
107
86
|
end
|
108
87
|
|
109
88
|
json = nil
|
110
89
|
begin
|
111
|
-
json = JSON.parse(response)
|
90
|
+
json = JSON.parse(response.body)
|
112
91
|
rescue JSON::ParserError => ex
|
113
92
|
logger.error(ex)
|
114
93
|
return {}
|
@@ -118,26 +97,9 @@ module Airbrake
|
|
118
97
|
end
|
119
98
|
|
120
99
|
def build_config_uri
|
121
|
-
uri = URI(@data.config_route)
|
100
|
+
uri = URI(@data.config_route(@host))
|
122
101
|
uri.query = QUERY_PARAMS
|
123
102
|
uri
|
124
103
|
end
|
125
|
-
|
126
|
-
def load_config
|
127
|
-
config_dir = File.dirname(CONFIG_DUMP_PATH)
|
128
|
-
Dir.mkdir(config_dir) unless File.directory?(config_dir)
|
129
|
-
|
130
|
-
return unless File.exist?(CONFIG_DUMP_PATH)
|
131
|
-
|
132
|
-
config = File.read(CONFIG_DUMP_PATH)
|
133
|
-
@data.merge!(JSON.parse(config))
|
134
|
-
end
|
135
|
-
|
136
|
-
def dump_config
|
137
|
-
config_dir = File.dirname(CONFIG_DUMP_PATH)
|
138
|
-
Dir.mkdir(config_dir) unless File.directory?(config_dir)
|
139
|
-
|
140
|
-
File.write(CONFIG_DUMP_PATH, JSON.dump(@data.to_h))
|
141
|
-
end
|
142
104
|
end
|
143
105
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Airbrake
|
2
|
+
class RemoteSettings
|
3
|
+
# Callback is a class that provides a callback for the config poller, which
|
4
|
+
# updates the local config according to the data.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
# @since v5.0.2
|
8
|
+
class Callback
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
@orig_error_notifications = config.error_notifications
|
12
|
+
@orig_performance_stats = config.performance_stats
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Airbrake::RemoteSettings::SettingsData] data
|
16
|
+
# @return [void]
|
17
|
+
def call(data)
|
18
|
+
@config.logger.debug do
|
19
|
+
"#{LOG_LABEL} applying remote settings: #{data.to_h}"
|
20
|
+
end
|
21
|
+
|
22
|
+
@config.error_host = data.error_host if data.error_host
|
23
|
+
@config.apm_host = data.apm_host if data.apm_host
|
24
|
+
|
25
|
+
process_error_notifications(data)
|
26
|
+
process_performance_stats(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def process_error_notifications(data)
|
32
|
+
return unless @orig_error_notifications
|
33
|
+
|
34
|
+
@config.error_notifications = data.error_notifications?
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_performance_stats(data)
|
38
|
+
return unless @orig_performance_stats
|
39
|
+
|
40
|
+
@config.performance_stats = data.performance_stats?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -11,7 +11,7 @@ module Airbrake
|
|
11
11
|
#
|
12
12
|
# settings_data.interval #=> 600
|
13
13
|
#
|
14
|
-
# @since
|
14
|
+
# @since v5.0.0
|
15
15
|
# @api private
|
16
16
|
class SettingsData
|
17
17
|
# @return [Integer] how frequently we should poll the config API
|
@@ -20,16 +20,15 @@ module Airbrake
|
|
20
20
|
# @return [String] API version of the S3 API to poll
|
21
21
|
API_VER = '2020-06-18'.freeze
|
22
22
|
|
23
|
-
# @return [String] what
|
23
|
+
# @return [String] what path to poll
|
24
24
|
CONFIG_ROUTE_PATTERN =
|
25
|
-
|
26
|
-
"#{API_VER}/config/%<project_id>s/config.json".freeze
|
25
|
+
"%<host>s/#{API_VER}/config/%<project_id>s/config.json".freeze
|
27
26
|
|
28
27
|
# @return [Hash{Symbol=>String}] the hash of all supported settings where
|
29
28
|
# the value is the name of the setting returned by the API
|
30
29
|
SETTINGS = {
|
31
|
-
errors: 'errors',
|
32
|
-
apm: 'apm',
|
30
|
+
errors: 'errors'.freeze,
|
31
|
+
apm: 'apm'.freeze,
|
33
32
|
}.freeze
|
34
33
|
|
35
34
|
# @param [Integer] project_id
|
@@ -56,17 +55,18 @@ module Airbrake
|
|
56
55
|
@data['poll_sec'] > 0 ? @data['poll_sec'] : DEFAULT_INTERVAL
|
57
56
|
end
|
58
57
|
|
58
|
+
# @param [String] remote_config_host
|
59
59
|
# @return [String] where the config is stored on S3.
|
60
|
-
def config_route
|
61
|
-
if
|
62
|
-
return
|
63
|
-
CONFIG_ROUTE_PATTERN,
|
64
|
-
bucket: 'staging-notifier-configs',
|
65
|
-
project_id: @project_id,
|
66
|
-
)
|
60
|
+
def config_route(remote_config_host)
|
61
|
+
if @data['config_route'] && !@data['config_route'].empty?
|
62
|
+
return remote_config_host.chomp('/') + '/' + @data['config_route']
|
67
63
|
end
|
68
64
|
|
69
|
-
|
65
|
+
format(
|
66
|
+
CONFIG_ROUTE_PATTERN,
|
67
|
+
host: remote_config_host.chomp('/'),
|
68
|
+
project_id: @project_id,
|
69
|
+
)
|
70
70
|
end
|
71
71
|
|
72
72
|
# @return [Boolean] whether error notifications are enabled
|