This howto shows how to modify some of the defaults in rails’ logging configuration. For background about what the logger is and how to use it, see logger.
You can control some aspects of the logger through the config object in config/environment.rb for a change in all environments
Log Level
The available log levels are: :debug, :info, :warn, :error, :fatal. The ruby Logger class normally also accepts :any, but that doesn’t seem to work with rails.
config.log_level = :debug
Log Path
The log file path is relative to the RAILS_ROOT directory. Make sure that the file exists and has permissions that allow writing for the user that rails will run under.
config.log_path = 'log/debug.log'
If you want log warnings when your application is running in production mode add the following line to the end of your #{RAILS_ROOT}/config/production.rb file:
config.log_level = :warn
However, if you use Ruby standard logger (for example config.logger = Logger.new(File.dirname(__FILE__) + "/../log/#{RAILS_ENV}.log") in config/environment.rb), not Rails’ default one, use this instead:
config.logger.level = Logger::WARN
Since Rails 2.0, something similar is done in "Rails::Initializer#initializelogger":http://github.com/github/rails/tree/c8da518bbfedc2a06b1d96912ddae00e57f21748/railties/lib/initializer.rb#L238-266 and initialize_framework_logging instead of in environment.rb as stated below._
environment.rb defines the loggers used by the components of rails. First it defines the default logger for the application :
begin
RAILS_DEFAULT_LOGGER = Logger.new(“#{RAILS_ROOT}/log/#{RAILS_ENV}.log”)
rescue StandardError
RAILS_DEFAULT_LOGGER = Logger.new(STDERR)
RAILS_DEFAULT_LOGGER.level = Logger::WARN
RAILS_DEFAULT_LOGGER.warn(
“Rails Error: Unable to access log file. Please ensure that log/#{RAILS_ENV}.log exists and is chmod 0666. " +
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed.”
)
end
Then it assigns this logger to its friends :
[ActiveRecord, ActionController, ActionMailer].each { |mod| mod::Base.logger ||= RAILS_DEFAULT_LOGGER }
Say you want Active record to log in database.log instead of the default #{RAILS_ENV}.log file. You can add the following to the end of your #{RAILS_ROOT}/config/#{RAILS_ENV}.rb file (for example to developement.rb):
ActiveRecord::Base.logger = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}_database.log")
The file /railties/lib/commands/servers/mongrel.rb (such as used in RadRails when running Mongrel) includes the following two lines:
require 'initializer' Rails::Initializer.run(:initialize_logger)
This breaks the option to set another logger using config.logger
You can still force RAILS_DEFAULT_LOGGER to whatever logger you want, though that will get you “warning: already initialized constant RAILS_DEFAULT_LOGGER”.
See ticket 8665
Removing the colour escape sequences that are used by default is quite simple. Simply set colorize_logging to false:
ActiveRecord::Base.colorize_logging = false
Note that the colorize_logging method doesn’t appear in ActionController (why?[because the only module in rails that adds color to the logs is ActiveRecord -jean]).
As said in the logger.rb source file:
> There is currently no supported way to change the overall format,
> but you may have some luck hacking the Format constant.
This, when added to the end of environment.rb, puts the timestamp and PID in the log.
class Logger
def format_message(severity, timestamp, progname, msg)
"#{timestamp} (#{$$}) #{msg}\n"
end
end
(Note that the Rails 2.0 default logger has been changed from Ruby’s own logger with the clean_logger extensions described below to ActiveSupport::BufferedLogger. From the CHANGELOG: “You can change it back with config.logger = Logger.new(”/path/to/log", level)". Rails::Initializer.initialize_logger() documentation also indicates you can set RAILS_DEFAULT_LOGGER.)
Look at the source to what Rails 1.2 does to mung the standard Logger object to do what it wants. Can you see anything wrong with this picture? If Rails wants a Logger object that behaves this way, it should subclass Logger, or inject its own Logger::Formatter into the picture rather than this mess.
What this accomplishes is utterly evil. If you’re adding things to a rails app that expect Logger to behave as documented, you’ll configure your logging date formats all day long and never have any impact. Worse, the code is flagged :nodoc:, so after screwing around with expected behavior, we’re left with no indication as to why a Logger instance created from the Rails console will behave different than a Logger created from IRB.
Mixins are a great feature of Ruby, but a mixin that irrevocably alters a core library class just to avoid the minor pain of creating a proper subclass where it it obviously necessary is just plain wrong, no matter how $!#^$% pragmatic you’re claiming to be.
This code added to environment.rb will restore the default logger behavior under 1.8.4, Rails 1.2:
class Logger
def format_message(severity, timestamp, progname, msg)
if <a href="mailto:!@formatter.nil">!@formatter.nil</a>?
@formatter.call(severity, timestamp, progname, msg)
else
@default_formatter.call(severity, timestamp, progname, msg)
end
end
end
class RailsFormatter < Logger::Formatter
def call(severity, timestamp, progname, msg)
"#{msg}\n"
end
end
RAILS_DEFAULT_LOGGER.formatter = RailsFormatter.new
In the old days, when you needed something similar to log4j , then you would be told to take a look at the log4r project It allows more advanced logging than the standard logger. However, log4r doesn’t work with the current version of Rails because of incompatibilities between the benchmarking module and log4r. See ticket 3512 for more information.
On 5/31/08, log4r has been idle since March 2004. It looks like no-one has been able to contact the original developer for some time, and a replacement project, http://logging.rubyforge.org has been created that solves the above mentioned problem.
For example, in config/environment.rb:
require 'logging'
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
# Logging.init is required to avoid
# unknown level was given 'info' (ArgumentError)
# or
# uninitialized constant Logging::MAX_LEVEL_LENGTH (NameError)
# when an Appender or Layout is created BEFORE any Logger is instantiated:
Logging.init :debug, :info, :warn, :error, :fatal
layout = Logging::Layouts::Pattern.new :pattern => "[%d] [%-5l] %m\n"
# Default logfile, history kept for 10 days
default_appender = Logging::Appenders::RollingFile.new 'default', \
:filename => ...., :age => 'daily', :keep => 10, :safe => true, :layout => layout
# Audit logfile, history kept forever
audit_appender = Logging::Appenders::RollingFile.new 'audit', \
:filename => ...., :age => 'daily', :safe => true, :layout => layout
DEFAULT_LOGGER = returning Logging::Logger['server'] do |l|
l.add_appenders default_appender
end
# A logger logging to two files, the audit trail being kept forever
AUDIT_LOGGER = returning Logging::Logger['audit'] do |l|
l.add_appenders [audit_appender, default_appender]
end
Rails::Initializer.run do |config|
:
# When using "script/server mongrel", use
# RAILS_DEFAULT_LOGGER = DEFAULT_LOGGER
# instead
config.logger = DEFAULT_LOGGER
:
end
and in the per-environment config, like environments/development.rb:
DEFAULT_LOGGER.level = :debug
AUDIT_LOGGER.level = :debug
Logging::Appender::Email (in June 2008) does not automatically flush after some inactivity. So, to get important alerts right away, be sure to either set :immediate_at => 'warn,error,fatal' or use :buffsize => 1. Also note that the values for :from and :to should be a plain email addresses, not something like Me myself <me at example.org>.
email_layout = Logging::Layouts::Pattern.new :pattern => '[%d] [%-5l] [%c] %m\n'
email_appender = Logging::Appenders::Email.new 'email',
:from => 'webmaster at example.org',
:to => 'webmaster at example.org',
:server => 'smtp.example.org',
:domain => 'example.org',
:level => :warn,
:buffsize => 1, # so we don't need to flush
# or:
# :immediate_at => 'warn,error,fatal',
:subject => "Alert from rails.example.org (#{RAILS_ENV})",
:layout => email_layout
DEFAULT_LOGGER = returning Logging::Logger['server'] do |l|
l.add_appenders [default_appender, email_appender]
end
and, for example, at the end of environment.rb:
if $0 !~ /rake/
DEFAULT_LOGGER.warn "My Application started"
at_exit do
DEFAULT_LOGGER.warn "My Application stopped"
end
end
When all you need is log rotation, then instead of using “Logging” you can also have the operating system execute the file rollovers. See DeploymentTips.
I ran into problems with logging on Apache 2 with Fcgi resulting in a “could not access {RAILS_ENV}/log/development.log” message in the Apache Error log and suggesting I made sure the log file existed and was chmodded 0666. Changine permissions on the files had no effect but setting a 777 permission on the /log directory fixed it.
Thank you for these useful informations. However I’d like to know if it’s possible to display the logs in the console rather than using the “debug.log” file .
The ActiveRecord #format_log_entry method looks like this:
def format_log_entry(message, dump = nil)
if ActiveRecord::Base.colorize_logging
if@row_even @row_even = false
message_color, dump_color = “4;36;1”, “0;1”
else
@@row_even = true
message_color, dump_color = “4;35;1”, “0”
end log_entry = " \e[#{message_color}m#{message}\e[0m " log_entry << “\e[#{dump_color}m%#{String === dump ? ‘s’ : ’p’}\e[0m” % dump if dump log_entry else “%s %s” % [message, dump] end end
it’s actually found in activerecord/connection_adapters/abstract_adapter.rb_
Question: I am running Rails 1.2.3 with Mongrel and Ruby 1.8.6 on Windows. No matter what I set the value of log_level in environment.rb or environments/development.rb, my logfile always contains the same output. How do I begin to track down why its doing so?
Answer: config.log_level = :info will not work if you are setting config.logger. In that case, you will want to set config.logger.log_level = (Logger::INFO or whatever). I have also seen cases where ./script/server will setup one logger and RAILS_DEFAULT_LOGGER will not get reset. You can assert this by keeping a global reference to the logger you setup in environment.rb, and later on doing a `throw “Lame” unless $my_logger == RAILS_DEFAULT_LOGGER`
Pretty logs for rails 1.2.x and 2.0
I use for creating pretty logs on rails
next patches
config/environment.rb
require ‘logger_core_ext’
file app/lib/logger_core_ext.rb
class Logger
# Used for rails 1.2.x
alias format_message old_format_message
class Formatter
def call(severity, time, progname, msg)
“s|#5d|%s] %s: %s\n” % [severity[0..3], $$, time.to_s(:db), progname, msg2str(msg)]
end
end
endmodule ActiveSupport
# Used for rails 2.0
class BufferedLogger
module Severity
def level_to_s(level)
case level
when 0
‘DEBUG’
when 1
‘INFO’
when 2
‘WARN’
when 3
‘ERROR’
when 4
‘FATAL’
when 5
‘UNKNOWN’
end
end
end
def add(severity, message = nil, progname = nil, &block)
return if @level > severity
# message = (message || (block && block.call) || progname).to_s
message = (message || (block && block.call) || progname).to_s
# If a newline is necessary then create a new message ending with a newline.
# Ensures that the original message is not mutated.
message = “#{message}\n” unless message[-1] == ?\n
message = “s|#5d|%s] %s: %s” % [level_to_s(severity)[0..3], $$, Time.now.to_s(:db), progname, message]
@buffer << message
auto_flush
message
end
end
end