How to convert log level symbol into constant? - ruby-on-rails

I am creating a new logger and want to configure the log level to equal Rails configuration value.
#logger = Logger.new(STDOUT)
#logger.level = Rails.configuration.log_level
I get an error since level expects integer and log_level is a symbol (:info). How can I convert the :info into something Logger understands (Logger::INFO)?
I tried using:
#logger.level = "Logger::#{Rails.configuration.log_level.to_s.upcase}".constantize
But constantize does not recognize the symbol:
NameError: uninitialized constant Logger::INFO

Just found it:
Logger.const_get(Rails.configuration.log_level.to_s.upcase)

Related

Overriding const_missing returns `NameError uninitialized constant` in non-dev environments

I have the following code in my rails app
# app/models/test_module/text_class.rb
module TestModule
class TestClass
end
end
# app/models/test_module.rb
module TestModule
def self.const_missing(name)
super(delete_end_number(name.to_s).to_sym)
end
def self.delete_end_number(str)
str.gsub(/\d+$/,'')
end
end
When it runs in development it works
>> TestModule::TestClass1
=> TestModule::TestClass
When I run it in production however I get
NameError (uninitialized constant TestModule::TestClass)
If I just copy TestModule::TestClass into the console it works. It seems just to not work with the const_missing method.
I suspect it may have something to do with the autoloading as when I set config.cache_classes and config.eager_load to true in development.rb it happens there too. I can't seem to figure out how to get it to work in cached environments though.
Change from
super(delete_end_number(name.to_s).to_sym)
To
const = delete_end_number(name.to_s).to_sym
ActiveSupport::Dependencies.load_missing_constant(self, const)

How to set log_level for the assets:precompile task?

