query_owl 0.4.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1269c2c24be94cd4ebf8ef07d12fa3e5da1aa2c1573256779f641d3f0a089c6e
4
- data.tar.gz: 891f6323d6a6ffd95b6e73e131ddbf2f556e90703f5aa67819d92a3bdf0b4ccb
3
+ metadata.gz: 2654c621778910c1a0b1926398480e21b967f86c63ff45b642e635d80bfebcd0
4
+ data.tar.gz: 54245db5c8aa3a64869f3ce18b9c95b662d01ba58a042150ea551f19e1bf7299
5
5
  SHA512:
6
- metadata.gz: 6fe7f83cbb596f8616ffdd11ccea40c4c2c485fa16b303f783a0b6b3a551eef4b8313e353585c8990061832227926738931fa582f9d2de8860b819afad301b96
7
- data.tar.gz: 87244c9aba6eff9996e97d1f5a3727b8fbf283d6b10647c05fb903d4d7833bc464fdd0c26db84895f8ed7dbc7e290bc6c22f5f225f01360649d03859b9e2dfb7
6
+ metadata.gz: 5d1a2a46359810b4f5e482097264c6d1d402c4344f5cf29b672d89a9c834c04e81936f07f3111454abf549e12c5a30614e6829c5d62563860de6b625220ce234
7
+ data.tar.gz: 41561e881c73aac6ed2ce8b7d3fc4f1a9f83d35fe92f4473713d274978451d9d7cd4ae7fada1af25739d3222457216ae13fdfb015b41bc8cf87116ee8aaa9b1a
@@ -34,6 +34,13 @@ QueryOwl.configure do |config|
34
34
  # Disabled by default (nil). Useful for persistence across restarts.
35
35
  # config.log_file = Rails.root.join("log/query_owl.log").to_s
36
36
 
37
+ # Paths to skip entirely — accepts strings (prefix match) or regexes.
38
+ # Useful for health check endpoints and other high-frequency low-value paths.
39
+ # config.ignore_paths = ["/up", "/healthz", %r{^/assets/}]
40
+
41
+ # Controllers to skip — matched against the Rails controller name (e.g. "rails/health").
42
+ # config.ignore_controllers = ["rails/health", "admin/metrics"]
43
+
37
44
  # Notifiers receive each detected event via #call(event).
38
45
  # Defaults to [QueryOwl::Notifiers::Logger] which writes to Rails.logger.
39
46
  # Use Console for TTY-aware colorized output (yellow: N+1, red: slow query).
@@ -5,7 +5,8 @@ module QueryOwl
5
5
 
6
6
  attr_reader :log_level, :backtrace_filter
7
7
  attr_accessor :enabled, :slow_query_threshold_ms, :n_plus_one_threshold, :backtrace_lines,
8
- :raise_on_n_plus_one, :event_store_size, :dashboard_enabled, :log_file
8
+ :raise_on_n_plus_one, :event_store_size, :dashboard_enabled, :log_file,
9
+ :ignore_paths, :ignore_controllers
9
10
 
10
11
  def notifiers
11
12
  @notifiers ||= [Notifiers::Logger.new]
@@ -31,6 +32,8 @@ module QueryOwl
31
32
  @event_store_size = 100
32
33
  @dashboard_enabled = Rails.env.development?
33
34
  @log_file = nil
35
+ @ignore_paths = []
36
+ @ignore_controllers = []
34
37
  end
35
38
 
36
39
  def log_level=(level)
@@ -4,41 +4,63 @@ module QueryOwl
4
4
  @app = app
5
5
  end
6
6
 
7
- def raise_on_n_plus_one!(events)
8
- event = events.find { |e| e[:type] == :n_plus_one }
9
- return unless event
10
-
11
- raise NPlusOneError, "N+1 detected: #{event[:sql]} (#{event[:count]} times) #{event[:backtrace].first}"
12
- end
13
-
14
7
  def call(env)
