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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2654c621778910c1a0b1926398480e21b967f86c63ff45b642e635d80bfebcd0
|
|
4
|
+
data.tar.gz: 54245db5c8aa3a64869f3ce18b9c95b662d01ba58a042150ea551f19e1bf7299
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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)
|
data/lib/query_owl/middleware.rb
CHANGED
|
@@ -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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
data/lib/query_owl/version.rb
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|