| About Me

Tailing log files with Capistrano 3

When deploying Rails Applications with Capistrano 2 it was common to have tasks to tail log files on production servers so that they could be viewed locally without sshing into the remote machine(s). In this post I'll cover how to do this with Capistrano 3.

In Capistrano 2, our streaming code would look something like this:

namespace :logs do
  desc "tail rails logs"
  task :tail_rails, :roles => :app do
    trap("INT") { puts 'Interupted'; exit 0; }
    run "tail -f #{shared_path}/log/#{rails_env}.log" do |channel, stream, data|
      puts "#{channel[:host]}: #{data}"
      break if stream == :err    
    end
  end
end

In Capistrano 3, it's even simpler:

namespace :logs do
  desc "tail rails logs"
  task :tail_rails do
    on roles(:app) do
      execute "tail -f #{shared_path}/log/#{fetch(:rails_env)}.log"
    end
  end
end

The above task should be added to lib/capistrano/tasks/logs.cap and can be invoked with:

cap production logs:tail_rails

And stopped with ctrl c.

This will tail the rails log files from all hosts defined as having the role :app. Additional tasks can be defined for other logs you wish to tail such as unicorn.

This can be made even more flexible using the following definition:

namespace :logs do
  task :tail, :file do |t, args|
    if args[:file]
      on roles(:app) do
        execute "tail -f #{shared_path}/log/#{args[:file]}.log"
      end
    else
      puts "please specify a logfile e.g: 'rake logs:tail[logfile]"
      puts "will tail 'shared_path/log/logfile.log'"
      puts "remember if you use zsh you'll need to format it as:"
      puts "rake 'logs:tail[logfile]' (single quotes)"
    end
  end
end

This takes advantage of Capistrano tasks just being rake tasks with some extra magic thrown in. We can therefore pass variables into Capistrano task invocations as we would with any rake task.

The above allows us to invoke:

rake logs:tail[production]

to tail rails_app_path/shared/log/production.log or:

rake logs:tail[unicorn]

to tail rails_app_path/shared/log/unicorn.log.

Unfortunately, if you're using ZSH, if you try to pass an argument to a rake task in the above format you'll receive an error similar to:

zsh: no matches found: logs:tail[production]

You can get around this by wrapping the task in quotes so the invocation would instead be:

rake 'logs:tail[production]'

Which works for any rake tasks when invoked using ZSH.