Class: Logbert::Handlers::LogRotator

Inherits:
BaseHandler show all
Defined in:
lib/logbert/handlers/log_rotator.rb

Overview

This class is a custom Handler responsible for rotating logs. This # means that it will periodically: #

  • Close the current log file and rename it. #

  • Begin writing future log messages to a new file. #

  • Delete the oldest log files to free up space #

Time-based log rotation #

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseHandler

#formatter, #formatter=, #publish

Constructor Details

#initialize(path, options = {}) ⇒ LogRotator

Returns a new instance of LogRotator.



40
41
42
43
44
45
# File 'lib/logbert/handlers/log_rotator.rb', line 40

def initialize(path, options = {})
  @path                = path
  @max_backups         = options.fetch(:max_backups, nil)
  @timestamp_formatter = options.fetch(:timestamp_formatter, LocaltimeFormatter.new)
  @interval            = options.fetch(:interval, (24 * 60 * 60))
end

Instance Attribute Details

#expiration_timestampObject (readonly)

Returns the value of attribute expiration_timestamp.



37
38
39
# File 'lib/logbert/handlers/log_rotator.rb', line 37

def expiration_timestamp
  @expiration_timestamp
end

#file_handleObject (readonly)

Returns the value of attribute file_handle.



36
37
38
# File 'lib/logbert/handlers/log_rotator.rb', line 36

def file_handle
  @file_handle
end

#intervalObject (readonly)

Returns the value of attribute interval.



36
37
38
# File 'lib/logbert/handlers/log_rotator.rb', line 36

def interval
  @interval
end

#max_backupsObject (readonly)

Returns the value of attribute max_backups.



37
38
39
# File 'lib/logbert/handlers/log_rotator.rb', line 37

def max_backups
  @max_backups
end

#pathObject (readonly)

Returns the value of attribute path.



38
39
40
# File 'lib/logbert/handlers/log_rotator.rb', line 38

def path
  @path
end

#timestamp_formatterObject (readonly)

Returns the value of attribute timestamp_formatter.



36
37
38
# File 'lib/logbert/handlers/log_rotator.rb', line 36

def timestamp_formatter
  @timestamp_formatter
end

Instance Method Details

#already_rotated?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/logbert/handlers/log_rotator.rb', line 59

def already_rotated?
  return File.ctime(@path) > @expiration_timestamp
end

#attach_to_logfile!Object



63
64
65
66
67
68
69
70
71
# File 'lib/logbert/handlers/log_rotator.rb', line 63

def attach_to_logfile!
  lock do
    dirname               = File.dirname(File.absolute_path(@path))
    FileUtils.mkdir_p dirname unless File.exists? dirname
    @file_handle          = File.open(@path, 'a')
    creation_timestamp    = File.ctime(@path)
    @expiration_timestamp = compute_expiration_timestamp_from(creation_timestamp)
  end
end

#attached?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'lib/logbert/handlers/log_rotator.rb', line 47

def attached?
  return !file_handle.nil?
end

#compress_backupsObject



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/logbert/handlers/log_rotator.rb', line 125

def compress_backups
  # Compress older log files (unless already compressed)
  get_old_logs.each do |log|
    unless File.extname(log) == ".gz"
      # Compress the file
      gzip(log)
      # Delete the actual log file
      File.delete(log)
    end
  end
end

#compute_expiration_timestamp_from(timestamp) ⇒ Object



80
81
82
# File 'lib/logbert/handlers/log_rotator.rb', line 80

def compute_expiration_timestamp_from(timestamp)
  return timestamp + @interval
end

#delete_backupsObject



137
138
139
140
141
142
143
144
145
146
# File 'lib/logbert/handlers/log_rotator.rb', line 137

def delete_backups
  # Delete, if any, old log files based upon max_backups
  unless @max_backups.nil?
    # Grab all the logs.  Sort from newest to oldest
    old_logs = get_old_logs.sort_by {|f| File.ctime(f)}.reverse[@max_backups..-1]

    # If we have more than max_backups logs, then delete the extras
    old_logs.each {|f| File.delete(f)} if old_logs
  end
end

#emit(output) ⇒ Object



73
74
75
76
77
78
# File 'lib/logbert/handlers/log_rotator.rb', line 73

def emit(output)
  attach_to_logfile! unless attached?
  rotate_logs! if rotation_required?
  @file_handle.puts output
  @file_handle.flush
end

#get_old_logsObject



157
158
159
160
161
# File 'lib/logbert/handlers/log_rotator.rb', line 157

def get_old_logs
  absolute_dir = File.dirname(File.absolute_path(@path))
  older_files = Dir[File.join(absolute_dir, "#{@path}.backup.*")]
  return older_files
end

#gzip(file) ⇒ Object



148
149
150
151
152
153
154
155
# File 'lib/logbert/handlers/log_rotator.rb', line 148

def gzip(file)
  Zlib::GzipWriter.open("#{file}.gz") do |gz|
    gz.mtime = File.mtime(file)
    gz.orig_name = file
    gz.write IO.binread(file)
    gz.close
  end
end

#lock(&block) ⇒ Object



84
85
86
87
88
# File 'lib/logbert/handlers/log_rotator.rb', line 84

def lock(&block)
  Lockfile.new(lock_file_name_for(@path)) do
    block.call
  end
end

#lock_file_name_for(log_path) ⇒ Object



51
52
53
# File 'lib/logbert/handlers/log_rotator.rb', line 51

def lock_file_name_for(log_path)
  return "#{log_path}.lock"
end

#rotate_logs!Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/logbert/handlers/log_rotator.rb', line 90

def rotate_logs!
  performed_swap = false

  lock do
    # Double-check that the file wasn't already rotated
    unless already_rotated?
      performed_swap = true

      # Close the old log
      if @file_handle and not @file_handle.closed?
        @file_handle.close
      end

      # Rename the old log
      if File.exists? @path
        FileUtils.mv @path, archive_destination
      end

    end
  end # Lockfile lock

  attach_to_logfile!

  # Post-Processing logic if the rotation was performed
  post_process if performed_swap
end

#rotation_required?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/logbert/handlers/log_rotator.rb', line 55

def rotation_required?
  return Time.now > @expiration_timestamp
end