How to initialize a logger in rails 3? - ruby-on-rails

I have read the documentation on this guide and the class. I wish to create a logger which take logging informations each day and after let's say a week, delete the oldest logging information automatically each time.
logfile = File.open(RAILS_ROOT + '/log/'+ (Date.today << 1).to_s + '_custom.log', 'a') #create log file
logfile.sync = true #automatically flushes data to file
CUSTOM_LOGGER = CustomLogger.new(logfile, 'daily') #constant accessible anywhere
Plus, I wish to create a custom logging, so for instance something that looks like this (format):
class MyLogger < Logger
def format_message(severity, timestamp, progname, msg)
"#{timestamp} : #{msg}\n"
end
end
So basically, I would like to have a better idea where to place everything correctly under which directory. For instance, where MyLogger should be logically placed... (anywhere? A helper? or under app/config/ ?
Is that a valid way to implement this?
I made it by putting everything in config/initializers and creating a file named my_logger.rb. I'm still stuck at deleting/managing log files.
Does the server handle that part with a log rotation ( I know there's something with logrotation from the linux OS)? Or Rails can handle that internally?

Where should MyLogger be logically placed?
Probably put it under /lib. You can then require it from the initializer where you set the custom logger.
How can you periodically delete the oldest logging information?
There are a countless ways you can do this and choosing will be based on your constraints. You haven't spoken much about your constraints, so it's going to be hard to give you the just-right answer. E.g. you could clean up old logs every time you add a new log entry, you could run a cron job, you could install some non-Rails software that does log rotation and other log maintenance, you could use Papertrail, if you use Heroku you could look up https://devcenter.heroku.com/articles/scheduled-jobs-custom-clock-processes.
Remember Rails is designed more to handle requests and respond to them in the context of that request, than to run maintenance outside of the context of receiving a request. You could do maintenance as a side-effect of every format_message request to MyLogger, checking for the oldest logging entry and if you find one older than a week, delete them. You haven't given a constraint why you can't do this in-process, and if you're prototyping something early and portable, then this would get you going fast.

Related

How to disable class cache for part of Rails application

I am developing a Rails app for network automation. Part of app consists logic to run operations, part are operations themselves. Operation is simply a ruby class that performs several commands for network device (router, switch etc).
Right now, operation is simply part of Rails app repo. But in order to make development process more agile, I would like to decouple app and operations. I would have 2 repos - one for app and one for operations. App deploy would follow standard procedure, but operation would sync every time something is pushed to master. And what is more important, I don't want to restart app after operations repo update.
So my question is:
How to exclude several classes (or namespaces) from being cashed in production Rails app - I mean every time I call this class it would be reread file from disk. What could be potential dangers of doing so?
Some code example:
# Example operation - I would like to add or modify such classes withou
class FooOperation < BaseOperation
def perform(host)
conn = new_connection(host) # method from BaseOperation
result = conn.execute("foo")
if result =~ /Error/
# retry, its known bug in device foo
conn.execute("foo")
else
conn.exit
return success # method from BaseOperation
end
end
end
# somewhere in admin panel I would do so:
o = Operations.create(name: "Foo", class_name: "Foo")
o.id # => 123 # for next example
# Ruby worker which actually runs an operation
class OperationWorker
def perform(operation_id, host)
operation = Operation.find(operation_id)
# here, everytime I load this I want ruby to search for implementation on filesystem, never cache
klass = operation.class_name.constantize
class.new(host).perform #
end
end
i think you have quite a misunderstanding about how ruby code loading and interpretation works!
the fact that rails reloads classes at development time is kind of a "hack" to let you iterate on the code while the server has already loaded, parsed and executed parts of your application.
in order to do so, it has to implement quite some magic to unload your code and reload parts of it on change.
so if you want to have up-to-date code when executing an "operation" you are probably best of by spawning a new process. this will guarantee that your new code is read and parsed properly when executed with a blank state.
another thing you can do is use load instead of require because it will actually re-read the source on subsequent requests. you have to keep in mind, that subsequent calls to load just add to the already existing code in the ruby VM. so you need to make sure that every change is compatible with the already loaded code.
this could be circumvented by some clever instance_eval tricks, but i'm not sure that is what you want...

Access the model in production.rb rails 3

I have a model called SystemSettings with a name on and a value. It is where I store the majority of my configuration for my app. I need to be able to access it in my production.rb inside my rails 3.2 app. How would you go about doing this?
Since the Rails config such as production.rbis read before ActiveRecord is initialised you would need to use a callback:
Rails.application.configure do
ActiveSupport.on_load(:active_record) do
config.custom_variable = SystemSettings.find_by(name: "Foo").value
end
end
But since the callback executes later when ActiveRecord is ready you can't immediately use its value which is why your approach may be flawed due to race conditions.
Unless you are building something like a CMS where you need to provide a user interface to edit system settings you will be better off using environmental variables. They are immediately available from memory and do not have the overhead of a database query.
http://guides.rubyonrails.org/v3.2.9/initialization.html

Rails - delayed_job application variables (not discussed much)

I have a question about delayed_job in Rails that doesn't seem to be mentioned much.
When you run a delayed job, it doesn't seem to load anything from ApplicationController. We have some code in ApplicationController to use a custom logger:
def setup_logger
logfile = File.open("#{RAILS_ROOT}/log/audit.log", 'a')
#audit_log = Logger.new(logfile)
$audit_log = #audit_log
end
We then reference $audit_log all through our code. But because DelayedJob doesn't load the ApplicationController this variable is nil and we get errors.
So Delayed_job is just running the specified method blindly, which could also be dangerous
if you rely on before_filters for checking data or validating things.
How can we fix our problem of getting DelayedJob to know about our global logging variable? We don't want to explicitly define the logger all through our code.
How else are people dealing with this problem, as it seems like it should be common, but its not talked about much.
Thanks
Why you think, a Job must run ApplicationController ?
A Job, precisely the worker running that job, loads the environment, sure, but not a controller...
If you don't want have it in a initializer, why not using a kind of config object for storing such data and reference to it in the job ?
And instead of do it in each job individually, I would suggest set up the logger in the BaseJob, and use it in the inherited jobs for what you need.
And another suggestion, please if you can, don't use delayed job, please use http://mperham.github.io/sidekiq/ or at least https://github.com/resque/resque

Creating only one log every day using Ruby standard Logger

I'm using ruby standard logger, I want rotational daily one, so in my code I have :
Logger.new("#{$ROOT_PATH}/log/errors.log", 'daily')
It is working perfectly, but it created two files errors.log.20130217 and errors.log.20130217.1.
How can I force it to create just one file a day ?
Your code is correct for a long-running application.
What's happening is you're running code more than once on a given day.
The first time you run it, Ruby creates a log file "errors.log".
When the day changes, Ruby renames the file to "errors.log.20130217".
But somehow you ran the code again, perhaps you're running two apps (or processes, or workers, or threads) that use similar code, and your logger saw that the file name "errors.log.20130217" already existed.
Your logger didn't want to clobber that file, but still needed to rename "errors.log" to a date, so the logger instead created a different file name "errors.log.20130217.1"
To solve this, run your code just once.
If you're running multiple apps called "foo" and "bar" then use log file names like "foo-errors.log" and "bar-errors.log". Or if you're using multiple workers, give each worker its own log file name (e.g. by using the worker's process id, or worker pool array index, or however you're keeping track of your workers).
If you really want to solve this using the Ruby logger, you'll need to override the logger #shift_log_period so it doesn't choose a ".1" suffix. You could subclass Logger and write your worn #shift_log_period to detect that there is an existing log file for the date, and if so, use it instead of doing the file rename.
This is the code causing it from the logger:
def shift_log_period(period_end)
postfix = period_end.strftime("%Y%m%d") # YYYYMMDD
age_file = "#{#filename}.#{postfix}"
if FileTest.exist?(age_file)
# try to avoid filename crash caused by Timestamp change.
idx = 0
# .99 can be overridden; avoid too much file search with 'loop do'
while idx < 100
idx += 1
age_file = "#{#filename}.#{postfix}.#{idx}"
break unless FileTest.exist?(age_file)
end
end
#dev.close rescue nil
File.rename("#{#filename}", age_file)
#dev = create_logfile(#filename)
return true
There is no solution (AFAIK) using the Ruby logger, with its built-in rotator, to manage logs written by multiple apps (a.k.a. workers, processes, threads) simultaneously. This is because each of the apps gets it own log file handle.
Alternatively, use any of the good log rotator tools, such as logrotate as suggested by the Tin Man user in the question comments: http://linuxcommand.org/man_pages/logrotate8.html
In general, logrotate will be your best bet IMHO.

Model-specific SQL logging in rails

In my rails application, I have a background process runner, model name Worker, that checks for new tasks to run every 10 seconds. This check generates two SQL queries each time - one to look for new jobs, one to delete old completed ones.
The problem with this - the main log file gets spammed for each of those queries.
Can I direct the SQL queries spawned by the Worker model into a separate log file, or at least silence them? Overwriting Worker.logger does not work - it redirects only the messages that explicitly call logger.debug("something").
The simplest and most idiomatic solution
logger.silence do
do_something
end
See Logger#silence
Queries are logged at Adapter level as I demonstrated here.
How do I get the last SQL query performed by ActiveRecord in Ruby on Rails?
You can't change the behavior unless tweaking the Adapter behavior with some really really horrible hacks.
class Worker < ActiveRecord::Base
def run
old_level, self.class.logger.level = self.class.logger.level, Logger::WARN
run_outstanding_jobs
remove_obsolete_jobs
ensure
self.class.logger.level = old_level
end
end
This is a fairly familiar idiom. I've seen it many times, in different situations. Of course, if you didn't know that ActiveRecord::Base.logger can be changed like that, it would have been hard to guess.
One caveat of this solution: this changes the logger level for all of ActiveRecord, ActionController, ActionView, ActionMailer and ActiveResource. This is because there is a single Logger instance shared by all modules.

Resources