I created a custom logger for my application, called CRON_LOG, just by adding this piece of code to config/environment.rb
CRON_LOG = Logger.new("#{Rails.root}/log/cron.log")
CRON_LOG.level = Logger::INFO
And then, when I want to log something, just do that:
CRON_LOG.info "something"
It works fine, but I'd like to add the current timestamp before each log message. Of course I can just add Time.now to my log message, but I'd like to know if there is a way to add it as default to every log message. How can I do that ?
Thanks.
The easiest way to make a SysLog-formatted logger is to assign a formatter directly:
logger = Logger.new Rails.root.join('path', 'to', 'log')
logger.formatter = Logger::Formatter.new
logger.info 'Hello, world!'
# Writes:
#
# I, [2011-06-02T20:02:34.579501 #15444] INFO -- : Hello, world!
You can redefine the proper method (e.g. adding the following to environment.rb or in an initializer):
class Logger
def format_message(severity, timestamp, progname, msg)
"#{timestamp} (#{$$}) #{msg}\n"
end
end
[Caution: this could disrupt other loggers; see Stephen's answer for a safe solution - jph]
Related
how to log every action of Net::Sftp into a file in ruby on rails for debugging and how can I log verbose::debug to a logfile.
sftp = Net::SFTP.start(config[:host], config[:user], config[:options], verbose::debug)
files_arr = []
sftp.connect!
sftp.dir.foreach(src_dir) do |entry|
file_name = entry.name
begin
if entry.file? && (file_name.end_with?(".xml") || file_name.end_with?(".zip"))
sftp.download!(File.join(src_dir, file_name), File.join(dest_dir, file_name))
files_arr << file_name
end
rescue Exception => e
next
end
end
sftp.channel.eof!
sftp.close_channel unless sftp.closed?
my objective is to log all that happens in this code to a file, how can I do it.
Following on from the comments, it seems that what you want is to define a custom logger that you can then use to log your custom info to its own file. Something like this in the relevant environment.rb should do what you need.
my_logger = Logger.new('log/my_logger.log')
my_logger.level = Logger::DEBUG
Then, you can add my_logger 'MESSAGE' at the relevant points in your code to add entries to your custom logfile.
The method below takes a few minutes to print out the database entry each time I call it in the console. I need to see the printing the rest of the time so shutting it off entirely isn't an option.
This is my unsuccessful attempt to stop the printing in the method directly.
def generate_dataset
old_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
ProductDatum.create(product_names: Products.all.pluck(:name))
ActiveRecord::Base.logger = old_logger
end
You're looking for #silence, which takes a block during which the logger is silenced:
def generate_dataset
ActiveRecord::Base.logger.silence do
ProductDatum.create(product_names: Products.all.pluck(:name))
end
end
When logging custom information, e.g. in a rake-task, I'd like to indent the log lines for better readability. For example:
Seeding database...
Importing xyz.csv...
Skipping row 5 due to invalid value 'Unknown' in column 'year'
Finished importing xyz.csv
Finished seeding database
In my seeds.rb, I use the following for logging:
logger = Logger.new(STDOUT)
logger.info('Seeding database...')
However, the logging of Skipping row 5... takes place in a service, which does not necessarily have to be called from the seeds.rb, but could be called from anywhere. Thus I can not hardcode the correct indentation (which sounds like a bad idea, anyway).
One possibility would be to keep an "indentation counter", which I could increase when starting to import a file and decrease when finishing. I'm unsure though how to access this from anywhere in my application, or if this is the best solution. Any ideas?
Better solution is to provide your service object with a logger object along with data to process. This way it does not have to know anything about your logger preferences.
And use something like:
require 'logger'
class IndentedLogger < Logger
INDENTATION_STR = ' '.freeze
attr_accessor :indentation
def initialize(io, *args, **params)
self.indentation = 0
super
end
def indented
return self.dup.tap{|l| l.indentation += 1 } unless block_given?
self.indentation += 1
yield self
self.indentation -= 1
end
protected
def format_message(severity, datetime, progname, msg)
(#formatter || #default_formatter).call(severity, datetime, progname, "#{INDENTATION_STR*indentation}#{msg}")
end
end
# Example:
logger = IndentedLogger.new(STDOUT)
logger.info "Foo"
logger.indented{
logger.info "Foo"
logger.indented.info "Indented even more"
}
logger.info "Foo"
And for service call - YourService.new(some_data, logger: logger.indented).process_or_whatever
I need to add a custom log level like "verbose" or "traffic" to ruby logger, how to do?
Your own logger just need to overwrite the Logger#format_severity method, something like this :
class MyLogger < Logger
SEVS = %w(DEBUG INFO WARN ERROR FATAL VERBOSE TRAFFIC)
def format_severity(severity)
SEVS[severity] || 'ANY'
end
def verbose(progname = nil, &block)
add(5, nil, progname, &block)
end
end
You can simply add to the Logger class:
require 'logger'
class Logger
def self.custom_level(tag)
SEV_LABEL << tag
idx = SEV_LABEL.size - 1
define_method(tag.downcase.gsub(/\W+/, '_').to_sym) do |progname, &block|
add(idx, nil, progname, &block)
end
end
# now add levels like this:
custom_level 'TRAFFIC'
custom_level 'ANOTHER-TAG'
end
# using it:
log = Logger.new($stdout)
log.traffic('testing')
log.another_tag('another message here.')
Log levels are nothing but integer constants defined in logger.rb:
# Logging severity.
module Severity
DEBUG = 0
INFO = 1
WARN = 2
ERROR = 3
FATAL = 4
UNKNOWN = 5
end
You can log messages with any level you like using Logger#add method:
l.add 6, 'asd'
#=> A, [2010-02-17T16:25:47.763708 #14962] ANY -- : asd
If you start needing a bunch of custom stuff, it may be worth checking out log4r, which is a flexible logging library that lets you do a bunch of interesting/useful stuff easily.
This is an old question, but since it comes up so high on google, I figured it'd be useful to have to correct answer. You can actually use the Logging.init method. Here's how you would add a trace log level
require 'logging'
Logging.init %w(trace debug info warn error fatal)
Logging.logger.root.level = :trace
Logging.logger.root.add_appenders Logging.appenders.stdout
Logging.logger['hello'].trace 'TEST'
This is using the 2.0.0 of the logging gem.
You can create your own logger by overloading the Logger class
I have this code that sends an email based on conditions. If there is an error its supposed to catch the account that wasn't able to be sent in an array like so:
def process_email(delivery_method_name)
begin
Account::Access.new(account).spam! if delivery_method_name == 'mark_as_spam'
AccountMailer.send("#{delivery_method_name}", account).deliver_now
rescue
#first_notification_error << account.id
#second_notification_failure << account.id
#third_notification_failure << account.id
#fourth_notification_error << account.id
#fourth_notification_failure << account.id
return
end
update_reverification_fields
end
So in my test.rb file I want to be able to test that the account.id was caught inside of the #first_notification_error and other containers. It's not exactly clear to me though how to do this though. I read in another post to place this code in development.rb and/or test.rb config.action_mailer.raise_delivery_errors = true but I don't think this is what I'm looking for. Is there a way I can raise the error in my test, perhaps with a stub or something similar?
You need to mock AccountMailer. Put this line before calling the code you're testing:
delivery_method = 'some method on mailer that you want to mock'
allow(AccountMailer).to receive(delivery_method).and_raise("boom")