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.
- 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
@@ -27,6 +27,7 @@ module Airbrake
|
|
27
27
|
@git_path = File.join(root_directory, '.git')
|
28
28
|
@weight = 116
|
29
29
|
@last_checkout = nil
|
30
|
+
@deploy_username = ENV['AIRBRAKE_DEPLOY_USERNAME']
|
30
31
|
end
|
31
32
|
|
32
33
|
# @macro call_filter
|
@@ -40,32 +41,31 @@ module Airbrake
|
|
40
41
|
|
41
42
|
return unless File.exist?(@git_path)
|
42
43
|
return unless (checkout = last_checkout)
|
44
|
+
|
43
45
|
notice[:context][:lastCheckout] = checkout
|
44
46
|
end
|
45
47
|
|
46
48
|
private
|
47
49
|
|
48
|
-
# rubocop:disable Metrics/AbcSize
|
49
50
|
def last_checkout
|
50
51
|
return unless (line = last_checkout_line)
|
51
52
|
|
52
53
|
parts = line.chomp.split("\t").first.split(' ')
|
53
54
|
if parts.size < MIN_HEAD_COLS
|
54
55
|
logger.error(
|
55
|
-
"#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}"
|
56
|
+
"#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}",
|
56
57
|
)
|
57
58
|
return
|
58
59
|
end
|
59
60
|
|
60
61
|
author = parts[2..-4]
|
61
62
|
@last_checkout = {
|
62
|
-
username: author[0..1].join(' '),
|
63
|
+
username: @deploy_username || author[0..1].join(' '),
|
63
64
|
email: parts[-3][1..-2],
|
64
65
|
revision: parts[1],
|
65
|
-
time: timestamp(parts[-2].to_i)
|
66
|
+
time: timestamp(parts[-2].to_i),
|
66
67
|
}
|
67
68
|
end
|
68
|
-
# rubocop:enable Metrics/AbcSize
|
69
69
|
|
70
70
|
def last_checkout_line
|
71
71
|
head_path = File.join(@git_path, 'logs', 'HEAD')
|
@@ -18,6 +18,7 @@ module Airbrake
|
|
18
18
|
# @macro call_filter
|
19
19
|
def call(notice)
|
20
20
|
return if notice[:context].key?(:repository)
|
21
|
+
|
21
22
|
attach_repository(notice)
|
22
23
|
end
|
23
24
|
|
@@ -39,6 +40,7 @@ module Airbrake
|
|
39
40
|
end
|
40
41
|
|
41
42
|
return unless @repository
|
43
|
+
|
42
44
|
notice[:context][:repository] = @repository
|
43
45
|
end
|
44
46
|
|
@@ -46,6 +48,7 @@ module Airbrake
|
|
46
48
|
|
47
49
|
def detect_git_version
|
48
50
|
return unless which('git')
|
51
|
+
|
49
52
|
Gem::Version.new(`git --version`.split[2])
|
50
53
|
end
|
51
54
|
|
@@ -30,6 +30,7 @@ module Airbrake
|
|
30
30
|
|
31
31
|
@revision = find_revision
|
32
32
|
return unless @revision
|
33
|
+
|
33
34
|
notice[:context][:revision] = @revision
|
34
35
|
end
|
35
36
|
|
@@ -41,6 +42,7 @@ module Airbrake
|
|
41
42
|
|
42
43
|
head = File.read(head_path)
|
43
44
|
return head unless head.start_with?(PREFIX)
|
45
|
+
|
44
46
|
head = head.chomp[PREFIX.size..-1]
|
45
47
|
|
46
48
|
ref_path = File.join(@git_path, head)
|
@@ -4,7 +4,7 @@ module Airbrake
|
|
4
4
|
# notice, but specified keys.
|
5
5
|
#
|
6
6
|
# @example
|
7
|
-
# filter = Airbrake::Filters::
|
7
|
+
# filter = Airbrake::Filters::KeysAllowlist.new(
|
8
8
|
# [:email, /credit/i, 'password']
|
9
9
|
# )
|
10
10
|
# airbrake.add_filter(filter)
|
@@ -22,9 +22,9 @@ module Airbrake
|
|
22
22
|
# # email: 'john@example.com',
|
23
23
|
# # account_id: 42 }
|
24
24
|
#
|
25
|
-
# @see
|
25
|
+
# @see KeysBlocklist
|
26
26
|
# @see KeysFilter
|
27
|
-
class
|
27
|
+
class KeysAllowlist
|
28
28
|
include KeysFilter
|
29
29
|
|
30
30
|
def initialize(*)
|
@@ -4,7 +4,7 @@ module Airbrake
|
|
4
4
|
# list of parameters in the payload of a notice.
|
5
5
|
#
|
6
6
|
# @example
|
7
|
-
# filter = Airbrake::Filters::
|
7
|
+
# filter = Airbrake::Filters::KeysBlocklist.new(
|
8
8
|
# [:email, /credit/i, 'password']
|
9
9
|
# )
|
10
10
|
# airbrake.add_filter(filter)
|
@@ -22,10 +22,10 @@ module Airbrake
|
|
22
22
|
# # email: '[Filtered]',
|
23
23
|
# # credit_card: '[Filtered]' }
|
24
24
|
#
|
25
|
-
# @see
|
25
|
+
# @see KeysAllowlist
|
26
26
|
# @see KeysFilter
|
27
27
|
# @api private
|
28
|
-
class
|
28
|
+
class KeysBlocklist
|
29
29
|
include KeysFilter
|
30
30
|
|
31
31
|
def initialize(*)
|
@@ -7,8 +7,8 @@ module Airbrake
|
|
7
7
|
# class that includes this module must implement.
|
8
8
|
#
|
9
9
|
# @see Notice
|
10
|
-
# @see
|
11
|
-
# @see
|
10
|
+
# @see KeysAllowlist
|
11
|
+
# @see KeysBlocklist
|
12
12
|
# @api private
|
13
13
|
module KeysFilter
|
14
14
|
# @return [String] The label to replace real values of filtered payload
|
@@ -19,19 +19,30 @@ module Airbrake
|
|
19
19
|
VALID_PATTERN_CLASSES = [String, Symbol, Regexp].freeze
|
20
20
|
|
21
21
|
# @return [Array<Symbol>] parts of a Notice's payload that can be modified
|
22
|
-
# by
|
22
|
+
# by blocklist/allowlist filters
|
23
23
|
FILTERABLE_KEYS = %i[environment session params].freeze
|
24
24
|
|
25
25
|
# @return [Array<Symbol>] parts of a Notice's *context* payload that can
|
26
|
-
# be modified by
|
27
|
-
FILTERABLE_CONTEXT_KEYS = %i[
|
26
|
+
# be modified by blocklist/allowlist filters
|
27
|
+
FILTERABLE_CONTEXT_KEYS = %i[
|
28
|
+
user
|
29
|
+
|
30
|
+
# Provided by Airbrake::Rack::HttpHeadersFilter
|
31
|
+
headers
|
32
|
+
referer
|
33
|
+
httpMethod
|
34
|
+
|
35
|
+
# Provided by Airbrake::Rack::ContextFilter
|
36
|
+
userAddr
|
37
|
+
userAgent
|
38
|
+
].freeze
|
28
39
|
|
29
40
|
include Loggable
|
30
41
|
|
31
42
|
# @return [Integer]
|
32
43
|
attr_reader :weight
|
33
44
|
|
34
|
-
# Creates a new
|
45
|
+
# Creates a new KeysBlocklist or KeysAllowlist filter that uses the given
|
35
46
|
# +patterns+ for filtering a notice's payload.
|
36
47
|
#
|
37
48
|
# @param [Array<String,Regexp,Symbol>] patterns
|
@@ -53,10 +64,14 @@ module Airbrake
|
|
53
64
|
validate_patterns
|
54
65
|
end
|
55
66
|
|
56
|
-
FILTERABLE_KEYS.each
|
67
|
+
FILTERABLE_KEYS.each do |key|
|
68
|
+
notice[key] = filter_hash(notice[key])
|
69
|
+
end
|
70
|
+
|
57
71
|
FILTERABLE_CONTEXT_KEYS.each { |key| filter_context_key(notice, key) }
|
58
72
|
|
59
73
|
return unless notice[:context][:url]
|
74
|
+
|
60
75
|
filter_url(notice)
|
61
76
|
end
|
62
77
|
|
@@ -70,26 +85,26 @@ module Airbrake
|
|
70
85
|
def filter_hash(hash)
|
71
86
|
return hash unless hash.is_a?(Hash)
|
72
87
|
|
88
|
+
hash_copy = hash.dup
|
89
|
+
|
73
90
|
hash.each_key do |key|
|
74
91
|
if should_filter?(key.to_s)
|
75
|
-
|
76
|
-
elsif
|
77
|
-
filter_hash(
|
92
|
+
hash_copy[key] = FILTERED
|
93
|
+
elsif hash_copy[key].is_a?(Hash)
|
94
|
+
hash_copy[key] = filter_hash(hash_copy[key])
|
78
95
|
elsif hash[key].is_a?(Array)
|
79
|
-
|
96
|
+
hash_copy[key].each_with_index do |h, i|
|
97
|
+
hash_copy[key][i] = filter_hash(h)
|
98
|
+
end
|
80
99
|
end
|
81
100
|
end
|
101
|
+
|
102
|
+
hash_copy
|
82
103
|
end
|
83
104
|
|
84
105
|
def filter_url_params(url)
|
85
106
|
url.query = Hash[URI.decode_www_form(url.query)].map do |key, val|
|
86
|
-
|
87
|
-
# invalid characters, so be sure to escape individual components.
|
88
|
-
if should_filter?(key)
|
89
|
-
"#{URI.encode_www_form_component(key)}=[Filtered]"
|
90
|
-
else
|
91
|
-
"#{URI.encode_www_form_component(key)}=#{URI.encode_www_form_component(val)}"
|
92
|
-
end
|
107
|
+
should_filter?(key) ? "#{key}=[Filtered]" : "#{key}=#{val}"
|
93
108
|
end.join('&')
|
94
109
|
|
95
110
|
url.to_s
|
@@ -103,6 +118,7 @@ module Airbrake
|
|
103
118
|
end
|
104
119
|
|
105
120
|
return unless url.query
|
121
|
+
|
106
122
|
notice[:context][:url] = filter_url_params(url)
|
107
123
|
end
|
108
124
|
|
@@ -111,6 +127,7 @@ module Airbrake
|
|
111
127
|
|
112
128
|
@patterns = @patterns.flat_map do |pattern|
|
113
129
|
next(pattern) unless pattern.respond_to?(:call)
|
130
|
+
|
114
131
|
pattern.call
|
115
132
|
end
|
116
133
|
end
|
@@ -124,14 +141,16 @@ module Airbrake
|
|
124
141
|
|
125
142
|
logger.error(
|
126
143
|
"#{LOG_LABEL} one of the patterns in #{self.class} is invalid. " \
|
127
|
-
"Known patterns: #{@patterns}"
|
144
|
+
"Known patterns: #{@patterns}",
|
128
145
|
)
|
129
146
|
end
|
130
147
|
|
131
148
|
def filter_context_key(notice, key)
|
132
149
|
return unless notice[:context][key]
|
133
150
|
return if notice[:context][key] == FILTERED
|
134
|
-
|
151
|
+
unless should_filter?(key)
|
152
|
+
return notice[:context][key] = filter_hash(notice[:context][key])
|
153
|
+
end
|
135
154
|
|
136
155
|
notice[:context][key] = FILTERED
|
137
156
|
end
|
@@ -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,10 +33,14 @@ 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
|
+
# @return [Regexp] the regexp that is applied after the feature regexps
|
41
|
+
# were used
|
42
|
+
POST_FILTER = /(?<=[values|in ]\().+(?=\))/i.freeze
|
43
|
+
|
40
44
|
# @return [Hash{Symbol=>Array<Symbol>}] a set of features that corresponds
|
41
45
|
# to a certain dialect
|
42
46
|
DIALECT_FEATURES = {
|
@@ -60,7 +64,7 @@ module Airbrake
|
|
60
64
|
cassandra: %i[
|
61
65
|
single_quotes uuids numeric_literals boolean_literals
|
62
66
|
hexadecimal_literals comments multi_line_comments
|
63
|
-
].freeze
|
67
|
+
].freeze,
|
64
68
|
}.freeze
|
65
69
|
|
66
70
|
# @return [Hash{Symbol=>Regexp}] a set of regexps to check for unmatches
|
@@ -72,9 +76,22 @@ module Airbrake
|
|
72
76
|
sqlite: %r{'|/\*|\*/},
|
73
77
|
cassandra: %r{'|/\*|\*/},
|
74
78
|
oracle: %r{'|/\*|\*/},
|
75
|
-
oracle_enhanced: %r{'|/\*|\*/}
|
79
|
+
oracle_enhanced: %r{'|/\*|\*/},
|
76
80
|
}.freeze
|
77
81
|
|
82
|
+
# @return [Array<Regexp>] the list of queries to be ignored
|
83
|
+
IGNORED_QUERIES = [
|
84
|
+
/\ACOMMIT/i,
|
85
|
+
/\ABEGIN/i,
|
86
|
+
/\ASET/i,
|
87
|
+
/\ASHOW/i,
|
88
|
+
/\AWITH/i,
|
89
|
+
/FROM pg_attribute/i,
|
90
|
+
/FROM pg_index/i,
|
91
|
+
/FROM pg_class/i,
|
92
|
+
/FROM pg_type/i,
|
93
|
+
].freeze
|
94
|
+
|
78
95
|
def initialize(dialect)
|
79
96
|
@dialect =
|
80
97
|
case dialect
|
@@ -95,7 +112,14 @@ module Airbrake
|
|
95
112
|
def call(resource)
|
96
113
|
return unless resource.respond_to?(:query)
|
97
114
|
|
98
|
-
|
115
|
+
query = resource.query
|
116
|
+
if IGNORED_QUERIES.any? { |q| q =~ query }
|
117
|
+
resource.ignore!
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
q = query.gsub(@regexp, FILTERED)
|
122
|
+
q.gsub!(POST_FILTER, FILTERED) if q =~ POST_FILTER
|
99
123
|
q = ERROR_MSG if UNMATCHED_PAIR[@dialect] =~ q
|
100
124
|
resource.query = q
|
101
125
|
end
|
@@ -16,7 +16,7 @@ module Airbrake
|
|
16
16
|
String,
|
17
17
|
Symbol,
|
18
18
|
Regexp,
|
19
|
-
Numeric
|
19
|
+
Numeric,
|
20
20
|
].freeze
|
21
21
|
|
22
22
|
# Variables starting with this prefix are not attached to a notice.
|
@@ -56,6 +56,7 @@ module Airbrake
|
|
56
56
|
def thread_variables(th)
|
57
57
|
th.thread_variables.map.with_object({}) do |var, h|
|
58
58
|
next if var.to_s.start_with?(IGNORE_PREFIX)
|
59
|
+
|
59
60
|
h[var] = sanitize_value(th.thread_variable_get(var))
|
60
61
|
end
|
61
62
|
end
|
@@ -63,6 +64,7 @@ module Airbrake
|
|
63
64
|
def fiber_variables(th)
|
64
65
|
th.keys.map.with_object({}) do |key, h|
|
65
66
|
next if key.to_s.start_with?(IGNORE_PREFIX)
|
67
|
+
|
66
68
|
h[key] = sanitize_value(th[key])
|
67
69
|
end
|
68
70
|
end
|
@@ -72,7 +74,7 @@ module Airbrake
|
|
72
74
|
thread_info[:group] = th.group.list.map(&:inspect)
|
73
75
|
thread_info[:priority] = th.priority
|
74
76
|
|
75
|
-
thread_info[:safe_level] = th.safe_level
|
77
|
+
thread_info[:safe_level] = th.safe_level if Airbrake::HAS_SAFE_LEVEL
|
76
78
|
end
|
77
79
|
|
78
80
|
def sanitize_value(value)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Grouppable adds the `#groups` method, so that we don't need to define it in
|
3
|
+
# all of performance models every time we add a model without groups.
|
4
|
+
#
|
5
|
+
# @since 4.9.0
|
6
|
+
# @api private
|
7
|
+
module Grouppable
|
8
|
+
def groups
|
9
|
+
{}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -21,7 +21,7 @@ module Airbrake
|
|
21
21
|
project_id: @config.project_id,
|
22
22
|
project_key: @config.project_key,
|
23
23
|
host: @config.host,
|
24
|
-
filter_chain: @filter_chain.inspect
|
24
|
+
filter_chain: @filter_chain.inspect,
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
@@ -30,7 +30,7 @@ module Airbrake
|
|
30
30
|
q.text("#<#{self.class}:0x#{(object_id << 1).to_s(16).rjust(16, '0')} ")
|
31
31
|
q.text(
|
32
32
|
"project_id=\"#{@config.project_id}\" project_key=\"#{@config.project_key}\" " \
|
33
|
-
"host=\"#{@config.host}\" filter_chain="
|
33
|
+
"host=\"#{@config.host}\" filter_chain=",
|
34
34
|
)
|
35
35
|
q.pp(@filter_chain)
|
36
36
|
q.text('>')
|
@@ -17,12 +17,12 @@ module Airbrake
|
|
17
17
|
# @api private
|
18
18
|
module Loggable
|
19
19
|
class << self
|
20
|
-
# @
|
20
|
+
# @return [Logger]
|
21
21
|
attr_writer :instance
|
22
22
|
|
23
23
|
# @return [Logger]
|
24
24
|
def instance
|
25
|
-
@instance ||= ::Logger.new(File::NULL)
|
25
|
+
@instance ||= ::Logger.new(File::NULL).tap { |l| l.level = ::Logger::WARN }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# MonotonicTime is a helper for getting monotonic time suitable for
|
3
|
+
# performance measurements. It guarantees that the time is strictly linearly
|
4
|
+
# increasing (unlike realtime).
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# MonotonicTime.time_in_ms #=> 287138801.144576
|
8
|
+
#
|
9
|
+
# @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
|
10
|
+
# @since v4.2.4
|
11
|
+
# @api private
|
12
|
+
module MonotonicTime
|
13
|
+
class << self
|
14
|
+
# @return [Integer] current monotonic time in milliseconds
|
15
|
+
def time_in_ms
|
16
|
+
time_in_nanoseconds / (10.0**6)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] current monotonic time in seconds
|
20
|
+
def time_in_s
|
21
|
+
time_in_nanoseconds / (10.0**9)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
27
|
+
|
28
|
+
def time_in_nanoseconds
|
29
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
30
|
+
end
|
31
|
+
|
32
|
+
elsif RUBY_ENGINE == 'jruby'
|
33
|
+
|
34
|
+
def time_in_nanoseconds
|
35
|
+
java.lang.System.nanoTime
|
36
|
+
end
|
37
|
+
|
38
|
+
else
|
39
|
+
|
40
|
+
def time_in_nanoseconds
|
41
|
+
time = Time.now
|
42
|
+
time.to_i * (10**9) + time.nsec
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/airbrake-ruby/notice.rb
CHANGED
@@ -4,19 +4,12 @@ module Airbrake
|
|
4
4
|
#
|
5
5
|
# @since v1.0.0
|
6
6
|
class Notice
|
7
|
-
# @return [Hash{Symbol=>String}] the information about the notifier library
|
8
|
-
NOTIFIER = {
|
9
|
-
name: 'airbrake-ruby'.freeze,
|
10
|
-
version: Airbrake::AIRBRAKE_RUBY_VERSION,
|
11
|
-
url: 'https://github.com/airbrake/airbrake-ruby'.freeze
|
12
|
-
}.freeze
|
13
|
-
|
14
7
|
# @return [Hash{Symbol=>String,Hash}] the information to be displayed in the
|
15
8
|
# Context tab in the dashboard
|
16
9
|
CONTEXT = {
|
17
10
|
os: RUBY_PLATFORM,
|
18
11
|
language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
|
19
|
-
notifier:
|
12
|
+
notifier: Airbrake::NOTIFIER_INFO,
|
20
13
|
}.freeze
|
21
14
|
|
22
15
|
# @return [Integer] the maxium size of the JSON payload in bytes
|
@@ -32,7 +25,7 @@ module Airbrake
|
|
32
25
|
IOError,
|
33
26
|
NotImplementedError,
|
34
27
|
JSON::GeneratorError,
|
35
|
-
Encoding::UndefinedConversionError
|
28
|
+
Encoding::UndefinedConversionError,
|
36
29
|
].freeze
|
37
30
|
|
38
31
|
# @return [Array<Symbol>] the list of keys that can be be overwritten with
|
@@ -51,11 +44,7 @@ module Airbrake
|
|
51
44
|
|
52
45
|
include Ignorable
|
53
46
|
include Loggable
|
54
|
-
|
55
|
-
# @since v1.7.0
|
56
|
-
# @return [Hash{Symbol=>Object}] the hash with arbitrary objects to be used
|
57
|
-
# in filters
|
58
|
-
attr_reader :stash
|
47
|
+
include Stashable
|
59
48
|
|
60
49
|
# @api private
|
61
50
|
def initialize(exception, params = {})
|
@@ -64,13 +53,14 @@ module Airbrake
|
|
64
53
|
errors: NestedException.new(exception).as_json,
|
65
54
|
context: context,
|
66
55
|
environment: {
|
67
|
-
program_name: $PROGRAM_NAME
|
56
|
+
program_name: $PROGRAM_NAME,
|
68
57
|
},
|
69
58
|
session: {},
|
70
|
-
params: params
|
59
|
+
params: params,
|
71
60
|
}
|
72
|
-
@stash = { exception: exception }
|
73
61
|
@truncator = Airbrake::Truncator.new(PAYLOAD_MAX_SIZE)
|
62
|
+
|
63
|
+
stash[:exception] = exception
|
74
64
|
end
|
75
65
|
|
76
66
|
# Converts the notice to JSON. Calls +to_json+ on each object inside
|
@@ -79,7 +69,7 @@ module Airbrake
|
|
79
69
|
#
|
80
70
|
# @return [Hash{String=>String}, nil]
|
81
71
|
# @api private
|
82
|
-
def to_json
|
72
|
+
def to_json(*_args)
|
83
73
|
loop do
|
84
74
|
begin
|
85
75
|
json = @payload.to_json
|
@@ -141,7 +131,7 @@ module Airbrake
|
|
141
131
|
# Make sure we always send hostname.
|
142
132
|
hostname: HOSTNAME,
|
143
133
|
|
144
|
-
severity: DEFAULT_SEVERITY
|
134
|
+
severity: DEFAULT_SEVERITY,
|
145
135
|
}.merge(CONTEXT).delete_if { |_key, val| val.nil? || val.empty? }
|
146
136
|
end
|
147
137
|
|
@@ -155,7 +145,7 @@ module Airbrake
|
|
155
145
|
logger.error(
|
156
146
|
"#{LOG_LABEL} truncation failed. File an issue at " \
|
157
147
|
"https://github.com/airbrake/airbrake-ruby " \
|
158
|
-
"and attach the following payload: #{@payload}"
|
148
|
+
"and attach the following payload: #{@payload}",
|
159
149
|
)
|
160
150
|
end
|
161
151
|
|