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 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsQueryTracer
4
+ VERSION = "0.1.0"
5
+ 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: []