airbrake-ruby 3.2.2-java
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 +7 -0
- data/lib/airbrake-ruby.rb +554 -0
- data/lib/airbrake-ruby/async_sender.rb +119 -0
- data/lib/airbrake-ruby/backtrace.rb +194 -0
- data/lib/airbrake-ruby/code_hunk.rb +53 -0
- data/lib/airbrake-ruby/config.rb +238 -0
- data/lib/airbrake-ruby/config/validator.rb +63 -0
- data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
- data/lib/airbrake-ruby/file_cache.rb +48 -0
- data/lib/airbrake-ruby/filter_chain.rb +95 -0
- data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
- data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
- data/lib/airbrake-ruby/hash_keyable.rb +37 -0
- data/lib/airbrake-ruby/ignorable.rb +44 -0
- data/lib/airbrake-ruby/nested_exception.rb +39 -0
- data/lib/airbrake-ruby/notice.rb +165 -0
- data/lib/airbrake-ruby/notice_notifier.rb +228 -0
- data/lib/airbrake-ruby/performance_notifier.rb +161 -0
- data/lib/airbrake-ruby/promise.rb +99 -0
- data/lib/airbrake-ruby/response.rb +71 -0
- data/lib/airbrake-ruby/stat.rb +56 -0
- data/lib/airbrake-ruby/sync_sender.rb +111 -0
- data/lib/airbrake-ruby/tdigest.rb +393 -0
- data/lib/airbrake-ruby/time_truncate.rb +17 -0
- data/lib/airbrake-ruby/truncator.rb +115 -0
- data/lib/airbrake-ruby/version.rb +6 -0
- data/spec/airbrake_spec.rb +171 -0
- data/spec/async_sender_spec.rb +154 -0
- data/spec/backtrace_spec.rb +438 -0
- data/spec/code_hunk_spec.rb +118 -0
- data/spec/config/validator_spec.rb +189 -0
- data/spec/config_spec.rb +281 -0
- data/spec/deploy_notifier_spec.rb +41 -0
- data/spec/file_cache.rb +36 -0
- data/spec/filter_chain_spec.rb +83 -0
- data/spec/filters/context_filter_spec.rb +25 -0
- data/spec/filters/dependency_filter_spec.rb +14 -0
- data/spec/filters/exception_attributes_filter_spec.rb +63 -0
- data/spec/filters/gem_root_filter_spec.rb +44 -0
- data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
- data/spec/filters/git_repository_filter.rb +53 -0
- data/spec/filters/git_revision_filter_spec.rb +126 -0
- data/spec/filters/keys_blacklist_spec.rb +236 -0
- data/spec/filters/keys_whitelist_spec.rb +205 -0
- data/spec/filters/root_directory_filter_spec.rb +42 -0
- data/spec/filters/sql_filter_spec.rb +219 -0
- data/spec/filters/system_exit_filter_spec.rb +14 -0
- data/spec/filters/thread_filter_spec.rb +279 -0
- data/spec/fixtures/notroot.txt +7 -0
- data/spec/fixtures/project_root/code.rb +221 -0
- data/spec/fixtures/project_root/empty_file.rb +0 -0
- data/spec/fixtures/project_root/long_line.txt +1 -0
- data/spec/fixtures/project_root/short_file.rb +3 -0
- data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
- data/spec/helpers.rb +9 -0
- data/spec/ignorable_spec.rb +14 -0
- data/spec/nested_exception_spec.rb +75 -0
- data/spec/notice_notifier_spec.rb +436 -0
- data/spec/notice_notifier_spec/options_spec.rb +266 -0
- data/spec/notice_spec.rb +297 -0
- data/spec/performance_notifier_spec.rb +287 -0
- data/spec/promise_spec.rb +165 -0
- data/spec/response_spec.rb +82 -0
- data/spec/spec_helper.rb +102 -0
- data/spec/stat_spec.rb +35 -0
- data/spec/sync_sender_spec.rb +140 -0
- data/spec/tdigest_spec.rb +230 -0
- data/spec/time_truncate_spec.rb +13 -0
- data/spec/truncator_spec.rb +238 -0
- metadata +278 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Filters
|
3
|
+
# Attaches git repository URL to `context`.
|
4
|
+
# @api private
|
5
|
+
# @since v2.12.0
|
6
|
+
class GitRepositoryFilter
|
7
|
+
# @return [Integer]
|
8
|
+
attr_reader :weight
|
9
|
+
|
10
|
+
# @param [String] root_directory
|
11
|
+
def initialize(root_directory)
|
12
|
+
@git_path = File.join(root_directory, '.git')
|
13
|
+
@repository = nil
|
14
|
+
@git_version = Gem::Version.new(`git --version`.split[2])
|
15
|
+
@weight = 116
|
16
|
+
end
|
17
|
+
|
18
|
+
# @macro call_filter
|
19
|
+
def call(notice)
|
20
|
+
return if notice[:context].key?(:repository)
|
21
|
+
|
22
|
+
if @repository
|
23
|
+
notice[:context][:repository] = @repository
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
return unless File.exist?(@git_path)
|
28
|
+
|
29
|
+
@repository =
|
30
|
+
if @git_version >= Gem::Version.new('2.7.0')
|
31
|
+
`cd #{@git_path} && git config --get remote.origin.url`.chomp
|
32
|
+
else
|
33
|
+
"`git remote get-url` is unsupported in git #{@git_version}. " \
|
34
|
+
'Consider an upgrade to 2.7+'
|
35
|
+
end
|
36
|
+
|
37
|
+
return unless @repository
|
38
|
+
notice[:context][:repository] = @repository
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Filters
|
3
|
+
# Attaches current git revision to `context`.
|
4
|
+
# @api private
|
5
|
+
# @since v2.11.0
|
6
|
+
class GitRevisionFilter
|
7
|
+
# @return [Integer]
|
8
|
+
attr_reader :weight
|
9
|
+
|
10
|
+
# @return [String]
|
11
|
+
PREFIX = 'ref: '.freeze
|
12
|
+
|
13
|
+
# @param [String] root_directory
|
14
|
+
def initialize(root_directory)
|
15
|
+
@git_path = File.join(root_directory, '.git')
|
16
|
+
@revision = nil
|
17
|
+
@weight = 116
|
18
|
+
end
|
19
|
+
|
20
|
+
# @macro call_filter
|
21
|
+
def call(notice)
|
22
|
+
return if notice[:context].key?(:revision)
|
23
|
+
|
24
|
+
if @revision
|
25
|
+
notice[:context][:revision] = @revision
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
return unless File.exist?(@git_path)
|
30
|
+
|
31
|
+
@revision = find_revision
|
32
|
+
return unless @revision
|
33
|
+
notice[:context][:revision] = @revision
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def find_revision
|
39
|
+
head_path = File.join(@git_path, 'HEAD')
|
40
|
+
return unless File.exist?(head_path)
|
41
|
+
|
42
|
+
head = File.read(head_path)
|
43
|
+
return head unless head.start_with?(PREFIX)
|
44
|
+
head = head.chomp[PREFIX.size..-1]
|
45
|
+
|
46
|
+
ref_path = File.join(@git_path, head)
|
47
|
+
return File.read(ref_path).chomp if File.exist?(ref_path)
|
48
|
+
|
49
|
+
find_from_packed_refs(head)
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_from_packed_refs(head)
|
53
|
+
packed_refs_path = File.join(@git_path, 'packed-refs')
|
54
|
+
return head unless File.exist?(packed_refs_path)
|
55
|
+
|
56
|
+
File.readlines(packed_refs_path).each do |line|
|
57
|
+
next if %w[# ^].include?(line[0])
|
58
|
+
next unless (parts = line.split(' ')).size == 2
|
59
|
+
return parts.first if parts.last == head
|
60
|
+
end
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Filters
|
3
|
+
# A default Airbrake notice filter. Filters only specific keys listed in the
|
4
|
+
# list of parameters in the payload of a notice.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# filter = Airbrake::Filters::KeysBlacklist.new(
|
8
|
+
# Logger.new(STDOUT),
|
9
|
+
# [:email, /credit/i, 'password']
|
10
|
+
# )
|
11
|
+
# airbrake.add_filter(filter)
|
12
|
+
# airbrake.notify(StandardError.new('App crashed!'), {
|
13
|
+
# user: 'John'
|
14
|
+
# password: 's3kr3t',
|
15
|
+
# email: 'john@example.com',
|
16
|
+
# credit_card: '5555555555554444'
|
17
|
+
# })
|
18
|
+
#
|
19
|
+
# # The dashboard will display this parameter as is, but all other
|
20
|
+
# # values will be filtered:
|
21
|
+
# # { user: 'John',
|
22
|
+
# # password: '[Filtered]',
|
23
|
+
# # email: '[Filtered]',
|
24
|
+
# # credit_card: '[Filtered]' }
|
25
|
+
#
|
26
|
+
# @see KeysWhitelist
|
27
|
+
# @see KeysFilter
|
28
|
+
# @api private
|
29
|
+
class KeysBlacklist
|
30
|
+
include KeysFilter
|
31
|
+
|
32
|
+
def initialize(*)
|
33
|
+
super
|
34
|
+
@weight = -110
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Boolean] true if the key matches at least one pattern, false
|
38
|
+
# otherwise
|
39
|
+
def should_filter?(key)
|
40
|
+
@patterns.any? do |pattern|
|
41
|
+
if pattern.is_a?(Regexp)
|
42
|
+
key.match(pattern)
|
43
|
+
else
|
44
|
+
key.to_s == pattern.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Namespace for all standard filters. Custom filters can also go under this
|
3
|
+
# namespace.
|
4
|
+
module Filters
|
5
|
+
# This is a filter helper that endows a class ability to filter notices'
|
6
|
+
# payload based on the return value of the +should_filter?+ method that a
|
7
|
+
# class that includes this module must implement.
|
8
|
+
#
|
9
|
+
# @see Notice
|
10
|
+
# @see KeysWhitelist
|
11
|
+
# @see KeysBlacklist
|
12
|
+
# @api private
|
13
|
+
module KeysFilter
|
14
|
+
# @return [String] The label to replace real values of filtered payload
|
15
|
+
FILTERED = '[Filtered]'.freeze
|
16
|
+
|
17
|
+
# @return [Array<String,Symbol,Regexp>] the array of classes instances of
|
18
|
+
# which can compared with payload keys
|
19
|
+
VALID_PATTERN_CLASSES = [String, Symbol, Regexp].freeze
|
20
|
+
|
21
|
+
# @return [Array<Symbol>] parts of a Notice's payload that can be modified
|
22
|
+
# by blacklist/whitelist filters
|
23
|
+
FILTERABLE_KEYS = %i[environment session params].freeze
|
24
|
+
|
25
|
+
# @return [Array<Symbol>] parts of a Notice's *context* payload that can
|
26
|
+
# be modified by blacklist/whitelist filters
|
27
|
+
FILTERABLE_CONTEXT_KEYS = %i[user headers].freeze
|
28
|
+
|
29
|
+
# @return [Integer]
|
30
|
+
attr_reader :weight
|
31
|
+
|
32
|
+
# Creates a new KeysBlacklist or KeysWhitelist filter that uses the given
|
33
|
+
# +patterns+ for filtering a notice's payload.
|
34
|
+
#
|
35
|
+
# @param [Logger, #error] logger
|
36
|
+
# @param [Array<String,Regexp,Symbol>] patterns
|
37
|
+
def initialize(logger, patterns)
|
38
|
+
@logger = logger
|
39
|
+
@patterns = patterns
|
40
|
+
@valid_patterns = false
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!macro call_filter
|
44
|
+
# This is a mandatory method required by any filter integrated with
|
45
|
+
# FilterChain.
|
46
|
+
#
|
47
|
+
# @param [Notice] notice the notice to be filtered
|
48
|
+
# @return [void]
|
49
|
+
# @see FilterChain
|
50
|
+
def call(notice)
|
51
|
+
unless @valid_patterns
|
52
|
+
eval_proc_patterns!
|
53
|
+
validate_patterns
|
54
|
+
end
|
55
|
+
|
56
|
+
FILTERABLE_KEYS.each { |key| filter_hash(notice[key]) }
|
57
|
+
FILTERABLE_CONTEXT_KEYS.each { |key| filter_context_key(notice, key) }
|
58
|
+
|
59
|
+
return unless notice[:context][:url]
|
60
|
+
filter_url(notice)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @raise [NotImplementedError] if called directly
|
64
|
+
def should_filter?(_key)
|
65
|
+
raise NotImplementedError, 'method must be implemented in the included class'
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def filter_hash(hash)
|
71
|
+
return hash unless hash.is_a?(Hash)
|
72
|
+
|
73
|
+
hash.each_key do |key|
|
74
|
+
if should_filter?(key.to_s)
|
75
|
+
hash[key] = FILTERED
|
76
|
+
elsif hash[key].is_a?(Hash)
|
77
|
+
filter_hash(hash[key])
|
78
|
+
elsif hash[key].is_a?(Array)
|
79
|
+
hash[key].each { |h| filter_hash(h) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def filter_url_params(url)
|
85
|
+
url.query = Hash[URI.decode_www_form(url.query)].map do |key, val|
|
86
|
+
# Ruby < 2.2 raises InvalidComponentError if the query contains
|
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
|
93
|
+
end.join('&')
|
94
|
+
|
95
|
+
url.to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
def filter_url(notice)
|
99
|
+
begin
|
100
|
+
url = URI(notice[:context][:url])
|
101
|
+
rescue URI::InvalidURIError
|
102
|
+
return
|
103
|
+
end
|
104
|
+
|
105
|
+
return unless url.query
|
106
|
+
notice[:context][:url] = filter_url_params(url)
|
107
|
+
end
|
108
|
+
|
109
|
+
def eval_proc_patterns!
|
110
|
+
return unless @patterns.any? { |pattern| pattern.is_a?(Proc) }
|
111
|
+
|
112
|
+
@patterns = @patterns.flat_map do |pattern|
|
113
|
+
next(pattern) unless pattern.respond_to?(:call)
|
114
|
+
pattern.call
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def validate_patterns
|
119
|
+
@valid_patterns = @patterns.all? do |pattern|
|
120
|
+
VALID_PATTERN_CLASSES.any? { |c| pattern.is_a?(c) }
|
121
|
+
end
|
122
|
+
|
123
|
+
return if @valid_patterns
|
124
|
+
|
125
|
+
@logger.error(
|
126
|
+
"#{LOG_LABEL} one of the patterns in #{self.class} is invalid. " \
|
127
|
+
"Known patterns: #{@patterns}"
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
def filter_context_key(notice, key)
|
132
|
+
return unless notice[:context][key]
|
133
|
+
return if notice[:context][key] == FILTERED
|
134
|
+
return filter_hash(notice[:context][key]) unless should_filter?(key)
|
135
|
+
|
136
|
+
notice[:context][key] = FILTERED
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Filters
|
3
|
+
# A default Airbrake notice filter. Filters everything in the payload of a
|
4
|
+
# notice, but specified keys.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# filter = Airbrake::Filters::KeysBlacklist.new(
|
8
|
+
# Logger.new(STDOUT),
|
9
|
+
# [:email, /credit/i, 'password']
|
10
|
+
# )
|
11
|
+
# airbrake.add_filter(filter)
|
12
|
+
# airbrake.notify(StandardError.new('App crashed!'), {
|
13
|
+
# user: 'John',
|
14
|
+
# password: 's3kr3t',
|
15
|
+
# email: 'john@example.com',
|
16
|
+
# account_id: 42
|
17
|
+
# })
|
18
|
+
#
|
19
|
+
# # The dashboard will display this parameters as filtered, but other
|
20
|
+
# # values won't be affected:
|
21
|
+
# # { user: 'John',
|
22
|
+
# # password: '[Filtered]',
|
23
|
+
# # email: 'john@example.com',
|
24
|
+
# # account_id: 42 }
|
25
|
+
#
|
26
|
+
# @see KeysBlacklist
|
27
|
+
# @see KeysFilter
|
28
|
+
class KeysWhitelist
|
29
|
+
include KeysFilter
|
30
|
+
|
31
|
+
def initialize(*)
|
32
|
+
super
|
33
|
+
@weight = -100
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Boolean] true if the key doesn't match any pattern, false
|
37
|
+
# otherwise.
|
38
|
+
def should_filter?(key)
|
39
|
+
@patterns.none? do |pattern|
|
40
|
+
if pattern.is_a?(Regexp)
|
41
|
+
key.match(pattern)
|
42
|
+
else
|
43
|
+
key.to_s == pattern.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Filters
|
3
|
+
# Replaces root directory with a label.
|
4
|
+
# @api private
|
5
|
+
class RootDirectoryFilter
|
6
|
+
# @return [String]
|
7
|
+
PROJECT_ROOT_LABEL = '/PROJECT_ROOT'.freeze
|
8
|
+
|
9
|
+
# @return [Integer]
|
10
|
+
attr_reader :weight
|
11
|
+
|
12
|
+
def initialize(root_directory)
|
13
|
+
@root_directory = root_directory
|
14
|
+
@weight = 100
|
15
|
+
end
|
16
|
+
|
17
|
+
# @macro call_filter
|
18
|
+
def call(notice)
|
19
|
+
notice[:errors].each do |error|
|
20
|
+
error[:backtrace].each do |frame|
|
21
|
+
next unless (file = frame[:file])
|
22
|
+
file.sub!(/\A#{@root_directory}/, PROJECT_ROOT_LABEL)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Filters
|
3
|
+
# SqlFilter filters out sensitive data from {Airbrake::Query}. Sensitive
|
4
|
+
# data is everything that is not table names or fields (e.g. column values
|
5
|
+
# and such).
|
6
|
+
#
|
7
|
+
# Supports the following SQL dialects:
|
8
|
+
# * PostgreSQL
|
9
|
+
# * MySQL
|
10
|
+
# * SQLite
|
11
|
+
# * Cassandra
|
12
|
+
# * Oracle
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since v3.2.0
|
16
|
+
class SqlFilter
|
17
|
+
# @return [String] the label to replace real values of filtered query
|
18
|
+
FILTERED = '?'.freeze
|
19
|
+
|
20
|
+
# @return [String] the string that will replace the query in case we
|
21
|
+
# cannot filter it
|
22
|
+
ERROR_MSG = 'Error: Airbrake::Query was not filtered'.freeze
|
23
|
+
|
24
|
+
# @return [Hash{Symbol=>Regexp}] matchers for certain features of SQL
|
25
|
+
ALL_FEATURES = {
|
26
|
+
# rubocop:disable Metrics/LineLength
|
27
|
+
single_quotes: /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
|
28
|
+
double_quotes: /"(?:[^"]|"")*?(?:\\".*|"(?!"))/,
|
29
|
+
dollar_quotes: /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/,
|
30
|
+
uuids: /\{?(?:[0-9a-fA-F]\-*){32}\}?/,
|
31
|
+
numeric_literals: /\b-?(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/,
|
32
|
+
boolean_literals: /\b(?:true|false|null)\b/i,
|
33
|
+
hexadecimal_literals: /0x[0-9a-fA-F]+/,
|
34
|
+
comments: /(?:#|--).*?(?=\r|\n|$)/i,
|
35
|
+
multi_line_comments: %r{/\*(?:[^/]|/[^*])*?(?:\*/|/\*.*)},
|
36
|
+
oracle_quoted_strings: /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'\<.*?(?:\>'|$)|q'\(.*?(?:\)'|$)/
|
37
|
+
# rubocop:enable Metrics/LineLength
|
38
|
+
}.freeze
|
39
|
+
|
40
|
+
# @return [Hash{Symbol=>Array<Symbol>}] a set of features that corresponds
|
41
|
+
# to a certain dialect
|
42
|
+
DIALECT_FEATURES = {
|
43
|
+
default: ALL_FEATURES.keys,
|
44
|
+
mysql: %i[
|
45
|
+
single_quotes double_quotes numeric_literals boolean_literals
|
46
|
+
hexadecimal_literals comments multi_line_comments
|
47
|
+
].freeze,
|
48
|
+
postgres: %i[
|
49
|
+
single_quotes dollar_quotes uuids numeric_literals boolean_literals
|
50
|
+
comments multi_line_comments
|
51
|
+
].freeze,
|
52
|
+
sqlite: %i[
|
53
|
+
single_quotes numeric_literals boolean_literals hexadecimal_literals
|
54
|
+
comments multi_line_comments
|
55
|
+
].freeze,
|
56
|
+
oracle: %i[
|
57
|
+
single_quotes oracle_quoted_strings numeric_literals comments
|
58
|
+
multi_line_comments
|
59
|
+
].freeze,
|
60
|
+
cassandra: %i[
|
61
|
+
single_quotes uuids numeric_literals boolean_literals
|
62
|
+
hexadecimal_literals comments multi_line_comments
|
63
|
+
].freeze
|
64
|
+
}.freeze
|
65
|
+
|
66
|
+
# @return [Hash{Symbol=>Regexp}] a set of regexps to check for unmatches
|
67
|
+
# quotes after filtering (should be none)
|
68
|
+
UNMATCHED_PAIR = {
|
69
|
+
mysql: %r{'|"|/\*|\*/},
|
70
|
+
mysql2: %r{'|"|/\*|\*/},
|
71
|
+
postgres: %r{'|/\*|\*/|\$(?!\?)},
|
72
|
+
sqlite: %r{'|/\*|\*/},
|
73
|
+
cassandra: %r{'|/\*|\*/},
|
74
|
+
oracle: %r{'|/\*|\*/},
|
75
|
+
oracle_enhanced: %r{'|/\*|\*/}
|
76
|
+
}.freeze
|
77
|
+
|
78
|
+
def initialize(dialect)
|
79
|
+
@dialect =
|
80
|
+
case dialect
|
81
|
+
when /mysql/i then :mysql
|
82
|
+
when /postgres/i then :postgres
|
83
|
+
when /sqlite/i then :sqlite
|
84
|
+
when /oracle/i then :oracle
|
85
|
+
when /cassandra/i then :cassandra
|
86
|
+
else
|
87
|
+
:default
|
88
|
+
end
|
89
|
+
|
90
|
+
features = DIALECT_FEATURES[@dialect].map { |f| ALL_FEATURES[f] }
|
91
|
+
@regexp = Regexp.union(features)
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param [Airbrake::Query] resource
|
95
|
+
def call(resource)
|
96
|
+
return unless resource.respond_to?(:query)
|
97
|
+
|
98
|
+
q = resource.query.gsub(@regexp, FILTERED)
|
99
|
+
q = ERROR_MSG if UNMATCHED_PAIR[@dialect] =~ q
|
100
|
+
resource.query = q
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|