There is a method Rake::SprocketsTask#log_level= (defined by sprockets-3.7.1/lib/rake/sprocketstask.rb) but how to call it?
Rafael França says:
To change the logger you can define your own precompile tasks and
setting logger=
Rake::SprocketsTask.new do |t|
t.logger = Logger.new("log/assets.log")
end
I tried that (with log_level) and it returns a SprocketsTask instance, but how do I replace the assets:precompile task?
I want to set the log_level of the assets:precompile task to something quieter, like :warn.
some feedback, logger is an attribute of the class SprocketsTask.
require 'logger'
module Rake
# Simple Sprockets compilation Rake task macro.
#
# Rake::SprocketsTask.new do |t|
# t.environment = Sprockets::Environment.new
# t.output = "./public/assets"
# t.assets = %w( application.js application.css )
# end
#
class SprocketsTask < Rake::TaskLib
# Logger to use during rake tasks. Defaults to using stderr.
#
# t.logger = Logger.new($stdout)
#
attr_accessor :logger
I did not find the info in the api, there is some good explanation in the Rails Guide about the Log4r logger used by rails.
This is the explanation of the code you included
Rails makes use of the ActiveSupport::Logger class to write log information. Other loggers, such as Log4r, may also be substituted.
You can specify an alternative logger in config/application.rb or any other environment file, for example:
config.logger = Logger.new(STDOUT)
config.logger = Log4r::Logger.new("Application Log")
That command creates a log file by default under Rails.root/log/
The info you need are about log levels
When something is logged, it's printed into the corresponding log if the log level of the message is equal to or higher than the configured log level. If you want to know the current log level, you can call the Rails.logger.level method.
The available log levels are: :debug, :info, :warn, :error, :fatal, and :unknown, corresponding to the log level numbers from 0 up to 5, respectively. To change the default log level, use
config.log_level = :warn # In any environment initializer, or
Rails.logger.level = 0 # at any time
This is the Log4r::Logger.rb class.
It has attr_reader for :level, which is set with the def initizialize.
module Log4r
# See log4r/logger.rb
class Logger
attr_reader :name, :fullname, :path, :level, :parent
attr_reader :additive, :trace, :outputters
# Logger requires a name. The last 3 parameters are:
#
# level:: Do I have a level? (Otherwise, I'll inherit my parent's)
# additive:: Am I additive?
# trace:: Do I record the execution trace? (slows things a wee bit)
def initialize(_fullname, _level=nil, _additive=true, _trace=false)
# validation
raise ArgumentError, "Logger must have a name", caller if _fullname.nil?
Log4rTools.validate_level(_level) unless _level.nil?
validate_name(_fullname)
# create the logger
#fullname = _fullname
#outputters = []
#additive = _additive
deal_with_inheritance(_level)
LoggerFactory.define_methods(self)
self.trace = _trace
Repository[#fullname] = self
end
You can set the level by doing:
Log4r::Logger.new('Assets Log', :warn)
So I believe t.logger.level = 2 should set the level to :warn
Rake::SprocketsTask.new do |t|
t.logger = Logger.new("log/assets.log")
t.logger.level = 2
end
:debug, :info, :warn, :error, :fatal, and :unknown, corresponding to the log level numbers from 0 up to 5, respectively.
Also this works because, Log4r::Logger has a method def level=(_level) to re-set the level attribute.
# Set the logger level dynamically. Does not affect children.
def level=(_level)
Log4rTools.validate_level(_level)
#level = _level
LoggerFactory.define_methods(self)
Logger.log_internal {"Logger '#{#fullname}' set to #{LNAMES[#level]}"}
#level
end

How do I make rails' logger use ANSI sequences when outputting to screen, but not when logging to a file?

I was annoyed with having to cut way through ANSI sequences in a log on production server (log/production.log), so I added config.colorize_logging = false to config/environments/production.rb. But now when I run a console (bin/rails c), the output is not colorized as well. Why is it so? Is there a way to make logger use ANSI sequences when outputting to screen, and not use them when logging to a file?
UPD What I was able to figure out. When rails app starts, it creates logger to log into a file:
Rails.logger ||= config.logger || begin
path = config.paths["log"].first
unless File.exist? File.dirname path
FileUtils.mkdir_p File.dirname path
end
f = File.open path, 'a'
f.binmode
f.sync = config.autoflush_log # if true make sure every write flushes
logger = ActiveSupport::Logger.new f
logger.formatter = config.log_formatter
logger = ActiveSupport::TaggedLogging.new(logger)
logger
rescue StandardError
logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
logger.level = ActiveSupport::Logger::WARN
logger.warn(
"Rails Error: Unable to access log file. Please ensure that #{path} exists and is writable " +
"(ie, make it writable for user and group: chmod 0664 #{path}). " +
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
)
logger
end
And then attaches to it another logger to output messages to STDOUT:
def log_to_stdout
wrapped_app # touch the app so the logger is set up
console = ActiveSupport::Logger.new($stdout)
console.formatter = Rails.logger.formatter
console.level = Rails.logger.level
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
end
For some reason, breaking with byebug keyword at ActiveSupport::Logger#initialize never succeed when I ran ./bin/rails c.
UPD Okay, the culprit was spring, console (or should I say activerecord) creates its logger here:
console do |app|
require "active_record/railties/console_sandbox" if app.sandbox?
require "active_record/base"
console = ActiveSupport::Logger.new(STDERR)
Rails.logger.extend ActiveSupport::Logger.broadcast console
end
One way to switch colorized_logging on in console would be to set it explicitly to true as follows:
$ bin/rails c
:001 > Rails.application.config.colorize_logging
=> false
:002 > Rails.application.config.colorize_logging = true
=> true
:003 > Rails.application.config.colorize_logging
=> true
There might be a way to set this automatically every time the console is loaded by customizing the console with a .irbrc file

Defining a logger in environment.rb does not work

I am trying to make a custom logger. I read elsewhere that I can define my logger in environment.rb and use it in other files.
I have in environment.rb:
# Load the rails application
require File.expand_path('../application', __FILE__)
my_logger = Logger.new("#{Rails.root}/log/my.log")
my_logger.formatter = Logger::Formatter.new
# Initialize the rails application
MyApp::Application.initialize!
And I call the logger from, for example, a controller:
my_logger.info "got here"
However, I obtain the error:
Undefined local variable or method `my_logger' for
Any clues as to why this could be happening? Thanks!
Open file into writing/appending mode :
Try :
logfile = File.open('#{Rails.root}/log/my.log', 'a')
my_logger = Logger.new(logfile)
my_logger.info 'Hello World!'
You can maintain own log :
my_logger = ActiveSupport::BufferedLogger.new("#{Rails.root}/log/my.log")

NameError: undefined local variable or method `logger'

When I run 'script/server' everything works fine, but when I run my unit tests (rake test:units), I get the error below, and am not sure how to solve this.
Error
NameError: undefined local variable or method `logger' for #<GiveawayEligibleMemberTest:0x10477dff8>
/Users/kamilski81/Sites/pe/vitality_mall/vendor/rails/actionpack/lib/action_controller/test_process.rb:471:in `method_missing'
/Users/kamilski81/Sites/pe/vitality_mall/lib/update_giveaway_eligible_members.rb:17:in `is_valid_checksum?'
/Users/kamilski81/Sites/pe/vitality_mall/test/unit/giveaway_eligible_member_test.rb:26:in `test_that_checksum_is_valid'
/Users/kamilski81/Sites/pe/vitality_mall/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:60:in `__send__'
/Users/kamilski81/Sites/pe/vitality_mall/vendor/rails/activesupport/lib/active_support/testing/setup_and_teardown.rb:60:in `run'
I tried putting:
class Test::Unit::TestCase
RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
RAILS_DEFAULT_LOGGER.level = Logger::WARN
logger = Logger.new(STDOUT)
logger.level = Logger::WARN
end
Here is the code that is using my logger:
def is_valid_checksum?(csv_arr)
expected_row_count = csv_arr[0][3].to_i
logger.debug "Expected record count: #{expected_row_count}"
actual_row_count = csv_arr.nitems - 1
logger.debug "Actual record count: #{actual_row_count}"
checksum_valid = false
if expected_row_count == actual_row_count
logger.debug "Checksum is valid"
checksum_valid = true
end
return checksum_valid
end
But this still does not solve the error
You can use the Rails logger outside of models and controllers:
Rails.logger.info "..."
Source
The logger method isn't available to test cases instance methods.
You have defined a local variable in your class definition but this isn't enough. The logger variable falls out of scope once the class is initialized. You may be looking for a class variable (##logger). But a cleaner solution would be wrapping it in a method like this.
class Test::Unit::TestCase
def logger
RAILS_DEFAULT_LOGGER ||= Logger.new(STDOUT)
end
end
Notice this code will use the default logger if it is available (which it should be). If this isn't desired, you can make your own just as easily.
def logger
#logger ||= Logger.new(STDOUT)
end
you should use RAILS_DEFAULT_LOGGER.debug the constant in your test case is_valid_checksum? not the variable logger(class level variable ) which cannot be used in a instance method.
I would suggest to wrap the code in a instance method something like
def logger
logger = Logger.new(STDOUT)
logger.level = Logger::WARN
logger
end
and then use logger

Resources