sql_tracker 1.1.1 → 1.3.2
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/.github/workflows/ci.yml +31 -0
- data/.github/workflows/publish.yml +27 -0
- data/README.md +40 -0
- data/bin/sql_tracker +26 -1
- data/lib/sql_tracker.rb +11 -1
- data/lib/sql_tracker/handler.rb +21 -3
- data/lib/sql_tracker/report.rb +21 -30
- data/lib/sql_tracker/terminal.rb +28 -0
- data/lib/sql_tracker/version.rb +1 -1
- data/sql_tracker.gemspec +2 -2
- metadata +14 -13
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7212f79d9a123731752126baff019de8dc807c78442607c594ffc9c4261281f0
|
4
|
+
data.tar.gz: bf99e463dc00c245c49135b7d18d5ed3b0b325d2534cadf9d829232c3e92beb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 689f6f10d3cb217274ce2b55e7972ba453e69b34a8fbed03837608bc9f1131afee3b3d1315d6323ddcca93a2a44e625771dc58d6fb4c39be7b68c41c16df402f
|
7
|
+
data.tar.gz: 8d9ed0706326a36d886b77a7d8591a1cfc8e117774d149a870750b983eb746630d26e01456be186eca80a83723b6b662c65b32f79c51ab9dfba84a07ce1fa8aa
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
2
|
+
|
3
|
+
name: CI
|
4
|
+
|
5
|
+
on:
|
6
|
+
push:
|
7
|
+
branches: [ master ]
|
8
|
+
pull_request:
|
9
|
+
branches: [ master ]
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
strategy:
|
16
|
+
matrix:
|
17
|
+
ruby-version: ['2.3', '2.4', '2.5', '2.6', '2.7']
|
18
|
+
|
19
|
+
steps:
|
20
|
+
- uses: actions/checkout@v2
|
21
|
+
- name: Set up Ruby
|
22
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
23
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
24
|
+
uses: ruby/setup-ruby@v1
|
25
|
+
with:
|
26
|
+
ruby-version: ${{ matrix.ruby-version }}
|
27
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
28
|
+
- name: Install dependencies
|
29
|
+
run: bundle install
|
30
|
+
- name: Run tests
|
31
|
+
run: bundle exec rake test
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: Publish
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
name: Build + Publish
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- name: Set up Ruby 2.6
|
14
|
+
uses: actions/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: 2.6.x
|
17
|
+
|
18
|
+
- name: Publish to RubyGems
|
19
|
+
run: |
|
20
|
+
mkdir -p $HOME/.gem
|
21
|
+
touch $HOME/.gem/credentials
|
22
|
+
chmod 0600 $HOME/.gem/credentials
|
23
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
24
|
+
gem build *.gemspec
|
25
|
+
gem push *.gem
|
26
|
+
env:
|
27
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/README.md
CHANGED
@@ -29,6 +29,24 @@ To start tracking, simply start your rails application server. When your server
|
|
29
29
|
|
30
30
|
`sql_tracker` can also track sql queries when running rails tests (e.g. your controller or integration tests), it will dump the data after all the tests are finished.
|
31
31
|
|
32
|
+
### Tracking Using a Block
|
33
|
+
|
34
|
+
It is also possible to track queries executed within a block. This method uses a new subscriber to `sql.active_record` event notifications for each invocation. Results using this method are not saved to a file.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
query_data = SqlTracker.track do
|
38
|
+
# Run some active record queries
|
39
|
+
end
|
40
|
+
|
41
|
+
query_data.values
|
42
|
+
# =>
|
43
|
+
# [{
|
44
|
+
# :sql=>"SELECT * FROM users",
|
45
|
+
# :count=>1,
|
46
|
+
# :duration=>1.0,
|
47
|
+
# :source=>["app/models/user.rb:12"]
|
48
|
+
# }]
|
49
|
+
```
|
32
50
|
|
33
51
|
## Reporting
|
34
52
|
|
@@ -36,6 +54,28 @@ To generate report, run
|
|
36
54
|
```bash
|
37
55
|
sql_tracker tmp/sql_tracker-*.json
|
38
56
|
```
|
57
|
+
The output report looks like this:
|
58
|
+
```
|
59
|
+
==================================
|
60
|
+
Total Unique SQL Queries: 24
|
61
|
+
==================================
|
62
|
+
Count | Avg Time (ms) | SQL Query | Source
|
63
|
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
64
|
+
8 | 0.33 | SELECT `users`.* FROM `users` WHERE `users`.`id` = xxx LIMIT 1 | app/controllers/users_controller.rb:125:in `create'
|
65
|
+
| | | app/controllers/projects_controller.rb:9:in `block in update'
|
66
|
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
67
|
+
4 | 0.27 | SELECT `projects`.* FROM `projects` WHERE `projects`.`user_id` = xxx AND `projects`.`id` = xxx LIMIT 1 | app/controllers/projects_controller.rb:4:in `update'
|
68
|
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
69
|
+
2 | 0.27 | UPDATE `projects` SET `updated_at` = xxx WHERE `projects`.`id` = xxx | app/controllers/projects_controller.rb:9:in `block in update'
|
70
|
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
71
|
+
2 | 1.76 | SELECT projects.* FROM projects WHERE projects.priority BETWEEN xxx AND xxx ORDER BY created_at DESC | app/controllers/projects_controller.rb:35:in `index'
|
72
|
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
73
|
+
... ...
|
74
|
+
```
|
75
|
+
By default, the report will be sorted by the total count of each query, you can also choose to sort it by average duration:
|
76
|
+
```bash
|
77
|
+
sql_tracker tmp/sql_tracker-*.json --sort-by=duration
|
78
|
+
```
|
39
79
|
|
40
80
|
## Configurations
|
41
81
|
|
data/bin/sql_tracker
CHANGED
@@ -1,6 +1,31 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
require 'sql_tracker/terminal'
|
2
3
|
require 'sql_tracker/report'
|
3
4
|
require 'json'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
default_options = { sort_by: 'count', terminal_width: SqlTracker::Terminal.width }
|
9
|
+
|
10
|
+
parser = OptionParser.new(ARGV) do |o|
|
11
|
+
o.banner = 'Usage: sql_tracker [file.json]+ [--sort-by=COLUMN]'
|
12
|
+
|
13
|
+
o.on('-s', '--sort-by COLUMN', 'The name of column that is used for sorting the table. Options: count, duration') do |arg|
|
14
|
+
options[:sort_by] = arg if %(count duration).include?(arg)
|
15
|
+
end
|
16
|
+
|
17
|
+
o.on('-w', '--terminal-width WIDTH', Integer,
|
18
|
+
"The width of the printed report. " \
|
19
|
+
"Default: #{default_options[:terminal_width]}. " \
|
20
|
+
"Minimum #{SqlTracker::Terminal::MIN_WIDTH}") do |arg|
|
21
|
+
options[:terminal_width] = arg if arg >= SqlTracker::Terminal::MIN_WIDTH
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
parser.parse!
|
26
|
+
parser.abort(parser.help) if ARGV.empty?
|
27
|
+
|
28
|
+
options = default_options.merge(options)
|
4
29
|
|
5
30
|
reports = []
|
6
31
|
until ARGV.empty?
|
@@ -23,4 +48,4 @@ if reports.empty?
|
|
23
48
|
end
|
24
49
|
|
25
50
|
report = reports.inject(:+)
|
26
|
-
report.print_text
|
51
|
+
report.print_text(options)
|
data/lib/sql_tracker.rb
CHANGED
@@ -8,11 +8,21 @@ module SqlTracker
|
|
8
8
|
|
9
9
|
config = SqlTracker::Config.apply_defaults
|
10
10
|
handler = SqlTracker::Handler.new(config)
|
11
|
-
|
11
|
+
handler.subscribe
|
12
12
|
@already_initialized = true
|
13
13
|
|
14
14
|
at_exit { handler.save }
|
15
15
|
end
|
16
|
+
|
17
|
+
def self.track
|
18
|
+
config = SqlTracker::Config.apply_defaults.new
|
19
|
+
config.enabled = true
|
20
|
+
handler = SqlTracker::Handler.new(config)
|
21
|
+
handler.subscribe
|
22
|
+
yield
|
23
|
+
handler.unsubscribe
|
24
|
+
handler.data
|
25
|
+
end
|
16
26
|
end
|
17
27
|
|
18
28
|
if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i >= 3
|
data/lib/sql_tracker/handler.rb
CHANGED
@@ -12,10 +12,25 @@ module SqlTracker
|
|
12
12
|
@data = {} # {key: {sql:, count:, duration, source: []}, ...}
|
13
13
|
end
|
14
14
|
|
15
|
+
def subscribe
|
16
|
+
@subscription ||= ActiveSupport::Notifications.subscribe(
|
17
|
+
'sql.active_record',
|
18
|
+
self
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def unsubscribe
|
23
|
+
return unless @subscription
|
24
|
+
|
25
|
+
ActiveSupport::Notifications.unsubscribe(@subscription)
|
26
|
+
|
27
|
+
@subscription = nil
|
28
|
+
end
|
29
|
+
|
15
30
|
def call(_name, started, finished, _id, payload)
|
16
31
|
return unless @config.enabled
|
17
32
|
|
18
|
-
sql = payload[:sql]
|
33
|
+
sql = payload[:sql].dup
|
19
34
|
return unless track?(sql)
|
20
35
|
|
21
36
|
cleaned_trace = clean_trace(caller)
|
@@ -47,9 +62,12 @@ module SqlTracker
|
|
47
62
|
|
48
63
|
def clean_sql_query(query)
|
49
64
|
query.squish!
|
50
|
-
query.gsub!(/(\s(=|>|<|>=|<=|<>|!=)\s)('[^']+'
|
65
|
+
query.gsub!(/(\s(=|>|<|>=|<=|<>|!=)\s)('[^']+'|[\$\+\-\w\.]+)/, '\1xxx')
|
51
66
|
query.gsub!(/(\sIN\s)\([^\(\)]+\)/i, '\1(xxx)')
|
52
|
-
query.gsub!(/(\sBETWEEN\s)('[^']+'
|
67
|
+
query.gsub!(/(\sBETWEEN\s)('[^']+'|[\+\-\w\.]+)(\sAND\s)('[^']+'|[\+\-\w\.]+)/i, '\1xxx\3xxx')
|
68
|
+
query.gsub!(/(\sVALUES\s)\(.+\)/i, '\1(xxx)')
|
69
|
+
query.gsub!(/(\s(LIKE|ILIKE|SIMILAR TO|NOT SIMILAR TO)\s)('[^']+')/i, '\1xxx')
|
70
|
+
query.gsub!(/(\s(LIMIT|OFFSET)\s)(\d+)/i, '\1xxx')
|
53
71
|
query
|
54
72
|
end
|
55
73
|
|
data/lib/sql_tracker/report.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module SqlTracker
|
2
2
|
class Report
|
3
3
|
attr_accessor :raw_data
|
4
|
+
attr_accessor :terminal_width
|
4
5
|
|
5
6
|
def initialize(data)
|
6
7
|
self.raw_data = data
|
@@ -25,7 +26,9 @@ module SqlTracker
|
|
25
26
|
raw_data['data']
|
26
27
|
end
|
27
28
|
|
28
|
-
def print_text(
|
29
|
+
def print_text(options)
|
30
|
+
self.terminal_width = options.fetch(:terminal_width)
|
31
|
+
f = STDOUT
|
29
32
|
f.puts '=================================='
|
30
33
|
f.puts "Total Unique SQL Queries: #{data.keys.size}"
|
31
34
|
f.puts '=================================='
|
@@ -35,7 +38,8 @@ module SqlTracker
|
|
35
38
|
)
|
36
39
|
f.puts '-' * terminal_width
|
37
40
|
|
38
|
-
data.values
|
41
|
+
sorted_data = sort_data(data.values, options[:sort_by])
|
42
|
+
sorted_data.each do |row|
|
39
43
|
chopped_sql = wrap_text(row['sql'], sql_width)
|
40
44
|
source_list = wrap_list(row['source'].uniq, sql_width - 10)
|
41
45
|
avg_duration = row['duration'].to_f / row['count']
|
@@ -50,15 +54,26 @@ module SqlTracker
|
|
50
54
|
duration = line == 0 ? avg_duration.round(2) : ''
|
51
55
|
source = source_list.length > line ? source_list[line] : ''
|
52
56
|
query = row['sql'].length > line ? chopped_sql[line] : ''
|
53
|
-
f.printf(
|
54
|
-
"%-#{count_width}s | %-#{duration_width}s | %-#{sql_width}s | %-#{sql_width}s\n",
|
55
|
-
count, duration, query, source
|
56
|
-
)
|
57
|
+
f.printf(row_format, count, duration, query, source)
|
57
58
|
end
|
58
59
|
f.puts '-' * terminal_width
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
def row_format
|
64
|
+
"%-#{count_width}s | %-#{duration_width}s | %-#{sql_width}s | %-#{sql_width}s\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
def sort_data(data, sort_by)
|
68
|
+
data.sort_by do |d|
|
69
|
+
if sort_by == 'duration'
|
70
|
+
-d['duration'].to_f / d['count']
|
71
|
+
else
|
72
|
+
-d['count']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
62
77
|
def +(other)
|
63
78
|
unless self.class == other.class
|
64
79
|
raise ArgumentError, "cannot combine #{other.class}"
|
@@ -112,29 +127,5 @@ module SqlTracker
|
|
112
127
|
def duration_width
|
113
128
|
15
|
114
129
|
end
|
115
|
-
|
116
|
-
def terminal_width
|
117
|
-
@terminal_width ||= begin
|
118
|
-
result = unix? ? dynamic_width : 80
|
119
|
-
result < 10 ? 80 : result
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def dynamic_width
|
124
|
-
@dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
|
125
|
-
end
|
126
|
-
|
127
|
-
def dynamic_width_stty
|
128
|
-
`stty size 2>/dev/null`.split[1].to_i
|
129
|
-
end
|
130
|
-
|
131
|
-
def dynamic_width_tput
|
132
|
-
`tput cols 2>/dev/null`.to_i
|
133
|
-
end
|
134
|
-
|
135
|
-
def unix?
|
136
|
-
RUBY_PLATFORM =~
|
137
|
-
/(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
|
138
|
-
end
|
139
130
|
end
|
140
131
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SqlTracker
|
2
|
+
class Terminal
|
3
|
+
DEFAULT_WIDTH = 80
|
4
|
+
MIN_WIDTH = 10
|
5
|
+
|
6
|
+
def self.width
|
7
|
+
if unix?
|
8
|
+
result = (dynamic_width_stty.nonzero? || dynamic_width_tput)
|
9
|
+
result < MIN_WIDTH ? DEFAULT_WIDTH : result
|
10
|
+
else
|
11
|
+
DEFAULT_WIDTH
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.dynamic_width_stty
|
16
|
+
`stty size 2>/dev/null`.split[1].to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.dynamic_width_tput
|
20
|
+
`tput cols 2>/dev/null`.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.unix?
|
24
|
+
RUBY_PLATFORM =~
|
25
|
+
/(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/sql_tracker/version.rb
CHANGED
data/sql_tracker.gemspec
CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = ['sql_tracker']
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_development_dependency 'bundler'
|
23
|
-
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
24
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
25
25
|
spec.add_development_dependency 'activesupport', '>= 3.0.0'
|
26
26
|
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sql_tracker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Yue
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,8 +74,9 @@ executables:
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".github/workflows/ci.yml"
|
78
|
+
- ".github/workflows/publish.yml"
|
77
79
|
- ".gitignore"
|
78
|
-
- ".travis.yml"
|
79
80
|
- Gemfile
|
80
81
|
- LICENSE.txt
|
81
82
|
- README.md
|
@@ -88,6 +89,7 @@ files:
|
|
88
89
|
- lib/sql_tracker/handler.rb
|
89
90
|
- lib/sql_tracker/railtie.rb
|
90
91
|
- lib/sql_tracker/report.rb
|
92
|
+
- lib/sql_tracker/terminal.rb
|
91
93
|
- lib/sql_tracker/version.rb
|
92
94
|
- sql_tracker.gemspec
|
93
95
|
homepage: http://www.github.com/steventen/sql_tracker
|
@@ -109,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
111
|
- !ruby/object:Gem::Version
|
110
112
|
version: '0'
|
111
113
|
requirements: []
|
112
|
-
|
113
|
-
rubygems_version: 2.5.1
|
114
|
+
rubygems_version: 3.0.3
|
114
115
|
signing_key:
|
115
116
|
specification_version: 4
|
116
117
|
summary: Rails SQL Query Tracker
|