sql_tracker 1.1.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d8d236c5adb10928d77ae8547ab0a88fffe16c4c
4
- data.tar.gz: af52c90be114c030abbe35ee5c4babda01b4a21f
2
+ SHA256:
3
+ metadata.gz: 7212f79d9a123731752126baff019de8dc807c78442607c594ffc9c4261281f0
4
+ data.tar.gz: bf99e463dc00c245c49135b7d18d5ed3b0b325d2534cadf9d829232c3e92beb8
5
5
  SHA512:
6
- metadata.gz: d0231bef73e7f00395eebc0afce207544cade7bd702b57abeea697ffd9dbd9da0b4cb4d42023b85b4a91d2d8c3044a936ffbbee720b59266b0e023cb5c0a08f3
7
- data.tar.gz: b41ac6f658f2dfcd9ebeab95bd694c998e8cf6a4d8ec5753564088af0110b918efa437a66fbd4687df206369d5a53927c266427f6c91ea9fbcdfed29eebd762a
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
- ActiveSupport::Notifications.subscribe('sql.active_record', handler)
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
@@ -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)('[^']+'|\w+)/, '\1xxx')
65
+ query.gsub!(/(\s(=|>|<|>=|<=|<>|!=)\s)('[^']+'|[\$\+\-\w\.]+)/, '\1xxx')
51
66
  query.gsub!(/(\sIN\s)\([^\(\)]+\)/i, '\1(xxx)')
52
- query.gsub!(/(\sBETWEEN\s)('[^']+'|\w+)(\sAND\s)('[^']+'|\w+)/i, '\1xxx\3xxx')
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
 
@@ -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(f = STDOUT)
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.sort_by { |d| -d['count'] }.each do |row|
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
@@ -1,3 +1,3 @@
1
1
  module SqlTracker
2
- VERSION = '1.1.1'.freeze
2
+ VERSION = '1.3.2'.freeze
3
3
  end
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', '~> 1.12'
23
- spec.add_development_dependency 'rake', '~> 10.0'
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.1.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: 2016-10-03 00:00:00.000000000 Z
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: '1.12'
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: '1.12'
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: '10.0'
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: '10.0'
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
- rubyforge_project:
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
data/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.3.0
6
- before_install:
7
- - gem install bundler
8
- - gem update bundler