Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 807
Eki Eqbal Avatar asked Feb 17 '13 23:02

Eki Eqbal


1 Answers

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.

like image 65
joelparkerhenderson Avatar answered Nov 15 '22 09:11

joelparkerhenderson