activerecord-instrumentation 0.3.0.jlauer2 → 0.4.0.jlauer1
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 +4 -4
- data/.rubocop.yml +6 -2
- data/CHANGELOG.md +4 -1
- data/Gemfile.lock +1 -1
- data/lib/active_record/open_tracing.rb +4 -2
- data/lib/active_record/open_tracing/processor.rb +10 -3
- data/lib/active_record/open_tracing/sql_sanitizer.rb +37 -0
- data/lib/active_record/open_tracing/sql_sanitizer/base.rb +63 -0
- data/lib/active_record/open_tracing/sql_sanitizer/mysql.rb +20 -0
- data/lib/active_record/open_tracing/sql_sanitizer/postgres.rb +20 -0
- data/lib/active_record/open_tracing/sql_sanitizer/regexes.rb +33 -0
- data/lib/active_record/open_tracing/sql_sanitizer/sql_server.rb +17 -0
- data/lib/active_record/open_tracing/sql_sanitizer/sqlite.rb +18 -0
- data/lib/active_record/open_tracing/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48fc834ad963b8c31ea09c9974ca1d024a692a71592a20e75f0079c9a2c93203
|
4
|
+
data.tar.gz: 6be927a6854e55963f0fa1ab1ca0f5aa05c44e2e75d4eb8bebe7b3a459a00c9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4980f5d2f6d3c2081c42b1c5a2761fa10470b820ac75db0d1eda27e2b85fc4d973ddf901b69acc0aa43d0ab09e688934f7b847c6ed2c354431fa73c4e76d2ab
|
7
|
+
data.tar.gz: 2be68549771581ee9e470bcc2664036206fbaa973d6adecdfef4b7f03993c3b2b9b8b45e170ed11da4293453925c3bdebd38192c00937edaa2ac555b15c4de63
|
data/.rubocop.yml
CHANGED
@@ -22,7 +22,11 @@
|
|
22
22
|
- "./spec/*_helper*.rb"
|
23
23
|
- "./tasks/ci.rake"
|
24
24
|
- "Gemfile"
|
25
|
-
|
25
|
+
|
26
|
+
Layout/LineLength:
|
27
|
+
Exclude:
|
28
|
+
- "./**/*_spec.rb"
|
29
|
+
|
26
30
|
Metrics/ModuleLength:
|
27
31
|
Exclude:
|
28
32
|
- "./**/*_spec*.rb"
|
@@ -31,5 +35,5 @@
|
|
31
35
|
Exclude:
|
32
36
|
- "./**/*_spec*.rb"
|
33
37
|
|
34
|
-
Rails
|
38
|
+
Rails:
|
35
39
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
-
## 0.
|
4
|
+
## 0.4.0.jlauer1 04/24/2020
|
5
|
+
* Add SQL sanitizers
|
6
|
+
|
7
|
+
## 0.3.0 04/22/2020
|
5
8
|
* Set up build pipeline with circleci and gem-publisher
|
6
9
|
* Fixed linting issues
|
7
10
|
* Renamed gem to `activerecord-instrumentation`
|
data/Gemfile.lock
CHANGED
@@ -5,11 +5,13 @@ require "opentracing"
|
|
5
5
|
|
6
6
|
require "active_record/open_tracing/version"
|
7
7
|
require "active_record/open_tracing/processor"
|
8
|
+
require "active_record/open_tracing/sql_sanitizer"
|
8
9
|
|
9
10
|
module ActiveRecord
|
10
11
|
module OpenTracing
|
11
|
-
def self.instrument(tracer: ::OpenTracing.global_tracer)
|
12
|
-
|
12
|
+
def self.instrument(tracer: ::OpenTracing.global_tracer, sanitizer: nil)
|
13
|
+
sql_sanitizer = sanitizer && SqlSanitizer.build_sanitizer(sanitizer)
|
14
|
+
processor = Processor.new(tracer, sanitizer: sql_sanitizer)
|
13
15
|
|
14
16
|
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
15
17
|
processor.call(*args)
|
@@ -8,12 +8,15 @@ module ActiveRecord
|
|
8
8
|
SPAN_KIND = "client"
|
9
9
|
DB_TYPE = "sql"
|
10
10
|
|
11
|
-
|
11
|
+
attr_reader :tracer, :sanitizer
|
12
|
+
|
13
|
+
def initialize(tracer, sanitizer: nil)
|
12
14
|
@tracer = tracer
|
15
|
+
@sanitizer = sanitizer
|
13
16
|
end
|
14
17
|
|
15
18
|
def call(_event_name, start, finish, _id, payload)
|
16
|
-
span =
|
19
|
+
span = tracer.start_span(
|
17
20
|
payload[:name] || DEFAULT_OPERATION_NAME,
|
18
21
|
start_time: start,
|
19
22
|
tags: tags_for_payload(payload)
|
@@ -45,12 +48,16 @@ module ActiveRecord
|
|
45
48
|
"span.kind" => SPAN_KIND,
|
46
49
|
"db.instance" => db_instance,
|
47
50
|
"db.cached" => payload.fetch(:cached, false),
|
48
|
-
"db.statement" => payload.fetch(:sql).squish,
|
51
|
+
"db.statement" => sanitize_sql(payload.fetch(:sql).squish),
|
49
52
|
"db.type" => DB_TYPE,
|
50
53
|
"peer.address" => db_address
|
51
54
|
}
|
52
55
|
end
|
53
56
|
|
57
|
+
def sanitize_sql(sql)
|
58
|
+
sanitizer ? sanitizer.sanitize(sql) : sql
|
59
|
+
end
|
60
|
+
|
54
61
|
def db_instance
|
55
62
|
@db_instance ||= db_config.fetch(:database)
|
56
63
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/open_tracing/sql_sanitizer/base"
|
4
|
+
require "active_record/open_tracing/sql_sanitizer/mysql"
|
5
|
+
require "active_record/open_tracing/sql_sanitizer/postgres"
|
6
|
+
require "active_record/open_tracing/sql_sanitizer/sql_server"
|
7
|
+
require "active_record/open_tracing/sql_sanitizer/sqlite"
|
8
|
+
require "active_record/open_tracing/sql_sanitizer/regexes"
|
9
|
+
|
10
|
+
module ActiveRecord
|
11
|
+
module OpenTracing
|
12
|
+
module SqlSanitizer
|
13
|
+
KLASSES = {
|
14
|
+
mysql: Mysql,
|
15
|
+
postgres: Postgres,
|
16
|
+
sql_server: SqlServer,
|
17
|
+
sqlite: Sqlite
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def build_sanitizer(sanitizer_name)
|
22
|
+
sanitizer_klass(sanitizer_name).build
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def sanitizer_klass(sanitizer_name)
|
28
|
+
key = KLASSES.keys.detect do |name|
|
29
|
+
sanitizer_name.to_sym == name
|
30
|
+
end || (raise NameError, "Unknown sanitizer #{sanitizer_name.inspect}")
|
31
|
+
|
32
|
+
KLASSES.fetch(key)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module OpenTracing
|
5
|
+
module SqlSanitizer
|
6
|
+
class Base
|
7
|
+
require "active_record/open_tracing/sql_sanitizer/regexes"
|
8
|
+
include ActiveRecord::OpenTracing::SqlSanitizer::Regexes
|
9
|
+
|
10
|
+
def sanitize(sql)
|
11
|
+
scrubbed = scrub(sql.dup)
|
12
|
+
apply_substitutions(scrubbed)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def substitutions
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply_substitutions(str)
|
22
|
+
substitutions.inject(str.dup) do |memo, (regex, replacement)|
|
23
|
+
if replacement.respond_to?(:call)
|
24
|
+
memo.gsub(regex, &replacement)
|
25
|
+
else
|
26
|
+
memo.gsub(regex, replacement)
|
27
|
+
end
|
28
|
+
end.strip
|
29
|
+
end
|
30
|
+
|
31
|
+
def encodings?(encodings = %w[UTF-8 binary])
|
32
|
+
encodings.all? do |enc|
|
33
|
+
begin
|
34
|
+
Encoding.find(enc)
|
35
|
+
rescue StandardError
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
MAX_SQL_LENGTH = 16384
|
42
|
+
|
43
|
+
def scrub(str)
|
44
|
+
# safeguard - don't sanitize or scrub large SQL statements
|
45
|
+
return "" if !str.is_a?(String) || str.length > MAX_SQL_LENGTH
|
46
|
+
|
47
|
+
# Whatever encoding it is, it is valid and we can operate on it
|
48
|
+
return str if str.valid_encoding?
|
49
|
+
|
50
|
+
# Prefer scrub over convert
|
51
|
+
if str.respond_to?(:scrub)
|
52
|
+
str.scrub("_")
|
53
|
+
elsif encodings?(%w[UTF-8 binary])
|
54
|
+
str.encode("UTF-8", "binary", invalid: :replace, undef: :replace, replace: "_")
|
55
|
+
else
|
56
|
+
# Unable to scrub invalid sql encoding, returning empty string
|
57
|
+
""
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module OpenTracing
|
5
|
+
module SqlSanitizer
|
6
|
+
class Mysql < Base
|
7
|
+
def substitutions
|
8
|
+
[
|
9
|
+
[MYSQL_VAR_INTERPOLATION, ""],
|
10
|
+
[MYSQL_REMOVE_SINGLE_QUOTE_STRINGS, "?"],
|
11
|
+
[MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS, "?"],
|
12
|
+
[MYSQL_REMOVE_INTEGERS, "?"],
|
13
|
+
[MYSQL_IN_CLAUSE, "IN (?)"],
|
14
|
+
[MULTIPLE_QUESTIONS, "?"]
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module OpenTracing
|
5
|
+
module SqlSanitizer
|
6
|
+
class Postgres < Base
|
7
|
+
def substitutions
|
8
|
+
[
|
9
|
+
[PSQL_PLACEHOLDER, "?"],
|
10
|
+
[PSQL_VAR_INTERPOLATION, ""],
|
11
|
+
[PSQL_AFTER_WHERE, ->(c) { c.gsub(PSQL_REMOVE_STRINGS, "?") }],
|
12
|
+
[PSQL_REMOVE_INTEGERS, "?"],
|
13
|
+
[PSQL_IN_CLAUSE, "IN (?)"],
|
14
|
+
[MULTIPLE_SPACES, " "]
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module OpenTracing
|
5
|
+
module SqlSanitizer
|
6
|
+
module Regexes
|
7
|
+
MULTIPLE_SPACES = /\s+/.freeze
|
8
|
+
MULTIPLE_QUESTIONS = /\?(,\?)+/.freeze
|
9
|
+
|
10
|
+
PSQL_VAR_INTERPOLATION = /\[\[.*\]\]\s*$/.freeze
|
11
|
+
PSQL_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
12
|
+
PSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
13
|
+
PSQL_PLACEHOLDER = /\$\d+/.freeze
|
14
|
+
PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
15
|
+
PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|$)/i.freeze
|
16
|
+
|
17
|
+
MYSQL_VAR_INTERPOLATION = /\[\[.*\]\]\s*$/.freeze
|
18
|
+
MYSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
19
|
+
MYSQL_REMOVE_SINGLE_QUOTE_STRINGS = /'(?:\\'|[^']|'')*'/.freeze
|
20
|
+
MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS = /"(?:\\"|[^"]|"")*"/.freeze
|
21
|
+
MYSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
22
|
+
|
23
|
+
SQLITE_VAR_INTERPOLATION = /\[\[.*\]\]\s*$/.freeze
|
24
|
+
SQLITE_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
25
|
+
SQLITE_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
26
|
+
|
27
|
+
SQLSERVER_EXECUTESQL = /EXEC sp_executesql N'(.*?)'.*/.freeze
|
28
|
+
SQLSERVER_REMOVE_INTEGERS = /(?<!LIMIT )\b(?<!@)\d+\b/.freeze
|
29
|
+
SQLSERVER_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module OpenTracing
|
5
|
+
module SqlSanitizer
|
6
|
+
class SqlServer < Base
|
7
|
+
def substitutions
|
8
|
+
[
|
9
|
+
[SQLSERVER_EXECUTESQL, '\1'],
|
10
|
+
[SQLSERVER_REMOVE_INTEGERS, "?"],
|
11
|
+
[SQLSERVER_IN_CLAUSE, "IN (?)"]
|
12
|
+
]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module OpenTracing
|
5
|
+
module SqlSanitizer
|
6
|
+
class Sqlite < Base
|
7
|
+
def substitutions
|
8
|
+
[
|
9
|
+
[SQLITE_VAR_INTERPOLATION, ""],
|
10
|
+
[SQLITE_REMOVE_STRINGS, "?"],
|
11
|
+
[SQLITE_REMOVE_INTEGERS, "?"],
|
12
|
+
[MULTIPLE_SPACES, " "]
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-instrumentation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0.jlauer1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SaleMove TechMovers
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-04-
|
12
|
+
date: 2020-04-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -200,6 +200,13 @@ files:
|
|
200
200
|
- activerecord-instrumentation.gemspec
|
201
201
|
- lib/active_record/open_tracing.rb
|
202
202
|
- lib/active_record/open_tracing/processor.rb
|
203
|
+
- lib/active_record/open_tracing/sql_sanitizer.rb
|
204
|
+
- lib/active_record/open_tracing/sql_sanitizer/base.rb
|
205
|
+
- lib/active_record/open_tracing/sql_sanitizer/mysql.rb
|
206
|
+
- lib/active_record/open_tracing/sql_sanitizer/postgres.rb
|
207
|
+
- lib/active_record/open_tracing/sql_sanitizer/regexes.rb
|
208
|
+
- lib/active_record/open_tracing/sql_sanitizer/sql_server.rb
|
209
|
+
- lib/active_record/open_tracing/sql_sanitizer/sqlite.rb
|
203
210
|
- lib/active_record/open_tracing/version.rb
|
204
211
|
homepage: https://github.com/doximity/ruby-activerecord-opentracing
|
205
212
|
licenses:
|