New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix dev initializer logs not going to stdout #46170
base: main
Are you sure you want to change the base?
Conversation
@@ -98,7 +98,9 @@ def perform | |||
# RAILS_ENV needs to be set before config/application is required. | |||
ENV["RAILS_ENV"] = options[:environment] | |||
|
|||
require_application_and_environment! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this method is still used by other commands like destroy
and help
.
These other commands will not log to stdout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I only included server
and console
is those are the only commands that are broadcasted to STDOUT
right now. However, I definitely agree that users could expect other commands to output to STDOUT
as well. Maybe the answer would be to always broadcast to STDOUT
in development?
That would also have the benefit of simplifying this change as it wouldn't need the log_to_stdout
config at all.
Edit: I remembered that the server
command also uses --log-to-stdout
, it just gets enabled by default in development unless the server is daemonized. I wonder how many people use --no-log-to-stdout
in development because they don't want STDOUT
logs
@@ -72,18 +72,6 @@ def create_tmp_directories | |||
end | |||
end | |||
|
|||
def log_to_stdout | |||
wrapped_app # touch the app so the logger is set up |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not sure where this method is defined and why it's no longer necessary. Any idea? 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question!
wrapped_app
comes from Rack::Server
here: https://github.com/rack/rack/blob/abca7d59c566320f1b60d1f5224beac9d201fa3b/lib/rack/server.rb#L421
Rack calls wrapped_app
on its own here: https://github.com/rack/rack/blob/abca7d59c566320f1b60d1f5224beac9d201fa3b/lib/rack/server.rb#L327
However, if you want to modify the application (like making the logger broadcast), then you have to "touch the app". All this does is load the app a bit earlier than Rack would normally so that things like initializers have a change to run first.
if config.log_to_stdout? && !ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT, STDERR) | ||
console = ActiveSupport::Logger.new(STDOUT) | ||
console.formatter = Rails.logger.formatter | ||
console.level = Rails.logger.level | ||
|
||
Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does seem to belong here with the other setup of the Rails.logger.
Also nice that it removes the duplication.
fe25005
to
0436e98
Compare
Previously, the Rails server and console commands would broadcast logs to STDOUT if the Rails.logger was not already logging to STDOUT/STDERR. However, the broadcast was setup after initialization had finished, so some logs may not have been broadcast. More specifically, anything logged during initialization after the :initialize_logger initializer would not be logged to STDOUT. This is an issue especially because of how common it is for deprecation warnings to be logged during initialization. Very important deprecation warnings such as :warn_if_autoloaded in Rails 6/6.1 would not be logged to STDOUT even though application logs after initialization would. This is very confusing because users most likely assumed that development logs were identical between STDOUT and log/development.log when they were actually not. This commit fixes the issue by broadcasting when the logger is set up, so that STDOUT and log/development.log will both log everything.
0436e98
to
bf9f76b
Compare
Tried your branch after rebasing and it LGTM ! There is a lot of moving parts on the logger configuration and I find a end to end test to be more robust. There is already one that make sure things get logged on STDOUT and in the file, but maybe we could add another one to see if deprecations are correctly output. Feel free to grab this patch if you agree. diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb
index 982b3f4f6e..3ea35a6254 100644
--- a/railties/test/application/server_test.rb
+++ b/railties/test/application/server_test.rb
@@ -66,6 +66,32 @@ def teardown
end
end
+ test "deprecation messages are displayed at boot time when server runs" do
+ skip "PTY unavailable" unless available_pty?
+
+ File.open("#{app_path}/config/boot.rb", "w") do |f|
+ f.puts "ENV['BUNDLE_GEMFILE'] = '#{Bundler.default_gemfile}'"
+ f.puts 'require "bundler/setup"'
+ end
+
+ add_to_config(<<~CODE)
+ initializer :deprecation, after: :initialize_logger do
+ ActiveSupport.deprecator.warn("Something something")
+ end
+ CODE
+
+ primary, replica = PTY.open
+ pid = nil
+
+ Bundler.with_original_env do
+ pid = Process.spawn("bin/rails server", chdir: app_path, in: replica, out: primary, err: replica)
+ assert_output("DEPRECATION WARNING: Something something", primary)
+ assert_output("Listening on", primary)
+ ensure
+ kill(pid) if pid
+ end
+ end
+
private
def kill(pid)
Process.kill("TERM", pid)
|
Motivation / Background
Previously, the Rails server and console commands would broadcast logs to STDOUT if the Rails.logger was not already logging to STDOUT/STDERR. However, the broadcast was setup after initialization had finished, so some logs may not have been broadcast. More specifically, anything logged during initialization after the :initialize_logger initializer would not be logged to STDOUT.
This is an issue especially because of how common it is for deprecation warnings to be logged during initialization. Very important deprecation warnings such as :warn_if_autoloaded in Rails 6/6.1 would not be logged to STDOUT even though application logs after initialization would.
This is very confusing because users most likely assumed that development logs were identical between STDOUT and log/development.log when they were actually not.
Detail
This commit fixes the issue by broadcasting when the logger is set up, so that STDOUT and log/development.log will both log everything.
Additional information
Ref #44800 as this also fixes the STDERR/STDOUT duplicate logs if Rails.logger is configured to output to STDERR
Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]
main
(if not - rebase it).