From 19f881f715a3112ca7807e2d7eccd47f39384f87 Mon Sep 17 00:00:00 2001 From: Mikhail Varabyou Date: Tue, 3 Nov 2026 14:46:31 +0100 Subject: [PATCH] Store SQL queries only while a request is being processed --- lib/lograge/active_record_log_subscriber.rb | 1 + lib/lograge/sql.rb | 11 ++++++++ .../active_record_log_subscriber_spec.rb | 26 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/lib/lograge/active_record_log_subscriber.rb b/lib/lograge/active_record_log_subscriber.rb index 26260b7..89bcd46 100644 --- a/lib/lograge/active_record_log_subscriber.rb +++ b/lib/lograge/active_record_log_subscriber.rb @@ -38,6 +38,7 @@ def filter_query(event) end def valid?(event) + return false unless Lograge::Sql.store_active? return false if event.payload[:name]&.match?(Lograge::Sql.query_name_denylist) # Only store SQL events if `event.duration` is greater than the configured +min_duration+ diff --git a/lib/lograge/sql.rb b/lib/lograge/sql.rb index 3bd9c4f..a658300 100644 --- a/lib/lograge/sql.rb +++ b/lib/lograge/sql.rb @@ -34,6 +34,17 @@ def setup(config) # rubocop:disable Metrics/AbcSize Lograge::ActiveRecordLogSubscriber.attach_to(:active_record) end + # Whether SQL events can be stored right now. + # Only a logged request drains the store, so with RequestStore events are stored only while a request + # is being processed: queries from Sidekiq jobs, Action Cable workers or other background threads + # would otherwise pile up in their thread forever. + # @return [Boolean] + def store_active? + return true unless defined?(RequestStore) && RequestStore.respond_to?(:active?) + + RequestStore.active? + end + # Gets the store, preferring RequestStore if the gem is found. # @return [Hash, Thread] the RequestStore or the current Thread. def store diff --git a/spec/lograge/active_record_log_subscriber_spec.rb b/spec/lograge/active_record_log_subscriber_spec.rb index 04eea10..60c053e 100644 --- a/spec/lograge/active_record_log_subscriber_spec.rb +++ b/spec/lograge/active_record_log_subscriber_spec.rb @@ -13,8 +13,11 @@ Lograge::Sql.extract_event = proc {} Lograge::Sql.store.clear Lograge::Sql.query_name_denylist = Regexp.union([/\ASCHEMA\z/, /\ASolidCable::/]) + RequestStore.begin! end + after { RequestStore.end! } + context 'with keep_default_active_record_log not set' do before do allow(Rails).to receive_message_chain('application.config.lograge_sql.keep_default_active_record_log') { nil } @@ -98,6 +101,29 @@ end end + context 'outside of a request' do + before do + allow(Rails).to receive_message_chain('application.config.lograge_sql.keep_default_active_record_log') { true } + RequestStore.end! + end + + it 'does not store the event' do + log_subscriber.sql(event) + expect(Lograge::Sql.store[:lograge_sql_queries]).to be_nil + end + end + + context 'within a request' do + before do + allow(Rails).to receive_message_chain('application.config.lograge_sql.keep_default_active_record_log') { true } + end + + it 'stores the event' do + log_subscriber.sql(event) + expect(Lograge::Sql.store[:lograge_sql_queries]).not_to be_nil + end + end + context 'with default name denylist' do before do allow(Rails).to receive_message_chain('application.config.lograge_sql.keep_default_active_record_log') { true }