active_record_query_stats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7616eb499befdd021733555a3d398dac7b92e97d10ff68428e99f8d82f313cee
4
+ data.tar.gz: 9a2e50b46a3eb1fc891a73ed2389a5a78f6de22fbdaa63a5b3c792a6728e8f91
5
+ SHA512:
6
+ metadata.gz: 5757368a32479b2d53f131df441524656142cc4e311b422a32f70c1a703e6983cb3107f76de17b046fc45615834996295b8b93d0424ff96ae4c1a6cc379ff4d8
7
+ data.tar.gz: e857808cd7cc2d5834a0d96c16033a33e72de951908ab516ef68ace4d3ce18fd6eca6e455f4bfeba7ec0c2ec47c52a59929a836d46e2d6dc899cfc3650b802dc
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Tianwen (Tian) Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # [ActiveRecordQueryStats](https://github.com/tian-im/active_record_query_stats)
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/active_record_query_stats.svg)](https://badge.fury.io/rb/active_record_query_stats)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/9ba0a610043a2e1a9e74/maintainability)](https://codeclimate.com/github/tian-im/active_record_query_stats/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/9ba0a610043a2e1a9e74/test_coverage)](https://codeclimate.com/github/tian-im/active_record_query_stats/test_coverage)
7
+
8
+ ActiveRecordQueryStats produces simple ActiveRecord query stats at the end of each request in the following format:
9
+
10
+ ```shell
11
+ Query Stats
12
+ -----------
13
+ total: 6, real: 5, cached: 1
14
+ select: 4, insert: 0, update: 1, delete: 0
15
+ transaction: 0, savepoint: 0, lock: 0, rollback: 0, other: 0
16
+ ```
17
+
18
+ - **total:** total queries occurred during the request.
19
+ - **real:** queries having run against the database.
20
+ - **cached:** queries that Rails encounters again and returns the cached result instead of hitting the database (see [SQL Caching](https://guides.rubyonrails.org/caching_with_rails.html#sql-caching)).
21
+ - **select:** SELECT queries.
22
+ - **insert:** INSERT queries.
23
+ - **update:** UPDATE queries.
24
+ - **delete:** DELETE queries.
25
+ - **transaction:** TRANSACTION related queries.
26
+ - **savepoint:** SAVEPOINT related queries.
27
+ - **lock:** LOCK related queries.
28
+ - **rollback:** ROLLBACK related queries.
29
+ - **other:** the rest queries go here.
30
+
31
+ ## Install
32
+
33
+ Add the following line to file `Gemfile`.
34
+
35
+ ```ruby
36
+ gem 'active_record_query_stats', group: :development
37
+ ```
38
+
39
+ And run re-bundle in terminal.
40
+
41
+ ```shell
42
+ bundle install
43
+ ```
44
+
45
+ That's it. Start the Rails server and see the stats!
46
+
47
+ ## Configuration
48
+
49
+ The query stats template can be customized by overwriting the translation for `active_record_query_stats.stats_template`, e.g.:
50
+
51
+ ```yml
52
+ # config/locales/en.yml
53
+ en:
54
+ # ...
55
+ active_record_query_stats:
56
+ stats_template: |
57
+ Query Stats
58
+ -----------
59
+ total: %{total}, real: %{real}, cached: %{cached}
60
+ select: %{select}, insert: %{insert}, update: %{update}, delete: %{delete}
61
+ transaction: %{transaction}, savepoint: %{savepoint}, lock: %{lock}, rollback: %{rollback}, other: %{other}
62
+ ```
63
+
64
+ ## Implementation
65
+
66
+ ActiveRecordQueryStats is based on the [Active Support Instrumentation](https://guides.rubyonrails.org/active_support_instrumentation.html) to implement the features by subscribing the following Rails events:
67
+
68
+ - `sql.active_record`: collect and analyze the query executed by ActiveRecord from this event.
69
+ - `process_action.action_controller`: display the stats when a request is finished.
70
+
71
+ ## Documentation
72
+
73
+ - [API Reference](https://www.rubydoc.info/gems/active_record_query_stats)
74
+ - [Change Logs](https://github.com/tian-im/active_record_query_stats/blob/master/CHANGELOG.md)
75
+
76
+ ## Want to contribute?
77
+
78
+ Raise an [issue](https://github.com/tian-im/active_record_query_stats/issues/new), discuss and resolve!
79
+
80
+ ## License
81
+
82
+ This project uses [MIT License](https://github.com/tian-im/active_record_query_stats/blob/master/LICENSE).
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordQueryStats
4
+ # NOTE: by inheriting from `ActionController::LogSubscriber`,
5
+ # it ensures to display the stats after the tranditional Rails stats
6
+ # e.g. `Completed 204 No Content in 170ms (ActiveRecord: 80.1ms | Allocations: 72539)`
7
+ class ProcessActionSubscriber < ActionController::LogSubscriber
8
+ def process_action(_event)
9
+ debug color(stats_display, MAGENTA)
10
+ end
11
+
12
+ private
13
+
14
+ def stats_display
15
+ I18n.t 'active_record_query_stats.stats_template', **summary.to_payload
16
+ end
17
+
18
+ def summary
19
+ Summary.call
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record_query_stats/summary'
4
+
5
+ module ActiveRecordQueryStats
6
+ class SqlEventAnalyzer
7
+ def initialize(event)
8
+ @event = event
9
+ end
10
+
11
+ def call
12
+ return if skipping?
13
+
14
+ increase_total
15
+ return increase_cached if cached_query?
16
+
17
+ increase_real
18
+ increase_transaction_related || increase_statements || increase_other
19
+ end
20
+
21
+ private
22
+
23
+ delegate :payload, to: :@event
24
+ delegate(*Summary.instance_methods(false), to: :summary)
25
+
26
+ def increase_transaction_related
27
+ case payload[:sql]
28
+ when /\A\s*rollback/mi then increase_rollback
29
+ when /select .*for update/mi, /\A\s*lock/mi then increase_lock
30
+ when /transaction\s*\Z/i then increase_transaction
31
+ when /\A\s*(release )?savepoint/i then increase_savepoint
32
+ end
33
+ end
34
+
35
+ def increase_statements
36
+ case payload[:sql]
37
+ when /\A\s*select/i then increase_select
38
+ when /\A\s*insert/i then increase_insert
39
+ when /\A\s*update/i then increase_update
40
+ when /\A\s*delete/i then increase_delete
41
+ end
42
+ end
43
+
44
+ def summary
45
+ Summary.call
46
+ end
47
+
48
+ def skipping?
49
+ schema_query? || schema_migration_query? || explain_query?
50
+ end
51
+
52
+ def schema_query?
53
+ /SCHEMA/i =~ payload[:name]
54
+ end
55
+
56
+ def schema_migration_query?
57
+ /schema_migrations/i =~ payload[:sql]
58
+ end
59
+
60
+ def explain_query?
61
+ /EXPLAIN/i =~ payload[:name]
62
+ end
63
+
64
+ def cached_query?
65
+ /CACHE/i =~ payload[:name] || payload[:cached]
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record_query_stats/sql_event_analyzer'
4
+
5
+ module ActiveRecordQueryStats
6
+ class SqlSubscriber < ActiveRecord::LogSubscriber
7
+ def sql(event)
8
+ SqlEventAnalyzer.new(event).call
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordQueryStats
4
+ class Summary
5
+ STATS = %i[
6
+ total real cached
7
+ select insert update delete
8
+ transaction savepoint lock rollback other
9
+ ].freeze
10
+
11
+ def self.call
12
+ RequestStore[:active_record_query_stats] ||= new
13
+ end
14
+
15
+ STATS.each do |field|
16
+ define_method field do
17
+ instance_variable_get(:"@#{field}") || 0
18
+ end
19
+
20
+ define_method :"increase_#{field}" do
21
+ instance_variable_set :"@#{field}", try(field) + 1
22
+ end
23
+ end
24
+
25
+ def to_payload
26
+ STATS.each_with_object({}) do |field, memo|
27
+ memo[field] = try field
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordQueryStats
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n'
4
+ require 'request_store'
5
+ require 'active_support'
6
+ require 'active_record/log_subscriber'
7
+ require 'action_controller'
8
+ require 'action_controller/log_subscriber'
9
+
10
+ require 'active_record_query_stats/sql_subscriber'
11
+ require 'active_record_query_stats/process_action_subscriber'
12
+
13
+ # This lib utilizes the Active Support Instrumentation system
14
+ # @see https://guides.rubyonrails.org/active_support_instrumentation.html
15
+ module ActiveRecordQueryStats
16
+ # Setup I18n
17
+ I18n.load_path += Dir[File.join(__dir__, 'locales', 'active_record_query_stats.en.yml')]
18
+ I18n.reload! if I18n.backend.initialized?
19
+
20
+ # @see https://guides.rubyonrails.org/active_support_instrumentation.html#sql-active-record
21
+ SqlSubscriber.attach_to :active_record
22
+
23
+ # @see https://guides.rubyonrails.org/active_support_instrumentation.html#process-action-action-controller
24
+ ProcessActionSubscriber.attach_to :action_controller
25
+ end
@@ -0,0 +1,8 @@
1
+ en:
2
+ active_record_query_stats:
3
+ stats_template: |
4
+ Query Stats
5
+ -----------
6
+ total: %{total}, real: %{real}, cached: %{cached}
7
+ select: %{select}, insert: %{insert}, update: %{update}, delete: %{delete}
8
+ transaction: %{transaction}, savepoint: %{savepoint}, lock: %{lock}, rollback: %{rollback}, other: %{other}
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_record_query_stats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tian Chen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 5.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 5.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 5.2.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 5.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: i18n
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: request_store
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Query Stats logging for ActiveRecord
84
+ email:
85
+ - me@tian.im
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE
91
+ - README.md
92
+ - lib/active_record_query_stats.rb
93
+ - lib/active_record_query_stats/process_action_subscriber.rb
94
+ - lib/active_record_query_stats/sql_event_analyzer.rb
95
+ - lib/active_record_query_stats/sql_subscriber.rb
96
+ - lib/active_record_query_stats/summary.rb
97
+ - lib/active_record_query_stats/version.rb
98
+ - lib/locales/active_record_query_stats.en.yml
99
+ homepage: https://github.com/tian-im/active_record_query_stats
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ homepage_uri: https://github.com/tian-im/active_record_query_stats
104
+ source_code_uri: https://github.com/tian-im/active_record_query_stats
105
+ changelog_uri: https://github.com/tian-im/active_record_query_stats/blob/master/CHANGELOG.md
106
+ rubygems_mfa_required: 'true'
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 2.5.0
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.3.7
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Query Stats logging for ActiveRecord
126
+ test_files: []