113

In a rake task if I use puts command then I see the output on console. However I will not see that message in log file when app is deployed on production.

However if I say Rails.logger.info then in development mode I see nothing on console. I need to go to log file and tail that.

I would ideally like to use Rails.logger.info and in development mode inside the rake task, the output from logger should also be sent to console.

Is there a way to achieve that?

Jonathan Julian
  • 12,163
  • 2
  • 42
  • 48
Nick Vanderbilt
  • 36,724
  • 29
  • 83
  • 106

9 Answers9

61

Put this in application.rb, or in a rake task initialize code

if defined?(Rails) && (Rails.env == 'development')
  Rails.logger = Logger.new(STDOUT)
end

This is Rails 3 code. Note that this will override logging to development.log. If you want both STDOUT and development.log you'll need a wrapper function.

If you'd like this behaviour only in the Rails console, place the same block of code in your ~/.irbrc.

shmichael
  • 2,985
  • 3
  • 25
  • 32
44

You could create a new rake task to get this to work.

desc "switch logger to stdout"
task :to_stdout => [:environment] do
 Rails.logger = Logger.new(STDOUT)
end

This way when you execute your rake task you can add to_stdout first to get stdout log messages or don't include it to have messages sent to the default log file

rake to_stdout some_task
Pete Brumm
  • 1,656
  • 19
  • 13
14

Code

For Rails 4 and newer, you can use Logger broadcast.

If you want to get both STDOUT and file logging for rake tasks in development mode, you can add this code into config/environments/development.rb :

  if File.basename($0) == 'rake'
    # http://stackoverflow.com/questions/2246141/puts-vs-logger-in-rails-rake-tasks
    log_file     = Rails.root.join("log", "#{Rails.env}.log")
    Rails.logger = ActiveSupport::Logger.new(log_file)
    Rails.logger.extend(ActiveSupport::Logger.broadcast(ActiveSupport::Logger.new(STDOUT)))
  end

Test

Here's a small Rake task to test the above code :

# lib/tasks/stdout_and_log.rake
namespace :stdout_and_log do
  desc "Test if Rails.logger outputs to STDOUT and log file"
  task :test => :environment do
    puts "HELLO FROM PUTS"
    Rails.logger.info "HELLO FROM LOGGER"
  end
end

Running rake stdout_and_log:test outputs

HELLO FROM PUTS
HELLO FROM LOGGER

while

HELLO FROM LOGGER

has been added to log/development.log.

Running rake stdout_and_log:test RAILS_ENV=production outputs

HELLO FROM PUTS

while

HELLO FROM LOGGER

has been added to log/production.log.

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • 1
    In Rails 5, the `basename($0) == 'rake'` trick no longer works, because the `rails` command itself runs `rake`. I'd love to find a good replacement for it beyond depending on a task that sets up the `broadcast`. (That part, at least, still works fine.) – Becca Royal-Gordon Jan 06 '17 at 00:47
  • 1
    @BrentRoyal-Gordon There's no need for this conditional in development, you can just add the code to the `Rakefile` at the root of your project – Mauricio Pasquier Juan Oct 28 '18 at 04:54
13

Rake tasks are run by a user, on a command-line. Anything they need to know right away ("processed 5 rows") should be output on the terminal with puts.

Anything that needs to be kept for posterity ("sent warning email to jsmith@example.com") should be sent to the Rails.logger.

Jonathan Julian
  • 12,163
  • 2
  • 42
  • 48
  • 22
    It's not uncommon to run rake tasks with cron. – Johannes Gorset Mar 15 '13 at 14:14
  • 2
    True. If you want the log messages to be emailed to you when the cron job completes, spit them out to $stdout or $stderr. – Jonathan Julian Mar 15 '13 at 18:00
  • @JohannesGorset enven given that that is true, it is also true that it is not uncommon to run other programs meant to be run by a user on a comand-line with cron. And those programs normally interact with the standard output, error. So logging that should be a problem of the Admin who maintain the cronjob, not of the program itself. So I'm finding this answer enlightening, and makes a lot of sense for me now. – jgomo3 Sep 03 '20 at 13:48
10

I'd say that using Rails.logger.info is the way to go.

You won't be able to see it in the server console because it won't run via the server. Just open up a new console and tail -f the log file, it'll do the trick.

Many users are aware of the UNIX® command 'tail', which can be used to display the last few lines of a large file. This can be useful for viewing log files, etc.

