Managing your log files from within your app

The log file can offer valuable information about what’s going on on your app.

If your app is running on a shared server, if it uses a lot of Ajax or if you’re lucky enough to have lots of traffic to your site your log can get pretty huge pretty quickly and take up valuable disc space.

One way around this is to configure Rails to only log messages with severity level ‘warn’ or higher. You can do this by adding the following to config/environments/production.rb

config.log_level = :warn

The default here is :info but you can set any of the following: :debug, :info, :warn, :error, :fatal, :unknown as the default severity level.

The down-side to this method is that potentially useful information is lost.

Here’s a handy way to manage the size of your log files, take backups of your log files and keep track of specific activity all from within your app.

First, create a controller for accessing your log files:

script/generate controller log show

In this controller we need three actions: show, backup and destroy.
show to view the log, backup to download a backup copy of the log when it starts to get too large and destroy to clear the log when it gets too large.

These actions should look something like this:

class LogsController < ApplicationController
  before_filter :login_required # => add some authentication

  LOG_PATH = Rails.root.join("log", "#{RAILS_ENV}.log")

  def show
    @log_content = File.read(LOG_PATH)
    @log_size = File.size?(LOG_PATH).to_f / 1048576 # converts file size to MB
  end

  def backup
    send_file LOG_PATH,
      :type => "text/plain",
      :stream => false,
      :filename => "log_backup_#{Date.today}"
  end

  def destroy
    File.open(LOG_PATH, "w") do |log_file|
      log_file.write "Log file cleared at #{Time.now}\n"
    end
    flash[:notice]  = "log was cleared\n"
    redirect_to log_path
  end
end

Firstly, it’s important to have some authentication in place on these actions to prevent any e-voyeurs from peeping at your log.

LOG_PATH is a constant we can set up to save a little repetition when referring to the log path. This will also ensure the correct log file is accessed whether you’re in development or production mode.

In the show action we’re simply setting two instance variables:
@log_content for the log content we want to show in the view and
@log_size to show the size of the log file in megabytes (1MB = 1,048,575 bytes).

The backup action calls send_file() to send the file to your computer. No redirect or render needs to be called here.

Finally, the destroy action replaces the entire contents of the log file with the message “Log file cleared at (Time file was cleared)” and redirects to the log page.

Next we need to add routes for these actions:

# /config/routes.rb
map.resource :log, :controller => "log", :member => "backup"

To view the log we need to write the view for the show action
app/views/logs/show.html.erb:

<p>Log size: <%= "%0.3f" % @log_size %>MB</p>
<p>
  <%= button_to "clear log", log_path, :method => :delete,
    :confirm => "Are you sure you want to clear the log?" %> |
  <%= link_to "backup log", backup_log_path %>
</p>
<pre>
<%= @log_content %>
</pre>

Here the log size is displayed in MBs to three decimal places, we have a link to “backup log” which should automatically download the log file to your computer and a button to “clear log” which asks for confirmation before clearing the log file.

The content of the log itself should be displayed in pre tags so the white space is preserved. This helps keep it legible.


As an optional extra, we could expand on this by using a specific notation when logging information. Wrapping each message in […][…] tags.

For example:

class User < ActiveRecord::Base
  after_create { |u| logger.info "[info]New user: #{u.name} was created[info]" }
end

After creating a new database entry we can log a message that reads:

“[info]New user: Jim was created[info]”  

You could also add warnings with the same notation:

“[warn]User jim tried to access log file at 13:59 on June 23rd[warn]”

We can then make a few changes to the index action so we can pick out and display these messages from the log content.

class LogsController < ApplicationController
  before_filter :login_required # => add some authentication

  LOG_PATH = Rails.root.join("log", "#{RAILS_ENV}.log"

  def show
    if params[:filter]
      reg_expression = Regexp.new('\[' + params[:filter] + '\](.+)\[' + params[:filter] + '\]', true)
      @log_content = File.read(LOG_PATH).scan(reg_expression).join("\n")
    else
      @log_content = File.read(LOG_PATH)
    end
    @log_size = File.size?(LOG_PATH).to_f / 1048576 #converts file size to MB
  end

  ...

end

And add links to the view:

<p>
  <%= link_to "show all", log_path %> |
  <%= link_to "show info", log_path(:filter => "info") %> |
  <%= link_to "show warnings", log_path(:filter => "warn") %>
</p>

Now, by clicking “show info” or “show warnings” we can see a quick list of all the custom messages we’ve logged.

Written by

Photo of Gavin Morrice
Gavin Morrice

Software engineer based in Scotland

Connect with me