class Sawmill::Rotater::DateBasedLogFile

A rotation strategy that produces log files with the date stamp in the file name. For example, you could set up an hourly log rotation that produces the following files:

rails.2009-10-09-22.log
rails.2009-10-09-23.log
rails.2009-10-10-00.log
rails.2009-10-10-01.log
etc...

The exact format depends on the rotation frequency, which could be anywhere from yearly to hourly. For settings less frequent than hourly, fewer fields will appear in the date stamp portion of the file name.

Public Class Methods

new(options_) click to toggle source

Create a new date-based log file rotation strategy.

Recognized options include:

:turnover_frequency

How often the log files should turn over. Allowed values are: :yearly, :monthly, :daily, :hourly, and :never.

:basedir

The base directory used if the filepath is a relative path. If not specified, the current working directory is used.

:path_prefix

The logfile path prefix. In the filename “rails.2009-10-11.log”, the prefix is “rails”. If not specified, defaults to “sawmill”.

:path_suffix

The logfile name prefix. In the filename “rails.2009-10-11.log”, the suffix is “.log”. If not specified, defaults to “.log”.

:uniquifier

If provided, log files are never reopened. (That is, they are opened with ::File::CREAT | ::File::EXCL.) The value of this parameter must be a proc that returns an actual file name to attempt to open. This proc is called repeatedly until it either returns a file path that does not yet exist, or signals failure by returning nil. See the session on Uniquifiers below.

:local_datestamps

If true, use the local timezone to create datestamps. The default is to use UTC.

Uniquifiers

DateBasedLogFile provides a facility for ensuring that log files are written to by only one process, by generating unique file names for log files. This facility is useful, for example, if you are deploying via Phusion Passenger where you may have a variable number of rails processes, and you want each process to own its own logfile so entries in log records are not interleaved.

To activate this feature, pass a proc to the :uniquifier option. When DateBasedLogFile wants to open a log file for writing, it first calls this proc. The proc should return a file path to try opening. DateBasedLogFile then tries to open the file with ::File::CREAT | ::File::EXCL, which will succeed only if the file has not already been created (e.g. by another process). If the file already exists, the proc will be called again, and repeatedly until it either returns a path that has not yet been created, or nil indicating that it has given up.

The proc is passed a single hash that provides information about what path to generate, as well as space for the proc to store any state it wishes to persist through the process. These keys are given to the proc by DateBasedLogFile. Any other keys are available for use by the proc.

:original_path

The original file path generated by DateBasedLogFile, which would have been used if there were no uniquifier.

:last_path

The last path generated by the proc, or nil if this is the first time this proc is called for a particular logfile.

:basedir

The basedir of the DateBasedLogFile.

:path_prefix

The path_prefix of the DateBasedLogFile.

:path_suffix

The path_suffix of the DateBasedLogFile.

# File lib/sawmill/rotater/date_based_log_file.rb, line 128
def initialize(options_)
  @turnover_frequency = options_[:turnover_frequency] || :none
  @basedir = options_[:basedir] || options_[:dirname] || ::Dir.getwd
  @prefix = options_[:path_prefix] || options_[:prefix] || 'sawmill'
  @suffix = options_[:path_suffix] || options_[:suffix] || '.log'
  @suffix = ".#{@suffix}" unless @suffix.length == 0 || @suffix[0,1] == '.'
  @uniquifier = options_[:uniquifier]
  @local_datestamps = options_[:local_datestamps]
  @date_pattern =
    case @turnover_frequency
    when :yearly then "%Y"
    when :monthly then "%Y-%m"
    when :daily then "%Y-%m-%d"
    when :hourly then "%Y-%m-%d-%H"
    else nil
    end
end
simple_uniquifier(opts_={}) click to toggle source

Returns a simple uniquifier that inserts an incrementing number before the path suffix. i.e. if the non-uniquified filename is “rails.2009-10-11.log”, then these names are generated:

rails.2009-10-11.0.log
rails.2009-10-11.1.log
rails.2009-10-11.2.log
etc.

The following options are available:

:min_digits

If provided, indicates the minimum number of digits for the unique number. For example, if :digits is set to 2, these names are generated:

rails.2009-10-11.00.log
rails.2009-10-11.01.log
rails.2009-10-11.02.log
...
rails.2009-10-11.09.log
rails.2009-10-11.10.log
rails.2009-10-11.11.log
...
rails.2009-10-11.99.log
rails.2009-10-11.100.log
rails.2009-10-11.101.log
etc.

The default is 1.

:start_value

The first value for the unique number. Default is 0.

# File lib/sawmill/rotater/date_based_log_file.rb, line 233
def simple_uniquifier(opts_={})
  if (digits_ = opts_[:min_digits])
    pattern_ = "%s.%0#{digits_.to_i}d%s"
  else
    pattern_ = "%s.%d%s"
  end
  ::Proc.new do |hash_|
    if hash_[:last_path]
      hash_[:value] += 1
    else
      suffix_ = hash_[:path_suffix]
      orig_ = hash_[:original_path]
      suffix_len_ = suffix_.length
      if suffix_len_ > 0 && orig_[-suffix_len_, suffix_len_] == suffix_
        pre_ = orig_[0, orig_.length - suffix_len_]
        post_ = suffix_
      else
        pre_ = orig_
        post_ = ''
      end
      hash_[:value] = opts_[:start_value].to_i
      hash_[:pre] = pre_
      hash_[:post] = post_
    end
    pattern_ % [hash_[:pre], hash_[:value], hash_[:post]]
  end
end

Public Instance Methods

before_write() click to toggle source

Implements the rotation strategy contract.

# File lib/sawmill/rotater/date_based_log_file.rb, line 195
def before_write
end
close_handle(handle_, io_) click to toggle source

Implements the rotation strategy contract.

# File lib/sawmill/rotater/date_based_log_file.rb, line 188
def close_handle(handle_, io_)
  io_.close
end
open_handle(handle_) click to toggle source

Implements the rotation strategy contract.

# File lib/sawmill/rotater/date_based_log_file.rb, line 162
def open_handle(handle_)
  path_ = ::File.expand_path(@date_pattern ? "#{@prefix}.#{handle_}#{@suffix}" : @prefix+@suffix, @basedir)
  file_ = nil
  if @uniquifier
    hash_ = {:path_prefix => @prefix, :path_suffix => @suffix, :basedir => @basedir, :original_path => path_.dup, :last_path => nil}
    until file_
      path_ = @uniquifier.call(hash_)
      unless path_
        raise Errors::NoUniqueLogFileError, "Could not find a unique log file path for #{hash_.inspect}"
      end
      begin
        file_ = ::File.open(path_, ::File::CREAT | ::File::EXCL | ::File::WRONLY)
      rescue ::Errno::EEXIST
        hash_[:last_path] = path_
      end
    end
  else
    file_ = ::File.open(path_, 'a')
  end
  file_.sync = true
  file_
end
preferred_handle() click to toggle source

Implements the rotation strategy contract.

# File lib/sawmill/rotater/date_based_log_file.rb, line 149
def preferred_handle
  if @date_pattern
    time_ = ::Time.now
    time_.utc unless @local_datestamps
    time_.strftime(@date_pattern)
  else
    ''
  end
end