Even more useful in some situations, is the '-f' parameter to the 'tail' command. This causes tail to 'follow' the output of the file. Initially, the response will be the same as for 'tail' on its own - the last few lines of the file will be displayed. However, the command does not return to the prompt, and instead, continues to 'follow' the file. When additional lines are added to the file, they will be displayed on the terminal. This is very useful for watching log files, or any other file which may be appended over time. Type 'man tail' for more details on this and other tail options.

(via)

marcgg
  • 65,020
  • 52
  • 178
  • 231
  • But it's not very comfortable while working on the local machine as you don't see the output in the same window where you called the task. Especially if you don't have a big screen to fit multiple windows side by side. – Tomas Markauskas Feb 11 '10 at 17:18
  • 11
    Even better is to use `tailf` "It is similar to tail -f but does not access the file when it is not growing" (from manpage). It is shorter too – MBO Feb 11 '10 at 17:18
  • @tomas why not minimizing the server log console and only have the one console with the tail-f running ? Anyways it's not a real problem... I am running like 8 consoles tracing what's going on in my app, just switch between tabs when you're working on a specific part of the system no big deal imho – marcgg Feb 11 '10 at 17:19
  • @mbo nice :) halas it doesn't look like it's available on my machine (mac os@leopard) – marcgg Feb 11 '10 at 17:20
  • 1
    @marcgg: I don't have a "server console" (I use passenger), but the question is about rake tasks and if you run a task from one terminal window, you don't see anything from the logger in that window. You have to have another window with the development.log to see the output. Ideally I would somehow add stdout as another output stream to Rails.logger, but not remove the original one. – Tomas Markauskas Feb 11 '10 at 17:24
  • @tomas you could try building your own logger: http://ruby-doc.org/core/classes/Logger.html – marcgg Feb 11 '10 at 17:26
  • I can build my own logger. But the goal was to messages from rake go to both rails default logger and to the console. I was wondering if I was missing something. It seems there it is not trivial to also redirect logger to do puts. – Nick Vanderbilt Feb 11 '10 at 18:43
3

In Rails 2.X to redirect the logger to STDOUT in models:

ActiveRecord::Base.logger = Logger.new(STDOUT)

To redirect logger in controllers:

ActionController::Base.logger = Logger.new(STDOUT)
Tania R
  • 499
  • 4
  • 11
  • I also found this article, very useful http://www.sepcot.com/blog/2009/08/rails-logging-to-stdout – Tania R Nov 22 '12 at 09:22
3

Execute a background job with '&' and open script/console or whatever.. That way you can run multiple commands in the same window.

tail -f log/development.log &
script/console
Loading development environment (Rails 2.3.5)
>> Product.all
2011-03-10 11:56:00 18062 DEBUG  Product Load (6.0ms)  SELECT * FROM "products"
[<Product.1>,<Product.2>]

note Can get sloppy quickly when there is a lot of logging output.

cweston
  • 11,297
  • 19
  • 82
  • 107
Tom Maeckelberghe
  • 1,969
  • 3
  • 21
  • 24
2

How about creating an application helper which detects which environment is running and does the right thing?

def output_debug(info)
   if RAILS_ENV == "development"
      puts info
   else
      logger.info info
   end
end

Then call output_debug instead of puts or logger.info

naven87
  • 964
  • 1
  • 7
  • 24
  • 6
    I don’t think this is a good idea. The Rails logger is meant to be configurable, but instead of using that configurability you’re just piling more layers on top of it here. – Sijmen Mulder Nov 05 '12 at 16:59
  • Yes, it's not a good idea since the same check will be done *everytime* you want to log something. – furiabhavesh Dec 05 '16 at 18:53
0

Extending on @Eric Duminil's answer. We can use use rake_tasks hook provided by rails to run code block only when rake tasks are invoked, i.e. this block will not run when server or console are run.

# config/application.rb
module MyApp
  class Application < Rails::Application

    rake_tasks do
      config.after_initialize do
        Rails.logger.extend(
          ActiveSupport::Logger.broadcast(ActiveSupport::Logger.new(STDOUT))
        )
      end
    end
  end
end

Output:

cat lib/tasks/logger_test.rake

namespace :stdout_and_log do
  desc "Test if Rails.logger outputs to STDOUT and log file"
  task test: :environment do
    puts "HELLO FROM PUTS"
    Rails.logger.info "HELLO FROM LOGGER"
  end
end

❱ bundle exec rails stdout_and_log:test
HELLO FROM PUTS
HELLO FROM LOGGER

the_spectator
  • 1,345
  • 11
  • 26