RollingFileOutputter - subclass of FileOutputter that rolls files on size or time. So, given a filename of “error.log“, the first log file will be “error000001.log“. When its check condition is exceeded, it’ll create and log to “error000002.log“, etc.
Additional hash arguments are:
Maximum size of the file in bytes.
Maximum age of the file in seconds.
Maxium number of prior log files to maintain. If max_backups is a positive number,
then each time a roll happens, RollingFileOutputter will delete the oldest backup log files in excess of this number (if any). So, if max_backups is 10, then a maximum of 11 files will be maintained (the current log, plus 10 backups). If max_backups is 0, no backups will be kept. If it is negative (the default), there will be no limit on the number of files created. Note that the sequence numbers will continue to escalate; old sequence numbers are not reused.
If true, deletes ALL existing log files (based on :filename) upon initialization,
and the sequence numbering will start over at 000001. Otherwise continues logging where it left off last time (i.e. either to the file with the highest sequence number, or a new file, as appropriate).
# File lib/log4r/outputter/rollingfileoutputter.rb, line 33 33: def initialize(_name, hash={}) 34: super( _name, hash.merge({:create => false}) ) 35: if hash.has_key?(:maxsize) || hash.has_key?('maxsize') 36: _maxsize = (hash[:maxsize] or hash['maxsize']).to_i 37: if _maxsize.class != Fixnum 38: raise TypeError, "Argument 'maxsize' must be an Fixnum", caller 39: end 40: if _maxsize == 0 41: raise TypeError, "Argument 'maxsize' must be > 0", caller 42: end 43: @maxsize = _maxsize 44: end 45: if hash.has_key?(:maxtime) || hash.has_key?('maxtime') 46: _maxtime = (hash[:maxtime] or hash['maxtime']).to_i 47: if _maxtime.class != Fixnum 48: raise TypeError, "Argument 'maxtime' must be an Fixnum", caller 49: end 50: if _maxtime == 0 51: raise TypeError, "Argument 'maxtime' must be > 0", caller 52: end 53: @maxtime = _maxtime 54: end 55: if hash.has_key?(:max_backups) || hash.has_key?('max_backups') 56: _max_backups = (hash[:max_backups] or hash['max_backups']).to_i 57: if _max_backups.class != Fixnum 58: raise TypeError, "Argument 'max_backups' must be an Fixnum", caller 59: end 60: @max_backups = _max_backups 61: else 62: @max_backups = 1 63: end 64: # @filename starts out as the file (including path) provided by the user, e.g. "\usr\logs\error.log". 65: # It will get assigned the current log file (including sequence number) 66: # @log_dir is the directory in which we'll log, e.g. "\usr\logs" 67: # @file_extension is the file's extension (if any) including any period, e.g. ".log" 68: # @core_file_name is the part of the log file's name, sans sequence digits or extension, e.g. "error" 69: @log_dir = File.dirname(@filename) 70: @file_extension = File.extname(@filename) # Note: the File API doc comment states that this doesn't include the period, but its examples and behavior do include it. We'll depend on the latter. 71: @core_file_name = File.basename(@filename, @file_extension) 72: if (@trunc) 73: purge_log_files(0) 74: end 75: @current_sequence_number = get_current_sequence_number() 76: makeNewFilename 77: # Now @filename points to a properly sequenced filename, which may or may not yet exist. 78: open_log_file('a') 79: 80: # Note: it's possible we're already in excess of our time or size constraint for the current file; 81: # no worries -- if a new file needs to be started, it'll happen during the write() call. 82: end
Get the highest existing log file sequence number, or 1 if there are no existing log files.
# File lib/log4r/outputter/rollingfileoutputter.rb, line 111 111: def get_current_sequence_number() 112: max_seq_no = 0 113: Dir.foreach(@log_dir) do |child| 114: if child =~ /^#{@core_file_name}(\d+)#{@file_extension}$/ 115: seq_no = $1.to_i 116: if (seq_no > max_seq_no) 117: max_seq_no = seq_no 118: end 119: end 120: end 121: return [max_seq_no, 1].max 122: end
Constructs a new filename from the @current_sequence_number, @core_file_name, and @file_extension, and assigns it to @filename
# File lib/log4r/outputter/rollingfileoutputter.rb, line 135 135: def makeNewFilename 136: # note use of hard coded 6 digit sequence width - is this enough files? 137: padded_seq_no = "0" * (6 - @current_sequence_number.to_s.length) + @current_sequence_number.to_s 138: newbase = "#{@core_file_name}#{padded_seq_no}#{@file_extension}" 139: @filename = File.join(@log_dir, newbase) 140: end
Open @filename with the given mode:
'a' - appends to the end of the file if it exists; otherwise creates it. 'w' - truncates the file to zero length if it exists, otherwise creates it.
Re-initializes @datasize and @startime appropriately.
# File lib/log4r/outputter/rollingfileoutputter.rb, line 146 146: def open_log_file(mode) 147: # It appears that if a file has been recently deleted then recreated, calls like 148: # File.ctime can return the erstwhile creation time. File.size? can similarly return 149: # old information. So instead of simply doing ctime and size checks after File.new, we 150: # do slightly more complicated checks beforehand: 151: if (mode == 'w' || !File.exists?(@filename)) 152: @start_time = Time.now() 153: @datasize = 0 154: else 155: @start_time = File.ctime(@filename) 156: @datasize = File.size?(@filename) || 0 # File.size? returns nil even if the file exists but is empty; we convert it to 0. 157: end 158: @out = File.new(@filename, mode) 159: Logger.log_internal {"File #{@filename} opened with mode #{mode}"} 160: end
Delete all but the latest number_to_keep log files.
# File lib/log4r/outputter/rollingfileoutputter.rb, line 89 89: def purge_log_files(number_to_keep) 90: Dir.chdir(@log_dir) do 91: # Make a list of the log files to delete. Start with all of the matching log files... 92: glob = "#{@core_file_name}[0-9][0-9][0-9][0-9][0-9][0-9]#{@file_extension}" 93: files = Dir.glob(glob) 94: 95: # ... if there are fewer than our threshold, just return... 96: if (files.size() <= number_to_keep ) 97: # Logger.log_internal {"No log files need purging."} 98: return 99: end 100: # ...then remove those that we want to keep (i.e. the most recent #{number_to_keep} files). 101: files.sort!().slice!(-number_to_keep, number_to_keep) 102: 103: # Delete the files. We use force (rm_f), so in case any files can't be deleted (e.g. someone's got one 104: # open in an editor), we'll swallow the error and keep going. 105: FileUtils.rm_f(files) 106: Logger.log_internal { "Purged #{files.length} log files: #{files}" } 107: end 108: end
does the file require a roll?
# File lib/log4r/outputter/rollingfileoutputter.rb, line 163 163: def requiresRoll 164: if !@maxsize.nil? && @datasize > @maxsize 165: Logger.log_internal { "Rolling because #{@filename} (#{@datasize} bytes) has exceded the maxsize limit (#{@maxsize} bytes)." } 166: return true 167: end 168: if !@maxtime.nil? && (Time.now - @start_time) > @maxtime 169: Logger.log_internal { "Rolling because #{@filename} (created: #{@start_time}) has exceded the maxtime age (#{@maxtime} seconds)." } 170: return true 171: end 172: false 173: end
roll the file
# File lib/log4r/outputter/rollingfileoutputter.rb, line 176 176: def roll 177: begin 178: # If @baseFilename == @filename, then this method is about to 179: # try to close out a file that is not actually opened because 180: # fileoutputter has been called with the parameter roll=true 181: # TODO: Is this check valid any more? I suspect not. Am commenting out...: 182: #if ( @baseFilename != @filename ) then 183: @out.close 184: #end 185: rescue 186: Logger.log_internal { 187: "RollingFileOutputter '#{@name}' could not close #{@filename}" 188: } 189: end 190: 191: # Prepare the next file. (Note: if max_backups is zero, we can skip this; we'll 192: # just overwrite the existing log file) 193: if (@max_backups != 0) 194: @current_sequence_number += 1 195: makeNewFilename 196: end 197: 198: open_log_file('w') 199: 200: # purge any excess log files (unless max_backups is negative, which means don't purge). 201: if (@max_backups >= 0) 202: purge_log_files(@max_backups + 1) 203: end 204: 205: end
perform the write
# File lib/log4r/outputter/rollingfileoutputter.rb, line 125 125: def write(data) 126: # we have to keep track of the file size ourselves - File.size doesn't 127: # seem to report the correct size when the size changes rapidly 128: @datasize += data.size + 1 # the 1 is for newline 129: roll if requiresRoll 130: super 131: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.