From a4333da1b7e22f894a16be7de04736345a06d445 Mon Sep 17 00:00:00 2001 From: Justin Howard Date: Thu, 5 Feb 2026 11:08:03 -0700 Subject: [PATCH 1/6] Justin H: Rails 7.1 spike --- epilog.gemspec | 4 +- lib/epilog/rails/railtie.rb | 52 +++++++++++++------ .../action_controller_subscriber_spec.rb | 20 +++---- spec/rails/action_view_subscriber_spec.rb | 12 ++--- spec/rails/railtie_spec.rb | 6 ++- spec/spec_helper.rb | 6 ++- 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/epilog.gemspec b/epilog.gemspec index d43f957..0fa5039 100644 --- a/epilog.gemspec +++ b/epilog.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.3' spec.add_development_dependency 'combustion', '~> 1.3' - spec.add_development_dependency 'rails', '>= 4.2', '< 7' + spec.add_development_dependency 'rails', '>= 4.2', '< 8' spec.add_development_dependency 'rspec', '~> 3.11' - spec.add_development_dependency 'rspec-rails', '~> 5.1' + spec.add_development_dependency 'rspec-rails', '>= 5.1' spec.add_development_dependency 'sqlite3', '~> 1.3' end diff --git a/lib/epilog/rails/railtie.rb b/lib/epilog/rails/railtie.rb index 0dace24..0480aba 100644 --- a/lib/epilog/rails/railtie.rb +++ b/lib/epilog/rails/railtie.rb @@ -44,8 +44,6 @@ def self.rails_6_1_higher? ] initializer 'epilog.configure' do |app| - disable_rails_defaults - ::Rails.logger ||= Logger.new($stdout) app.config.epilog.subscriptions.each do |namespace| @@ -56,29 +54,49 @@ def self.rails_6_1_higher? ) end end + + # In Rails 7.1+, some subscribers (like ActionView) are attached late + # in the initialization process, so we need to disable them after + # initialization completes + config.after_initialize do + disable_rails_defaults + end - private + class << self + private - def disable_rails_defaults - blacklisted_subscribers.each do |subscriber| - subscriber.patterns.each do |pattern| - unsubscribe_listeners(subscriber, pattern) + def disable_rails_defaults + blacklisted_subscribers.each do |subscriber| + subscriber.patterns.each do |pattern| + unsubscribe_listeners(subscriber, pattern) + end end end - end - def unsubscribe_listeners(subscriber, pattern) - notifier = ActiveSupport::Notifications.notifier - notifier.listeners_for(Array.wrap(pattern).first).each do |listener| - if listener.delegates_to?(subscriber) - ActiveSupport::Notifications.unsubscribe(listener) + def unsubscribe_listeners(subscriber, pattern) + notifier = ActiveSupport::Notifications.notifier + notifier.listeners_for(Array.wrap(pattern).first).each do |listener| + if listener.delegates_to?(subscriber) || delegates_to_subscriber_class?(listener, subscriber) + ActiveSupport::Notifications.unsubscribe(listener) + end end end - end + + def delegates_to_subscriber_class?(listener, subscriber) + return false unless listener.respond_to?(:delegate) + + delegate = listener.delegate + subscriber_class = subscriber.class + + # Check if the delegate's class name starts with the subscriber's class name + # This handles cases like ActionView::LogSubscriber::Start delegating for ActionView::LogSubscriber + delegate.class.name.to_s.start_with?(subscriber_class.name.to_s) + end - def blacklisted_subscribers - ActiveSupport::LogSubscriber.log_subscribers.select do |subscriber| - SUBSCRIBER_BLACKLIST.include?(subscriber.class) + def blacklisted_subscribers + ActiveSupport::LogSubscriber.log_subscribers.select do |subscriber| + SUBSCRIBER_BLACKLIST.include?(subscriber.class) + end end end end diff --git a/spec/rails/action_controller_subscriber_spec.rb b/spec/rails/action_controller_subscriber_spec.rb index 577ad47..3f138a0 100644 --- a/spec/rails/action_controller_subscriber_spec.rb +++ b/spec/rails/action_controller_subscriber_spec.rb @@ -20,7 +20,7 @@ def params(values) } expect(Rails.logger[0][0]).to eq('INFO') - expect(Rails.logger[0][3]).to match( + expect(Rails.logger[0][3]).to eq( message: 'GET /empty started', request: { id: nil, @@ -43,7 +43,7 @@ def params(values) expect(Rails.logger[2][4]).to eq([context]) expect(Rails.logger[3][0]).to eq('INFO') - expect(Rails.logger[3][3]).to match( + expect(Rails.logger[3][3]).to eq( message: 'GET /empty > 200 OK', request: { id: nil, @@ -77,7 +77,7 @@ def params(values) it 'removes default Rails params' do get(:index, params(foo: 'bar', password: 'secret')) - expect(Rails.logger[0][3]).to match(hash_including( + expect(Rails.logger[0][3]).to eq(hash_including( message: 'GET /empty started', request: hash_including( path: '/empty', @@ -95,7 +95,7 @@ def params(values) it 'skips start log' do get(:index) - expect(Rails.logger[2][3]).to match(hash_including( + expect(Rails.logger[2][3]).to eq(hash_including( message: 'GET /empty > 200 OK' )) end @@ -107,7 +107,7 @@ def params(values) get(:index) expect(Rails.logger[1][0]).to eq('INFO') - expect(Rails.logger[1][3]).to match( + expect(Rails.logger[1][3]).to eq( message: 'Redirect > https://www.google.com', metrics: { duration: be_between(0, 20) @@ -123,7 +123,7 @@ def params(values) get(:index) expect(Rails.logger[2][0]).to eq('INFO') - expect(Rails.logger[2][3]).to match( + expect(Rails.logger[2][3]).to eq( message: 'Sent data test.txt', metrics: { duration: be_between(0, 20) @@ -139,7 +139,7 @@ def params(values) get(:index) expect(Rails.logger[1][0]).to eq('INFO') - expect(Rails.logger[1][3]).to match( + expect(Rails.logger[1][3]).to eq( message: "Sent file #{filename}", metrics: { duration: be_between(0, 20) @@ -155,7 +155,7 @@ def params(values) get(:index) expect(Rails.logger[2][0]).to eq('INFO') - expect(Rails.logger[2][3]).to match( + expect(Rails.logger[2][3]).to eq( message: 'Filter chain halted as :halt rendered or redirected', metrics: { duration: be_within(1).of(0) @@ -176,14 +176,14 @@ def params(values) l[3][:message].match(/(read|write)_fragment/) end - expect(logs[0][3]).to match( + expect(logs[0][3]).to eq( message: start_with('read_fragment views/'), metrics: { duration: be_between(0, 20) } ) - expect(logs[1][3]).to match( + expect(logs[1][3]).to eq( message: start_with('write_fragment views/'), metrics: { duration: be_between(0, 20) diff --git a/spec/rails/action_view_subscriber_spec.rb b/spec/rails/action_view_subscriber_spec.rb index 5c077db..b15f889 100644 --- a/spec/rails/action_view_subscriber_spec.rb +++ b/spec/rails/action_view_subscriber_spec.rb @@ -16,10 +16,10 @@ def compiled_method_container let(:view) { action_view_base.new(lookup_context, {}, EmptyController.new) } it 'logs rendering a template' do - view.render(template: 'action_view/template.html.erb') + view.render(template: 'action_view/template') expect(Rails.logger[0][0]).to eq('DEBUG') - expect(Rails.logger[0][3]).to match( + expect(Rails.logger[0][3]).to eq( message: 'Rendered template', layout: nil, template: 'action_view/template.html.erb', @@ -30,10 +30,10 @@ def compiled_method_container end it 'logs rendering a partial' do - view.render(template: 'action_view/template_w_partial.html.erb') + view.render(template: 'action_view/template_w_partial') expect(Rails.logger[0][0]).to eq('DEBUG') - expect(Rails.logger[0][3]).to match( + expect(Rails.logger[0][3]).to eq( message: 'Rendered partial', layout: nil, template: 'action_view/_partial.html.erb', @@ -44,10 +44,10 @@ def compiled_method_container end it 'logs rendering a collection' do - view.render(template: 'action_view/template_w_collection.html.erb') + view.render(template: 'action_view/template_w_collection') expect(Rails.logger[0][0]).to eq('DEBUG') - expect(Rails.logger[0][3]).to match( + expect(Rails.logger[0][3]).to eq( message: 'Rendered collection', layout: nil, template: 'action_view/_collection.html.erb', diff --git a/spec/rails/railtie_spec.rb b/spec/rails/railtie_spec.rb index ab4bdb6..37bf73d 100644 --- a/spec/rails/railtie_spec.rb +++ b/spec/rails/railtie_spec.rb @@ -2,6 +2,10 @@ RSpec.describe Epilog::Rails::Railtie do it 'overrides the default Rails logger' do - expect(Rails.logger).to be_instance_of(Epilog::MockLogger) + if Rails.logger.respond_to?(:broadcasts) + expect(Rails.logger.broadcasts).to include(MOCK_LOGGER) + else + expect(Rails.logger).to eq(MOCK_LOGGER) + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3e95d14..c7d325d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,8 @@ Combustion.path = 'spec/rails_app' Combustion.initialize! :all do - config.logger = Epilog::MockLogger.new + MOCK_LOGGER = Epilog::MockLogger.new + config.logger = MOCK_LOGGER config.logger.progname = 'epilog' config.log_level = :debug config.action_controller.perform_caching = true @@ -53,10 +54,11 @@ config.disable_monkey_patching! config.before do - Rails.logger.reset + MOCK_LOGGER.reset end config.after do + MOCK_LOGGER.to_a.each { |l| puts l.inspect } FileUtils.rm_rf(File.join(Rails.root, 'tmp')) end end From 88a5b6e352106496f6871d4706604463a3c2566d Mon Sep 17 00:00:00 2001 From: Wei-Ping Liao Date: Fri, 6 Feb 2026 11:31:30 -0800 Subject: [PATCH 2/6] Update dev setup --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .rubocop.yml | 2 +- Gemfile | 21 +++++++-------------- epilog.gemspec | 8 ++++---- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2675e50..4c480c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,20 +13,20 @@ jobs: fail-fast: false matrix: include: - - ruby: '2.3' - rails: '5.2' - bundler: '1' - - ruby: '2.4' - rails: '5.2' - bundler: '1' - - ruby: '2.5' - rails: '6.0' + - ruby: '3.3' # v1.x sqlite3 requires Ruby 3.3 + rails: '7.0' bundler: 'default' - - ruby: '2.6' - rails: '6.0' + - ruby: '3.4' + rails: '7.1' bundler: 'default' - - ruby: '2.7' - rails: '6.1' + - ruby: '3.4' + rails: '7.2' + bundler: 'default' + - ruby: '3.4' + rails: '8.0' + bundler: 'default' + - ruby: '3.4' + rails: '8.1' bundler: 'default' env: RAILS_VERSION: ${{ matrix.rails }} @@ -39,14 +39,14 @@ jobs: bundler: ${{ matrix.bundler }} bundler-cache: true - run: bundle exec rubocop - if: matrix.ruby == '2.7' + if: matrix.rails == '7.1' - run: bundle exec rspec --format doc - uses: codecov/codecov-action@v3 - if: matrix.ruby == '2.7' + if: matrix.rails == '7.1' with: files: coverage/coverage.xml - run: bin/yardoc --fail-on-warning - if: matrix.ruby == '2.7' + if: matrix.rails == '7.1' - run: bin/check-version release: diff --git a/.rubocop.yml b/.rubocop.yml index 7756c6e..5e3424a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,7 +4,7 @@ require: AllCops: SuggestExtensions: false - TargetRubyVersion: 2.3 + TargetRubyVersion: 3.4 Gemspec/DeprecatedAttributeAssignment: { Enabled: true } Gemspec/RequireMFA: { Enabled: true } diff --git a/Gemfile b/Gemfile index 7c890b9..626f465 100644 --- a/Gemfile +++ b/Gemfile @@ -4,23 +4,16 @@ source 'https://rubygems.org' gemspec not_jruby = %i[ruby mingw x64_mingw].freeze -ruby_version = Gem::Version.new(RUBY_VERSION) -rails_version = Gem::Version.new(ENV.fetch('RAILS_VERSION', '5.2')) +rails_version = Gem::Version.new(ENV.fetch('RAILS_VERSION', '7.1.0')) gem 'byebug', platforms: not_jruby gem 'rails', "~> #{rails_version}" gem 'redcarpet', '~> 3.5', platforms: not_jruby +gem 'rubocop', '~> 1.36.0' +gem 'rubocop-rspec', '~> 2.12.0' +gem 'simplecov', '>= 0.17.1' +gem 'simplecov-cobertura', '~> 2.1' gem 'yard', '~> 0.9.25', platforms: not_jruby -if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6') - gem 'rubocop', '~> 1.36.0' - gem 'rubocop-rspec', '~> 2.12.0' - gem 'simplecov', '>= 0.17.1' - gem 'simplecov-cobertura', '~> 2.1' -end - -if ruby_version >= Gem::Version.new('2.6') - gem 'sqlite3', '~> 1.5' -else - gem 'sqlite3', '~> 1.4', '< 1.5' -end +# Rails 7.0's ActiveRecord adapter requires sqlite3 ~> 1.4; 7.1+ allows 2.x +gem 'sqlite3', rails_version < Gem::Version.new('7.1') ? '~> 1.4' : '>= 1.4' diff --git a/epilog.gemspec b/epilog.gemspec index 0fa5039..ceee785 100644 --- a/epilog.gemspec +++ b/epilog.gemspec @@ -24,11 +24,11 @@ Gem::Specification.new do |spec| spec.files = Dir['lib/**/*.rb', '*.md', '*.txt', '.yardopts'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 2.3' + spec.required_ruby_version = '>= 3.3' spec.add_development_dependency 'combustion', '~> 1.3' - spec.add_development_dependency 'rails', '>= 4.2', '< 8' + spec.add_development_dependency 'rails', '>= 4.2' spec.add_development_dependency 'rspec', '~> 3.11' - spec.add_development_dependency 'rspec-rails', '>= 5.1' - spec.add_development_dependency 'sqlite3', '~> 1.3' + spec.add_development_dependency 'rspec-rails', '>= 7.0' + spec.add_development_dependency 'sqlite3', '>= 1.4' end From f74432fa6aaead8e9d9b36393b5b12d8c2ad1d1c Mon Sep 17 00:00:00 2001 From: Wei-Ping Liao Date: Fri, 6 Feb 2026 11:31:46 -0800 Subject: [PATCH 3/6] Add support for Rails 7.1+ --- lib/epilog/rails/active_record_subscriber.rb | 2 +- lib/epilog/rails/ext/event_delegate.rb | 11 ++++--- lib/epilog/rails/railtie.rb | 33 +++++++++++-------- .../action_controller_subscriber_spec.rb | 30 ++++++++--------- spec/rails/action_view_subscriber_spec.rb | 6 ++-- spec/spec_helper.rb | 3 +- 6 files changed, 48 insertions(+), 37 deletions(-) diff --git a/lib/epilog/rails/active_record_subscriber.rb b/lib/epilog/rails/active_record_subscriber.rb index f7c356e..392497e 100644 --- a/lib/epilog/rails/active_record_subscriber.rb +++ b/lib/epilog/rails/active_record_subscriber.rb @@ -6,7 +6,7 @@ class ActiveRecordSubscriber < LogSubscriber IGNORE_PAYLOAD_NAMES = %w[SCHEMA EXPLAIN].freeze def sql(event) - ActiveRecord::LogSubscriber.runtime += event.duration + ActiveRecord::RuntimeRegistry.sql_runtime += event.duration return unless logger.debug? diff --git a/lib/epilog/rails/ext/event_delegate.rb b/lib/epilog/rails/ext/event_delegate.rb index 37c36dc..2a5cef0 100644 --- a/lib/epilog/rails/ext/event_delegate.rb +++ b/lib/epilog/rails/ext/event_delegate.rb @@ -3,10 +3,13 @@ module Epilog module EventDelegateExt # Rails has no public API to determine the delegate for an event - # object. Add this method to allow checking if the delegate matches - # a given object. - def delegates_to?(delegate) - @delegate == delegate + # object. Add these methods to allow checking the delegate. + def delegate + @delegate + end + + def delegates_to?(other) + @delegate == other end end end diff --git a/lib/epilog/rails/railtie.rb b/lib/epilog/rails/railtie.rb index 0480aba..bb8cbb7 100644 --- a/lib/epilog/rails/railtie.rb +++ b/lib/epilog/rails/railtie.rb @@ -54,8 +54,8 @@ def self.rails_6_1_higher? ) end end - - # In Rails 7.1+, some subscribers (like ActionView) are attached late + + # In Rails 7.1+, ActionView::LogSubscriber::Start are attached late # in the initialization process, so we need to disable them after # initialization completes config.after_initialize do @@ -71,26 +71,33 @@ def disable_rails_defaults unsubscribe_listeners(subscriber, pattern) end end + + # Rails 7.1 adds ActionView::LogSubscriber::Start which subscribes + # separately and logs "Rendering..." messages at the start of rendering + # see https://github.com/rails/rails/commit/9c58a54702b038b9acebdb3efa85c26156ff1987#diff-fd389a9f74e2259b56015e3f8d15a5ce33c093045dd4cb354e82d6d81fe9b06aR98-R99 + unsubscribe_action_view_start_listeners end def unsubscribe_listeners(subscriber, pattern) notifier = ActiveSupport::Notifications.notifier notifier.listeners_for(Array.wrap(pattern).first).each do |listener| - if listener.delegates_to?(subscriber) || delegates_to_subscriber_class?(listener, subscriber) + if listener.delegates_to?(subscriber) ActiveSupport::Notifications.unsubscribe(listener) end end end - - def delegates_to_subscriber_class?(listener, subscriber) - return false unless listener.respond_to?(:delegate) - - delegate = listener.delegate - subscriber_class = subscriber.class - - # Check if the delegate's class name starts with the subscriber's class name - # This handles cases like ActionView::LogSubscriber::Start delegating for ActionView::LogSubscriber - delegate.class.name.to_s.start_with?(subscriber_class.name.to_s) + + def unsubscribe_action_view_start_listeners + return unless defined?(ActionView::LogSubscriber::Start) + + notifier = ActiveSupport::Notifications.notifier + %w[render_template.action_view render_layout.action_view].each do |pattern| + notifier.listeners_for(pattern).each do |listener| + if listener.delegate.is_a?(ActionView::LogSubscriber::Start) + ActiveSupport::Notifications.unsubscribe(listener) + end + end + end end def blacklisted_subscribers diff --git a/spec/rails/action_controller_subscriber_spec.rb b/spec/rails/action_controller_subscriber_spec.rb index 3f138a0..7854976 100644 --- a/spec/rails/action_controller_subscriber_spec.rb +++ b/spec/rails/action_controller_subscriber_spec.rb @@ -43,7 +43,7 @@ def params(values) expect(Rails.logger[2][4]).to eq([context]) expect(Rails.logger[3][0]).to eq('INFO') - expect(Rails.logger[3][3]).to eq( + expect(Rails.logger[3][3]).to match( message: 'GET /empty > 200 OK', request: { id: nil, @@ -54,7 +54,7 @@ def params(values) status: 200 }, metrics: { - db_runtime: 0, + db_runtime: 0.0, view_runtime: be_within(10).of(10), request_runtime: be_between(0, 20) } @@ -63,7 +63,7 @@ def params(values) end it 'logs a request with unpermitted parameters' do - get(:index, params(foo: 'bar')) + get(:index, **params(foo: 'bar')) expect(Rails.logger[1][0]).to eq('DEBUG') expect(Rails.logger[1][3]).to match( @@ -75,15 +75,15 @@ def params(values) end it 'removes default Rails params' do - get(:index, params(foo: 'bar', password: 'secret')) + get(:index, **params(foo: 'bar', password: 'secret')) - expect(Rails.logger[0][3]).to eq(hash_including( + expect(Rails.logger[0][3]).to include( message: 'GET /empty started', - request: hash_including( + request: include( path: '/empty', params: { 'foo' => 'bar', 'password' => '[FILTERED]' } ) - )) + ) end context 'with double_request_logs = false' do @@ -95,9 +95,9 @@ def params(values) it 'skips start log' do get(:index) - expect(Rails.logger[2][3]).to eq(hash_including( + expect(Rails.logger[2][3]).to include( message: 'GET /empty > 200 OK' - )) + ) end end end @@ -107,7 +107,7 @@ def params(values) get(:index) expect(Rails.logger[1][0]).to eq('INFO') - expect(Rails.logger[1][3]).to eq( + expect(Rails.logger[1][3]).to match( message: 'Redirect > https://www.google.com', metrics: { duration: be_between(0, 20) @@ -123,7 +123,7 @@ def params(values) get(:index) expect(Rails.logger[2][0]).to eq('INFO') - expect(Rails.logger[2][3]).to eq( + expect(Rails.logger[2][3]).to match( message: 'Sent data test.txt', metrics: { duration: be_between(0, 20) @@ -139,7 +139,7 @@ def params(values) get(:index) expect(Rails.logger[1][0]).to eq('INFO') - expect(Rails.logger[1][3]).to eq( + expect(Rails.logger[1][3]).to match( message: "Sent file #{filename}", metrics: { duration: be_between(0, 20) @@ -155,7 +155,7 @@ def params(values) get(:index) expect(Rails.logger[2][0]).to eq('INFO') - expect(Rails.logger[2][3]).to eq( + expect(Rails.logger[2][3]).to match( message: 'Filter chain halted as :halt rendered or redirected', metrics: { duration: be_within(1).of(0) @@ -176,14 +176,14 @@ def params(values) l[3][:message].match(/(read|write)_fragment/) end - expect(logs[0][3]).to eq( + expect(logs[0][3]).to match( message: start_with('read_fragment views/'), metrics: { duration: be_between(0, 20) } ) - expect(logs[1][3]).to eq( + expect(logs[1][3]).to match( message: start_with('write_fragment views/'), metrics: { duration: be_between(0, 20) diff --git a/spec/rails/action_view_subscriber_spec.rb b/spec/rails/action_view_subscriber_spec.rb index b15f889..6614a14 100644 --- a/spec/rails/action_view_subscriber_spec.rb +++ b/spec/rails/action_view_subscriber_spec.rb @@ -19,7 +19,7 @@ def compiled_method_container view.render(template: 'action_view/template') expect(Rails.logger[0][0]).to eq('DEBUG') - expect(Rails.logger[0][3]).to eq( + expect(Rails.logger[0][3]).to match( message: 'Rendered template', layout: nil, template: 'action_view/template.html.erb', @@ -33,7 +33,7 @@ def compiled_method_container view.render(template: 'action_view/template_w_partial') expect(Rails.logger[0][0]).to eq('DEBUG') - expect(Rails.logger[0][3]).to eq( + expect(Rails.logger[0][3]).to match( message: 'Rendered partial', layout: nil, template: 'action_view/_partial.html.erb', @@ -47,7 +47,7 @@ def compiled_method_container view.render(template: 'action_view/template_w_collection') expect(Rails.logger[0][0]).to eq('DEBUG') - expect(Rails.logger[0][3]).to eq( + expect(Rails.logger[0][3]).to match( message: 'Rendered collection', layout: nil, template: 'action_view/_collection.html.erb', diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c7d325d..a345d5e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,9 +22,10 @@ @orig_stdout = $stdout $stdout = File.open(File::NULL, 'w') +MOCK_LOGGER = Epilog::MockLogger.new + Combustion.path = 'spec/rails_app' Combustion.initialize! :all do - MOCK_LOGGER = Epilog::MockLogger.new config.logger = MOCK_LOGGER config.logger.progname = 'epilog' config.log_level = :debug From 946c5502bfc3249c6909f3aa6532ac427ff8f7ae Mon Sep 17 00:00:00 2001 From: Wei-Ping Liao Date: Fri, 6 Feb 2026 11:38:19 -0800 Subject: [PATCH 4/6] Fix rubocop errors --- .rubocop.yml | 2 +- lib/epilog/filter/blacklist.rb | 2 +- lib/epilog/filter/filter_parameters.rb | 4 ++-- .../rails/action_controller_subscriber.rb | 4 ++-- lib/epilog/rails/action_view_subscriber.rb | 2 +- lib/epilog/rails/active_job_subscriber.rb | 2 +- lib/epilog/rails/ext/action_controller.rb | 20 +++++++++---------- spec/rails/active_record_subscriber_spec.rb | 2 +- spec/rails/railtie_spec.rb | 4 ++-- spec/spec_helper.rb | 11 +++++----- 10 files changed, 26 insertions(+), 27 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 5e3424a..e792a19 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,7 +4,7 @@ require: AllCops: SuggestExtensions: false - TargetRubyVersion: 3.4 + TargetRubyVersion: 3.3 Gemspec/DeprecatedAttributeAssignment: { Enabled: true } Gemspec/RequireMFA: { Enabled: true } diff --git a/lib/epilog/filter/blacklist.rb b/lib/epilog/filter/blacklist.rb index 050a495..6b05209 100644 --- a/lib/epilog/filter/blacklist.rb +++ b/lib/epilog/filter/blacklist.rb @@ -13,7 +13,7 @@ class Blacklist < HashKey attr_reader :blacklist def initialize(blacklist = DEFAULT_BLACKLIST) - @blacklist = blacklist.map { |b| [b.to_s.downcase, nil] }.to_h + @blacklist = blacklist.to_h { |b| [b.to_s.downcase, nil] } super() end diff --git a/lib/epilog/filter/filter_parameters.rb b/lib/epilog/filter/filter_parameters.rb index 92d9802..4651799 100644 --- a/lib/epilog/filter/filter_parameters.rb +++ b/lib/epilog/filter/filter_parameters.rb @@ -8,9 +8,9 @@ class FilterParameters < Blacklist def filter_parameters return @filter_parameters if @filter_parameters - filtered = ::Rails.application.config.filter_parameters.map do |p| + filtered = ::Rails.application.config.filter_parameters.to_h do |p| [p.to_s.downcase, true] - end.to_h + end @filter_parameters = filtered if ::Rails.initialized? filtered diff --git a/lib/epilog/rails/action_controller_subscriber.rb b/lib/epilog/rails/action_controller_subscriber.rb index 41b8b4b..8be946f 100644 --- a/lib/epilog/rails/action_controller_subscriber.rb +++ b/lib/epilog/rails/action_controller_subscriber.rb @@ -88,7 +88,7 @@ def log_end(event) info do { message: response_string(event), - request: request, + request:, response: response_hash(event), metrics: process_metrics(event.payload[:metrics] .merge(request_runtime: event.duration.round(2))) @@ -158,7 +158,7 @@ def normalize_status(event) def basic_message(event, message) { - message: message, + message:, metrics: process_metrics(duration: event.duration) } end diff --git a/lib/epilog/rails/action_view_subscriber.rb b/lib/epilog/rails/action_view_subscriber.rb index f181419..8510143 100644 --- a/lib/epilog/rails/action_view_subscriber.rb +++ b/lib/epilog/rails/action_view_subscriber.rb @@ -19,7 +19,7 @@ def render_collection(event) def hash(event, message) { - message: message, + message:, template: fix_path(event.payload[:identifier]), layout: fix_path(event.payload[:layout]), metrics: { diff --git a/lib/epilog/rails/active_job_subscriber.rb b/lib/epilog/rails/active_job_subscriber.rb index 509ab0a..6caa6d5 100644 --- a/lib/epilog/rails/active_job_subscriber.rb +++ b/lib/epilog/rails/active_job_subscriber.rb @@ -75,7 +75,7 @@ def perform(event) def event_hash(message, event) { - message: message, + message:, job: job_hash(event.payload[:job]), adapter: adapter_name(event.payload[:adapter]) } diff --git a/lib/epilog/rails/ext/action_controller.rb b/lib/epilog/rails/ext/action_controller.rb index 4259e0b..47bc778 100644 --- a/lib/epilog/rails/ext/action_controller.rb +++ b/lib/epilog/rails/ext/action_controller.rb @@ -5,29 +5,27 @@ module ActionControllerExt def process_action(*) epilog_instrument('request_received') epilog_instrument('process_request') do |payload| - begin - super - ensure - payload[:response] = response - payload[:metrics] = epilog_metrics - end + super + ensure + payload[:response] = response + payload[:metrics] = epilog_metrics end end private - def epilog_instrument(name, &block) + def epilog_instrument(name, &) ActiveSupport::Notifications.instrument( "#{name}.action_controller", epilog_payload, - &block + & ) end def epilog_payload { - request: request, - response: response, + request:, + response:, controller: self.class.name, action: action_name, context: epilog_context @@ -37,7 +35,7 @@ def epilog_payload def epilog_metrics { db_runtime: try(:db_runtime), - view_runtime: view_runtime + view_runtime: } end diff --git a/spec/rails/active_record_subscriber_spec.rb b/spec/rails/active_record_subscriber_spec.rb index 955b482..13deca9 100644 --- a/spec/rails/active_record_subscriber_spec.rb +++ b/spec/rails/active_record_subscriber_spec.rb @@ -19,7 +19,7 @@ expect(Rails.logger[0][3]).to match( message: 'User Load', sql: query, - binds: binds, + binds:, metrics: { query_runtime: be_between(0, 20) } diff --git a/spec/rails/railtie_spec.rb b/spec/rails/railtie_spec.rb index 37bf73d..e517f56 100644 --- a/spec/rails/railtie_spec.rb +++ b/spec/rails/railtie_spec.rb @@ -3,9 +3,9 @@ RSpec.describe Epilog::Rails::Railtie do it 'overrides the default Rails logger' do if Rails.logger.respond_to?(:broadcasts) - expect(Rails.logger.broadcasts).to include(MOCK_LOGGER) + expect(Rails.logger.broadcasts.first).to be_instance_of(Epilog::MockLogger) else - expect(Rails.logger).to eq(MOCK_LOGGER) + expect(Rails.logger).to be_instance_of(Epilog::MockLogger) end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a345d5e..8a48db3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,11 +22,9 @@ @orig_stdout = $stdout $stdout = File.open(File::NULL, 'w') -MOCK_LOGGER = Epilog::MockLogger.new - Combustion.path = 'spec/rails_app' Combustion.initialize! :all do - config.logger = MOCK_LOGGER + config.logger = Epilog::MockLogger.new config.logger.progname = 'epilog' config.log_level = :debug config.action_controller.perform_caching = true @@ -55,11 +53,14 @@ config.disable_monkey_patching! config.before do - MOCK_LOGGER.reset + if Rails.logger.respond_to?(:broadcasts) + Rails.logger.broadcasts.first.reset + else + Rails.logger.reset + end end config.after do - MOCK_LOGGER.to_a.each { |l| puts l.inspect } FileUtils.rm_rf(File.join(Rails.root, 'tmp')) end end From bf29194677bde6ccb263c57aef66d488dbc022bd Mon Sep 17 00:00:00 2001 From: Wei-Ping Liao Date: Mon, 9 Feb 2026 11:21:46 -0800 Subject: [PATCH 5/6] Give more leeway to resposne time --- CHANGELOG.md | 7 +++++++ lib/epilog/version.rb | 2 +- spec/rails/action_controller_subscriber_spec.rb | 14 +++++++------- spec/rails/action_mailer_subscriber_spec.rb | 4 ++-- spec/rails/action_view_subscriber_spec.rb | 6 +++--- spec/rails/active_record_subscriber_spec.rb | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c091508..e05dfd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.9.0 + +- Add support for Rails 7.0, 7.1, 7.2, and 8.0 +- Fix deprecation warning for `ActiveRecord::LogSubscriber.runtime` in Rails 7.1+ +- Update development dependencies +- Update gems and CI + ## 0.8.0 - Update license to MIT [#19](https://github.com/nullscreen/epilog/pull/19) diff --git a/lib/epilog/version.rb b/lib/epilog/version.rb index 077648a..8800e9b 100644 --- a/lib/epilog/version.rb +++ b/lib/epilog/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Epilog - VERSION = '0.8.0' + VERSION = '0.9.0' def self.version Gem::Version.new(VERSION) diff --git a/spec/rails/action_controller_subscriber_spec.rb b/spec/rails/action_controller_subscriber_spec.rb index 7854976..f8e0686 100644 --- a/spec/rails/action_controller_subscriber_spec.rb +++ b/spec/rails/action_controller_subscriber_spec.rb @@ -56,7 +56,7 @@ def params(values) metrics: { db_runtime: 0.0, view_runtime: be_within(10).of(10), - request_runtime: be_between(0, 20) + request_runtime: be_between(0, 40) } ) expect(Rails.logger[3][4]).to eq([context]) @@ -69,7 +69,7 @@ def params(values) expect(Rails.logger[1][3]).to match( message: 'Unpermitted parameters: foo', metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -110,7 +110,7 @@ def params(values) expect(Rails.logger[1][3]).to match( message: 'Redirect > https://www.google.com', metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -126,7 +126,7 @@ def params(values) expect(Rails.logger[2][3]).to match( message: 'Sent data test.txt', metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -142,7 +142,7 @@ def params(values) expect(Rails.logger[1][3]).to match( message: "Sent file #{filename}", metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -179,14 +179,14 @@ def params(values) expect(logs[0][3]).to match( message: start_with('read_fragment views/'), metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) expect(logs[1][3]).to match( message: start_with('write_fragment views/'), metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end diff --git a/spec/rails/action_mailer_subscriber_spec.rb b/spec/rails/action_mailer_subscriber_spec.rb index 989aa02..718ae91 100644 --- a/spec/rails/action_mailer_subscriber_spec.rb +++ b/spec/rails/action_mailer_subscriber_spec.rb @@ -22,7 +22,7 @@ recipients: ['user@example.com'], body: include('My mail message!'), metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -39,7 +39,7 @@ message: 'Received mail', body: include('test mail'), metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end diff --git a/spec/rails/action_view_subscriber_spec.rb b/spec/rails/action_view_subscriber_spec.rb index 6614a14..5d1003d 100644 --- a/spec/rails/action_view_subscriber_spec.rb +++ b/spec/rails/action_view_subscriber_spec.rb @@ -24,7 +24,7 @@ def compiled_method_container layout: nil, template: 'action_view/template.html.erb', metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -38,7 +38,7 @@ def compiled_method_container layout: nil, template: 'action_view/_partial.html.erb', metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end @@ -52,7 +52,7 @@ def compiled_method_container layout: nil, template: 'action_view/_collection.html.erb', metrics: { - duration: be_between(0, 20) + duration: be_between(0, 40) } ) end diff --git a/spec/rails/active_record_subscriber_spec.rb b/spec/rails/active_record_subscriber_spec.rb index 13deca9..13b4c34 100644 --- a/spec/rails/active_record_subscriber_spec.rb +++ b/spec/rails/active_record_subscriber_spec.rb @@ -21,7 +21,7 @@ sql: query, binds:, metrics: { - query_runtime: be_between(0, 20) + query_runtime: be_between(0, 40) } ) end From 8728df88fe7166122391e3f894b834b527cf0a0b Mon Sep 17 00:00:00 2001 From: Wei-Ping Liao Date: Mon, 9 Feb 2026 11:43:40 -0800 Subject: [PATCH 6/6] Bump rubocop gem --- Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 626f465..919255b 100644 --- a/Gemfile +++ b/Gemfile @@ -4,12 +4,12 @@ source 'https://rubygems.org' gemspec not_jruby = %i[ruby mingw x64_mingw].freeze -rails_version = Gem::Version.new(ENV.fetch('RAILS_VERSION', '7.1.0')) +rails_version = Gem::Version.new(ENV.fetch('RAILS_VERSION', '7.0.0')) gem 'byebug', platforms: not_jruby gem 'rails', "~> #{rails_version}" gem 'redcarpet', '~> 3.5', platforms: not_jruby -gem 'rubocop', '~> 1.36.0' +gem 'rubocop', '~> 1.46' gem 'rubocop-rspec', '~> 2.12.0' gem 'simplecov', '>= 0.17.1' gem 'simplecov-cobertura', '~> 2.1'