Module: LogRotate::Impl

Defined in:
lib/logrotate/impl.rb

Overview

This module contains implementation methods for the LogRotate class. In most cases, the client of this library will only use the LogRotate class directly to rotate files. However, I have exposed a number of methods in this class, as they may be useful.

Constant Summary collapse

DEFAULT_COUNT =
5
DEFAULT_GZIP =
false
DEFAULT_DATE_TIME_EXTENSION =
false
DEFAULT_DATE_TIME_FORMAT =
'%F'
DEFAULT_DATE_TIME_EXT =
false

Class Method Summary collapse

Class Method Details

.get_expired_rotated_files_date_extension(rotated_files_and_dates, count, new_rotated_file = nil) ⇒ Object

Description:

This method returns a list of rotated files that are due to be deleted.

The new_rotated_file field should only be set if the caller is currently in the process of rotating a file (eg. inside LogRotate::rotate_file), and the caller wants to retain 1 less file than count. This would be because the caller will shortly create a new rotated file, and would rather remove the expired rotated file BEFORE creating the new rotated file to minimize storage space usage. Thus, there would only be count rotated files at any time (as opposed to count + 1 if the caller first generated the new rotated file, and then deleted the expired file).

Parameters:

rotated_files_and_dates

The rotated file names and dates.

count

The number of rotated files to keep.

new_rotated_file

If the caller is in the midst of rotating a file, this value is set to the new rotated file (yet to be created, not listed in rotated_files_and_dates).

Returns:

A list of rotated file names and corresponding date/times to delete.



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/logrotate/impl.rb', line 142

def self.get_expired_rotated_files_date_extension(rotated_files_and_dates, 
                                                  count, 
                                                  new_rotated_file = nil)
  if (new_rotated_file)
    # Add 1 to account for the additional rotated file that will be
    # created after the original file passed in is rotated.
    num_old_files = rotated_files_and_dates.length() - count + 1
    
    # There is a chance that the newly rotated file will have the same
    # name as one of the already existing rotated files (eg. if the
    # extension is simply the date, and the job is ran a second time
    # in the same day).  In this case, there will be 1 less rotated
    # file (and thus 1 less to delete).
    if (rotated_files_and_dates.find {|entry| entry[:file] == new_rotated_file})
      num_old_files -= 1
    end
  else
    num_old_files = rotated_files_and_dates.length() - count
  end
  
  files_to_delete = []
  if (num_old_files > 0)
    files_to_delete = rotated_files_and_dates.slice!(-num_old_files, num_old_files)
  end
  
  return files_to_delete
end

.get_expired_rotated_files_integer_extension(rotated_files_and_counts, count, new_rotated_file = nil) ⇒ Object

Description:

This method returns a list of rotated files that are due to be deleted.

The new_rotated_file field should only be set if the caller is currently in the process of rotating a file (eg. inside LogRotate::rotate_file), and the caller wants to retain 1 less file than count. This would be because the caller will shortly create a new rotated file, and would rather remove the expired rotated file BEFORE creating the new rotated file to minimize storage space usage. Thus, there would only be count rotated files at any time (as opposed to count + 1 if the caller first generated the new rotated file, and then deleted the expired file).

Parameters:

rotated_files_and_counts

The rotated file names and associated indexes.

count

The number of rotated files to keep.

new_rotated_file

If the caller is in the midst of rotating a file, this value is set to the new rotated file (yet to be created, not listed in rotated_files_and_counts).

Returns:

A list of rotated file names and corresponding indexes to delete.



196
197
198
199
200
201
202
203
204
205
# File 'lib/logrotate/impl.rb', line 196

def self.get_expired_rotated_files_integer_extension(rotated_files_and_counts, 
                                                     count, 
                                                     new_rotated_file = nil)
  if (new_rotated_file)
    count -= 1
  end
  deleted_files_and_counts = rotated_files_and_counts.select { |entry| entry[:index] > count }
  
  return deleted_files_and_counts
end

.is_valid_date_time_file_name(file_name, rotation_base_name, date_time_format) ⇒ Object

This method validates the rotated file name with date extension. Specifically, it checks that the file name specified has a date that is formatted according to the date/time format specified.

Parameters:

file_name

The rotated file name to be validated.

rotation_base_name

