Dave is here!

Dave

I'm Dave, one of the founders of the Recurse Center.

How to make Unicorn act like `rails server` in development

July 08, 2012

12/15/2013: Added note about --no-default-middleware.

I’ve been doing some work on our servers and I decided to try using Unicorn. To lower the risk of bugs popping up after pushing the site to production, I wanted to drop WEBrick in development and use Unicorn there as well. The information required to do this was scattered around the web, so I’ve collected it all in one place:

The first step is to add Unicorn to your Gemfile and run bundle install:

# Gemfile
# ...
gem 'unicorn'
# ...

You’ll also want a Unicorn config file. You can find many examples online. Ours is pretty simple:

# config/unicorn.rb
if ENV["RAILS_ENV"] == "development"
  worker_processes 1
else
  worker_processes 3
end

timeout 30

To start Unicorn, just run:

$ unicorn -c config/unicorn.rb

Logging

Once Unicorn is running on your dev machine, you’ll find that the logs are quite different. The standard Rails logs are no longer bing printed to STDOUT, and in their place is what looks like Apache access logs. Rails is still logging to log/development.log, but that’s not particularly useful.

Why is this happening? The Rails logger writes its logs to files, not STDOUT. When you start your app with rails server, a piece of middleware called Rails::Rack::LogTailer is loaded that prints the log to the screen after each request. Starting your app with unicorn does no such thing. The simplest way to fix this is to tell Rails to log to STDOUT instead:

# config/environments/development.rb
Hackerschool::Application.configure do
  # ...
  config.logger = Logger.new(STDOUT)
  config.logger.level = Logger.const_get(
    ENV['LOG_LEVEL'] ? ENV['LOG_LEVEL'].upcase : 'DEBUG'
  )
end

Cribbed from http://help.papertrailapp.com/discussions/questions/116-logging-from-heroku-apps-using-unicorn

If you want to use this trick to make Unicorn log to STDOUT in production, you can use the same code in production.rb, but change the default log level from 'DEBUG' to 'INFO' because that’s the standard behavior.

Note: This workaround has been made obselete by the addition of the --no-default-middleware option to unicorn.

This fixes the missing Rails logs, but what about Unicorn’s Apache-like output? This is a specific quirk of Unicorn for the development and deployment environments. In order to ease the transition from the rackup to unicorn commands, Unicorn adds the same default middleware as Rack to those two environments. This includes Rack::CommonLogger, which unsurprisingly logs all requests in the Apache common log format. The way to fix this is to set the RACK_ENV environmental variable to none. However, with RACK_ENV, and thus RAILS_ENV, set to none, Rails will be unable to load the proper configs for the app and databse. The solution is to explicitly set RAILS_ENV to development in addition to setting RACK_ENV. This tells Rails to use the correct environment while avoiding Unicorn’s default middleware stack:

$ RACK_ENV=none RAILS_ENV=development unicorn -c config/unicorn.rb

If you’re using foreman to run your app locally, you can put these into your .env file.