Skip to content

Commit 0e10f7c

Browse files
authored
Merge pull request rails#49095 from Shopify/activerecord-lazy-query-source
Optimize ActiveRecord::LogSubscriber#query_source_location
2 parents b10c1c8 + a7fdc1c commit 0e10f7c

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

activerecord/lib/active_record/log_subscriber.rb

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,25 @@ def debug(progname = nil, &block)
140140
end
141141

142142
def log_query_source
143-
source = extract_query_source_location(caller)
143+
source = query_source_location
144144

145145
if source
146146
logger.debug(" ↳ #{source}")
147147
end
148148
end
149149

150-
def extract_query_source_location(locations)
151-
backtrace_cleaner.clean(locations.lazy).first
150+
if Thread.respond_to?(:each_caller_location)
151+
def query_source_location
152+
Thread.each_caller_location do |location|
153+
frame = backtrace_cleaner.clean_frame(location)
154+
return frame if frame
155+
end
156+
nil
157+
end
158+
else
159+
def query_source_location
160+
backtrace_cleaner.clean(caller(1).lazy).first
161+
end
152162
end
153163

154164
def filter(name, value)

activerecord/test/cases/log_subscriber_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def test_verbose_query_with_ignored_callstack
204204
ActiveRecord.verbose_query_logs = true
205205

206206
logger = TestDebugLogSubscriber.new
207-
def logger.extract_query_source_location(*); nil; end
207+
def logger.query_source_location; nil; end
208208

209209
logger.sql(Event.new(0, sql: "hi mom!"))
210210
assert_equal 1, @logger.logged(:debug).size

activesupport/lib/active_support/backtrace_cleaner.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ def clean(backtrace, kind = :silent)
5454
end
5555
alias :filter :clean
5656

57+
# Returns the frame with all filters applied.
58+
# returns +nil+ if the frame was silenced.
59+
def clean_frame(frame, kind = :silent)
60+
frame = frame.to_s
61+
@filters.each do |f|
62+
frame = f.call(frame.to_s)
63+
end
64+
65+
case kind
66+
when :silent
67+
frame unless @silencers.any? { |s| s.call(frame) }
68+
when :noise
69+
frame if @silencers.any? { |s| s.call(frame) }
70+
else
71+
frame
72+
end
73+
end
74+
5775
# Adds a filter from the block provided. Each line in the backtrace will be
5876
# mapped against this filter.
5977
#

railties/test/backtrace_cleaner_test.rb

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def setup
88
@cleaner = Rails::BacktraceCleaner.new
99
end
1010

11-
test "should consider traces from irb lines as User code" do
11+
test "#clean should consider traces from irb lines as User code" do
1212
backtrace = [ "(irb):1",
1313
"/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'",
1414
"bin/rails:4:in `<main>'" ]
@@ -17,7 +17,7 @@ def setup
1717
assert_equal 1, result.length
1818
end
1919

20-
test "should show relative paths" do
20+
test "#clean should show relative paths" do
2121
backtrace = [ "./test/backtrace_cleaner_test.rb:123",
2222
"/Path/to/rails/activesupport/some_testing_file.rb:42:in `test'",
2323
"bin/rails:4:in `<main>'" ]
@@ -26,7 +26,7 @@ def setup
2626
assert_equal 1, result.length
2727
end
2828

29-
test "can filter for noise" do
29+
test "#clean can filter for noise" do
3030
backtrace = [ "(irb):1",
3131
"/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'",
3232
"bin/rails:4:in `<main>'" ]
@@ -36,10 +36,35 @@ def setup
3636
assert_equal 2, result.length
3737
end
3838

39-
test "should omit ActionView template methods names" do
39+
test "#clean should omit ActionView template methods names" do
4040
method_name = ActionView::Template.new(nil, "app/views/application/index.html.erb", nil, locals: []).send :method_name
4141
backtrace = [ "app/views/application/index.html.erb:4:in `block in #{method_name}'"]
4242
result = @cleaner.clean(backtrace, :all)
4343
assert_equal "app/views/application/index.html.erb:4", result[0]
4444
end
45+
46+
test "#clean_frame should consider traces from irb lines as User code" do
47+
assert_equal "(irb):1", @cleaner.clean_frame("(irb):1")
48+
assert_nil @cleaner.clean_frame("/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'")
49+
assert_nil @cleaner.clean_frame("bin/rails:4:in `<main>'")
50+
end
51+
52+
test "#clean_frame should show relative paths" do
53+
assert_equal "./test/backtrace_cleaner_test.rb:123", @cleaner.clean_frame("./test/backtrace_cleaner_test.rb:123")
54+
assert_nil @cleaner.clean_frame("/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'")
55+
assert_nil @cleaner.clean_frame("bin/rails:4:in `<main>'")
56+
end
57+
58+
test "#clean_frame can filter for noise" do
59+
assert_nil @cleaner.clean_frame("(irb):1", :noise)
60+
frame = @cleaner.clean_frame("/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'", :noise)
61+
assert_equal "/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'", frame
62+
assert_equal "bin/rails:4:in `<main>'", @cleaner.clean_frame("bin/rails:4:in `<main>'", :noise)
63+
end
64+
65+
test "#clean_frame should omit ActionView template methods names" do
66+
method_name = ActionView::Template.new(nil, "app/views/application/index.html.erb", nil, locals: []).send :method_name
67+
frame = @cleaner.clean_frame("app/views/application/index.html.erb:4:in `block in #{method_name}'", :all)
68+
assert_equal "app/views/application/index.html.erb:4", frame
69+
end
4570
end

0 commit comments

Comments
 (0)