The base name of the rotated file (without the date/time extension).

date_time_format

The format of the date/time extension of the rotated file.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/logrotate/impl.rb', line 37

def self.is_valid_date_time_file_name(file_name, rotation_base_name, date_time_format)
  base_name = File.basename(file_name)
  
  if (match_data = base_name.match("^#{rotation_base_name}\.([^.]+)(\.gz)?$"))
    # Since the caller is allowed to specify the date/time format,
    # here we do not have a regular expression to match the
    # date/time portion of the rotated file's extension.  As a
    # result, some foreign files can sneak into our list of rotated
    # files (eg. base_file.GARBAGE.gz).  We will attempt to weed
    # them out below.
    
    # Validation #1: convert the date/time portion of the
    # extension to a DateTime object and skip the file if an
    # exception is thrown.
    begin
      date_time = DateTime.strptime(match_data[1], date_time_format)
      gz = match_data[2] ? match_data[2] : ""
      
      # Validation #2: convert the date/time portion of the
      # extension to a DateTime object and back to a string again.
      # Then reconstruct the rotated file name with this string,
      # and ensure the reconstructed rotated file matches the
      # original.  This will take into account cases where
      # strptime succeeded but had additional garbage characters
      # present.  
      # eg: date_time_format = '%F', file = 'mysql_backup.2008-08-04.BACK.gz'
      reconstructed_file = "#{rotation_base_name}.#{date_time.strftime(date_time_format)}#{gz}"
      if (base_name == reconstructed_file)
        return true
      end
    rescue => e
    end
  end
  
  return false
end

.organize_rotated_files_date_extension(file_names, rotation_base_name, date_time_format) ⇒ Object

Description:

This method is passed a list of rotated file names. This method validates that list, and returns a list of rotated file names and corresponding dates/times. Specifically, an array is returned, where each element is a hash containing the following fields:

file

A rotated file name.

date_time

A DateTime object extracted from the rotated file name’s extension.

Validating the list refers to ensuring that each file name has a properly formatted date/time. See is_valid_date_time_file_name for further information.

Parameters:

file_names

The rotated file names.

rotation_base_name

The base name of the rotated file (without the date/time extension).

date_time_format

The format of the date/time extension of the rotated file.

Returns:

A list of rotated file names and corresponding date/times.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/logrotate/impl.rb', line 98

def self.organize_rotated_files_date_extension(file_names, rotation_base_name, date_time_format)
  rotated_files_and_dates = file_names.map do |file|
    if (is_valid_date_time_file_name(file, rotation_base_name, date_time_format))
      next get_rotated_file_and_date(file, date_time_format)
    end
    
    nil
  end
  
  # Remove the null entries.
  rotated_files_and_dates = rotated_files_and_dates.select { |entry| entry }
  
  # Sort files by date (newer to older).
  rotated_files_and_dates.sort! { |left, right| right[:date_time] <=> left[:date_time] }
  
  return rotated_files_and_dates
end

.organize_rotated_files_integer_extension(file_names, rotation_base_name) ⇒ Object

Description:

This method is passed a list of rotated file names. This method validates that list, and returns a list of rotated file names and corresponding indexes (derived from the file names’ integer extensions). Specifically, an array is returned, where each element is a hash containing the following fields:

file

A rotated file name.

index

The index extracted from the rotated file name’s extension.

Validating the list of file names specified consists of ensuring that each file name has a properly formatted integer extension.

Parameters:

file_names

The rotated file names.

rotation_base_name

The base name of the rotated file (without the date/time extension).

Returns:

A list of rotated file names and corresponding indexes.



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/logrotate/impl.rb', line 229

def self.organize_rotated_files_integer_extension(file_names, rotation_base_name)
  rotated_files_and_counts = file_names.map do |file_name|

    if (file_name.match("#{rotation_base_name}\.[0-9]+(\.gz)?$"))
      index = file_name.match("\.([0-9]+)(\.gz)?$")[1].to_i()
      next { :index => index, :file => file_name }
    end
    
    nil
  end
  
  rotated_files_and_counts = rotated_files_and_counts.select { |entry| entry }
  
  # Sort files by count (newer to older).
  rotated_files_and_counts.sort! { |left, right| left[:index] <=> right[:index] }
  
  return rotated_files_and_counts
end