Add custom severity inside a Ruby logger formatter [duplicate] - ruby-on-rails

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

Related

How can we log everything done by Net::Sftp in a file

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.

Indentation in Rails logs

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

Ruby console - can't initialize a class with variables

I am using Ruby console. I start it by using Rails console and then do require 'path to my file' which works fine.
The file looks like this:
module App
module Tools
module Pollers
class Kpi
attr_reader :start_time,:stop_time
def initialize(start_time,stop_time)
#start_time = start_time
#stop_time = stop_time
end
def create_social_audiences
....
So what I do is declare the stop and start time like this in the console
var end_date = new Date(2012, 7, 1);
var start = new Date(2012, 5, 1);
and then I try to call .new on this file and get this error
>> kpi = App::Tools::Pollers::Kpi.new
ArgumentError: wrong number of arguments (0 for 2)
from (irb):7:in `initialize'
from (irb):7:in `new'
from (irb):7
>>
but the weird part is that if I do this command which attempts to pass the variables it works:
>> kpi = App::Tools::Pollers::Kpi.new(start , end_date)
=> #<App::Tools::Pollers::Kpi:0x11489e198 #start_time=nil, #stop_time=nil>
but then when I try to do set the varialbes to anything other than nil I get errors that initialize is a private method:
?> kpi.initialize(start, end_date)
NoMethodError: private method `initialize' called for #<App::Tools::Pollers::Kpi:0x11489e198>
from (irb):15
Any thoughts on what might be happening wrong here?
Thanks!!
You should not call the class initializer after the class has been instantiated (and you cannot from outside of the class itself, given that it has private visibility).
The following does not work because you have an initializer with two parameters, therefore you also must pass those:
kpi = App::Tools::Pollers::Kpi.new
This works because the initializer is called when the class is being constructed (the initialize method is always invoked when you create a new instance using new):
kpi = App::Tools::Pollers::Kpi.new(start , end_date)
Finally, the following does not work (no matter what values start/end_date hold) because, as I explained earlier, the initializer of a class is private in ruby:
kpi.initialize(start, end_date) # Breaks!
If you want to modify these variables after constructing the class, create a method for that:
def initialize(start, end)
set_time(start_end)
end
def set_time(start, end)
#start_time = start
#end_time = end
end

Add current time before log message

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]

find untranslated locales in rails

I'm using rails 2.3.5 with i18n. I's there a way to find all not yet translated locales in all views?
Maybe a after_filter in the application controller, but which code I can use for this job?
thanks
When using the i18n gem (which Rails does), you can specify your own exception handler. Try this code:
# A simple exception handler that behaves like the default exception handler
# but additionally logs missing translations to a given log.
#
module I18n
class << self
def missing_translations_logger
##missing_translations_logger ||= Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
end
def missing_translations_log_handler(exception, locale, key, options)
if MissingTranslationData === exception # use MissingTranslation in Rails 3.x !!!
puts "logging #{exception.message}"
missing_translations_logger.warn(exception.message)
return exception.message
else
raise exception
end
end
end
end
I18n.exception_handler = :missing_translations_log_handler
(put it for example into RAILS_ROOT/config/initializers/i18n.rb)
Now, whenever you try to translate a key for which you have no translation specified, a warning gets printed into RAILS_ROOT/log/missing_translations.log.
Hope this helps!
I couldn't find a simple trick to do this, so I did this. First implement a 'before_filter' in your application_controller.rb
before_filter :set_user_language
# set the language, 'zen' is a special URL parameter that makes localizations the use the 't' method visible
def set_user_language
# turn on 'zen' to see localization by adding 'zen=true' to query string, will stay on until a query with 'zen=false'
session[:zen] = (session[:zen] || params[:zen] == "true") && params[:zen] != "false"
I18n.locale = 'en'
end
The above finds 'zen=true' and 'zen=false' in the query string. Then add this method to your application_helper.rb:
def t(*args)
result = super(*args)
result = "[#{result}]" if session[:zen] && result.is_a?(String)
result
end
With this method 'zen=true' makes the 't' method display localized strings in square brackets []. To turn it off enter a query string with 'zen=false'.

Resources