rails-query-tracer 0.1.0
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/rails-query-tracer/analyzer.rb +26 -0
- data/lib/rails-query-tracer/config.rb +17 -0
- data/lib/rails-query-tracer/reporter.rb +51 -0
- data/lib/rails-query-tracer/tracker.rb +40 -0
- data/lib/rails-query-tracer/version.rb +5 -0
- data/lib/rails-query-tracer.rb +15 -0
- metadata +71 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3d1dd2ec824da4387699b60da35df292eef2fd918e7633336abf08a9bd3f1a81
|
|
4
|
+
data.tar.gz: 821531d2ec1e9eddc23f4b521a9baa9b2128d8bd6fe65761e071120d53be5911
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b72ae1c1e7934242bf445dfa6962a7ec9be3e0591980b8e595d825aa7fd6c82788498fe8d1a90823851cb9949bd8a0e3c1ebdfdfc292af047e8aaf219f30b6b2
|
|
7
|
+
data.tar.gz: 4d70c4342fda57e8f93f66f4a46c3594226b8c70a9aacdfd4e7879ac777666a4cd75fa44922f725462b647a65377fece07a3c2d7b0df5edc8a565cce0604a118
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsQueryTracer
|
|
4
|
+
class Analyzer
|
|
5
|
+
def self.analyze(queries)
|
|
6
|
+
{ n_plus_one: detect_n_plus_one(queries), slow: detect_slow_queries(queries) }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.detect_n_plus_one(queries)
|
|
10
|
+
n1 = []
|
|
11
|
+
queries.group_by { |q| q[:sql] }.each do |sql, repeated|
|
|
12
|
+
next unless repeated.size > 1
|
|
13
|
+
|
|
14
|
+
locs = repeated.filter_map { |q| q[:location] }.uniq
|
|
15
|
+
n1 << { sql: sql, count: repeated.size, locations: locs }
|
|
16
|
+
end
|
|
17
|
+
n1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.detect_slow_queries(queries)
|
|
21
|
+
queries.select { |q| q[:duration] && q[:duration] > RailsQueryTracer::Config.slow_queries_threshold }.map do |q|
|
|
22
|
+
{ sql: q[:sql], duration: q[:duration], location: q[:location] }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsQueryTracer
|
|
4
|
+
module Config
|
|
5
|
+
class << self
|
|
6
|
+
attr_accessor :slow_queries_threshold, :show_stack_trace, :log_to_file
|
|
7
|
+
|
|
8
|
+
def setup_defaults
|
|
9
|
+
@slow_queries_threshold = 20
|
|
10
|
+
@show_stack_trace = true
|
|
11
|
+
@log_to_file = nil
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
setup_defaults
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsQueryTracer
|
|
4
|
+
class Reporter
|
|
5
|
+
RED = "\e[31m"
|
|
6
|
+
GREEN = "\e[32m"
|
|
7
|
+
YELLOW = "\e[33m"
|
|
8
|
+
CYAN = "\e[36m"
|
|
9
|
+
RESET = "\e[0m"
|
|
10
|
+
|
|
11
|
+
def self.report(report)
|
|
12
|
+
n_plus_one_queries = report[:n_plus_one] || []
|
|
13
|
+
slow = report[:slow] || []
|
|
14
|
+
|
|
15
|
+
report_n_plus_one(n_plus_one_queries)
|
|
16
|
+
report_slow_queries(slow)
|
|
17
|
+
report_success if n_plus_one_queries.empty? && slow.empty?
|
|
18
|
+
report_complete
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.report_n_plus_one(n_plus_one_queries)
|
|
22
|
+
return unless n_plus_one_queries.any?
|
|
23
|
+
|
|
24
|
+
puts "#{YELLOW}[RailsQueryTracer] N+1 Queries:#{RESET}"
|
|
25
|
+
n_plus_one_queries.each do |q|
|
|
26
|
+
locs = Array(q[:locations])
|
|
27
|
+
puts " #{q[:sql]} repeated #{q[:count]} times at #{locs.join(', ')}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.report_slow_queries(slow)
|
|
32
|
+
return unless slow.any?
|
|
33
|
+
|
|
34
|
+
puts "#{RED}[RailsQueryTracer] Slow Queries:#{RESET}"
|
|
35
|
+
slow.each do |q|
|
|
36
|
+
sql = q[:sql] || "unknown SQL"
|
|
37
|
+
duration = q[:duration] ? "#{q[:duration].round(2)}ms" : "unknown duration"
|
|
38
|
+
loc = q[:location] || "unknown"
|
|
39
|
+
puts " #{sql} (#{duration}) at #{loc}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.report_success
|
|
44
|
+
puts "#{GREEN}[RailsQueryTracer] No N+1 or slow queries detected#{RESET}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.report_complete
|
|
48
|
+
puts "#{CYAN}[RailsQueryTracer] Report Complete#{RESET}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/notifications"
|
|
4
|
+
|
|
5
|
+
module RailsQueryTracer
|
|
6
|
+
class Tracker
|
|
7
|
+
@queries = []
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
attr_reader :queries
|
|
11
|
+
|
|
12
|
+
def start
|
|
13
|
+
@notifications = ActiveSupport::Notifications
|
|
14
|
+
@subscriber = @notifications.subscribe("sql.active_record") do |_name, start, finish, _id, payload|
|
|
15
|
+
next if payload[:name] == "SCHEMA"
|
|
16
|
+
|
|
17
|
+
location = extract_location
|
|
18
|
+
@queries << {
|
|
19
|
+
sql: payload[:sql],
|
|
20
|
+
duration: (finish - start) * 1000,
|
|
21
|
+
location: location
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stop
|
|
27
|
+
ActiveSupport::Notifications.unsubscribe(@subscriber) if @subscriber
|
|
28
|
+
@subscriber = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def extract_location
|
|
32
|
+
caller.reject { |line| line.include?("/gems/") || line.include?("/ruby/") }.first
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def reset!
|
|
36
|
+
@queries = []
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails-query-tracer/version"
|
|
4
|
+
require "rails-query-tracer/config"
|
|
5
|
+
require "rails-query-tracer/tracker"
|
|
6
|
+
require "rails-query-tracer/analyzer"
|
|
7
|
+
require "rails-query-tracer/reporter"
|
|
8
|
+
|
|
9
|
+
module RailsQueryTracer
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
def self.configure
|
|
13
|
+
yield(Config)
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails-query-tracer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Davide V.
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-11-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.0'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '8.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '6.0'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
33
|
+
description: RailsQueryTracer hooks into ActiveRecord, finds N+1 queries, slow queries,
|
|
34
|
+
and provides actionable insights with optional dashboard.
|
|
35
|
+
email:
|
|
36
|
+
- daemonzone@users.noreply.github.com
|
|
37
|
+
executables: []
|
|
38
|
+
extensions: []
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- lib/rails-query-tracer.rb
|
|
42
|
+
- lib/rails-query-tracer/analyzer.rb
|
|
43
|
+
- lib/rails-query-tracer/config.rb
|
|
44
|
+
- lib/rails-query-tracer/reporter.rb
|
|
45
|
+
- lib/rails-query-tracer/tracker.rb
|
|
46
|
+
- lib/rails-query-tracer/version.rb
|
|
47
|
+
homepage: https://github.com/yourusername/rails-query-tracer
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
50
|
+
metadata:
|
|
51
|
+
rubygems_mfa_required: 'true'
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
require_paths:
|
|
55
|
+
- lib
|
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 2.7.6
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0'
|
|
66
|
+
requirements: []
|
|
67
|
+
rubygems_version: 3.1.6
|
|
68
|
+
signing_key:
|
|
69
|
+
specification_version: 4
|
|
70
|
+
summary: Trace and detect slow queries and N+1 problems in Rails
|
|
71
|
+
test_files: []
|