8
+ tracking = false
15
9
  return @app.call(env) unless QueryOwl.config.enabled
10
+ return @app.call(env) if ignored_path?(env["PATH_INFO"])
16
11
 
12
+ tracking = true
17
13
  QueryTracker.start!
18
14
  EagerLoadTracker.start!
19
15
  @app.call(env)
20
16
  ensure
21
- params = env["action_dispatch.request.path_parameters"] || {}
22
- RequestContext.set(controller: params[:controller], action: params[:action], path: env["PATH_INFO"])
23
- queries = QueryTracker.stop!
24
- eager_data = EagerLoadTracker.stop!
25
- context = RequestContext.current
26
- RequestContext.clear
27
- events = (Detector.detect_n_plus_one(queries) +
17
+ if tracking
18
+ params = env["action_dispatch.request.path_parameters"] || {}
19
+ RequestContext.set(controller: params[:controller], action: params[:action], path: env["PATH_INFO"])
20
+ queries = QueryTracker.stop!
21
+ eager_data = EagerLoadTracker.stop!
22
+ context = RequestContext.current
23
+ RequestContext.clear
24
+
25
+ unless ignored_controller?(context[:controller])
26
+ events = (Detector.detect_n_plus_one(queries) +
28
27
  Detector.detect_slow_queries(queries) +
29
28
  Detector.detect_unused_eager_loads(eager_data))
30
29
  .map { |e| e.merge(context) }
31
- events.each do |event|
32
- QueryOwl.config.notifiers.each do |notifier|
33
- notifier.call(event)
34
- rescue => e
35
- Rails.logger.error "[QueryOwl] Notifier #{notifier.class} raised: #{e.message}"
30
+ events.each do |event|
31
+ QueryOwl.config.notifiers.each do |notifier|
32
+ notifier.call(event)
33
+ rescue => e
34
+ Rails.logger.error "[QueryOwl] Notifier #{notifier.class} raised: #{e.message}"
35
+ end
36
+ end
37
+ Logger.log_summary(events)
38
+ events.each { |e| EventStore.push(e) }
39
+ FileLogger.append(events)
40
+ raise_on_n_plus_one!(events) if QueryOwl.config.raise_on_n_plus_one
36
41
  end
37
42
  end
38
- Logger.log_summary(events)
39
- events.each { |e| EventStore.push(e) }
40
- FileLogger.append(events)
41
- raise_on_n_plus_one!(events) if QueryOwl.config.raise_on_n_plus_one
42
43
  end
44
+
45
+ def raise_on_n_plus_one!(events)
46
+ event = events.find { |e| e[:type] == :n_plus_one }
47
+ return unless event
48
+
49
+ raise NPlusOneError, "N+1 detected: #{event[:sql]} (#{event[:count]} times) #{event[:backtrace].first}"
50
+ end
51
+
52
+ private
53
+
54
+ def ignored_path?(path)
55
+ QueryOwl.config.ignore_paths.any? do |pattern|
56
+ pattern.is_a?(Regexp) ? pattern.match?(path) : path.start_with?(pattern.to_s)
57
+ end
58
+ end
59
+
60
+ def ignored_controller?(controller)
61
+ return false unless controller
62
+
63
+ QueryOwl.config.ignore_controllers.any? { |name| name.to_s == controller }
64
+ end
43
65
  end
44
66
  end
@@ -1,3 +1,3 @@
1
1
  module QueryOwl
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -1,4 +1,8 @@
1
- # desc "Explaining what the task does"
2
- # task :query_owl do
3
- # # Task goes here
4
- # end
1
+ namespace :query_owl do
2
+ desc "Clear all events from the QueryOwl in-memory event store"
3
+ task clear: :environment do
4
+ count = QueryOwl::EventStore.size
5
+ QueryOwl::EventStore.clear
6
+ puts "[QueryOwl] Event store cleared (#{count} event#{"s" if count != 1} removed)."
7
+ end
8
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: query_owl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith