1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
#
# This class allows to output a message to the log file or STDERR and
# to keep track of the progress of a method which takes long time to complete.
#
# @api private
#
class RGFA::Logger
# Information about the progress of a computation
ProgressData = Struct.new(:counter, :units, :partsize,
:lastpart, :total, :starttime,
:strlen)
# Create a Logger instance
#
# @param channel [#puts]
# where to output (default: STDERR)
# @param prefix [String]
# output prefix (default: "#")
# @param verbose_level [Integer]
# 0: no logging; >0: the higher, the more logging
# @return [RGFA::Logger]
def initialize(verbose_level: 1, channel: STDERR, prefix: "#")
@progress = false
if !verbose_level.kind_of?(Integer)
raise ArgumentError, "verbose_level must be an Integer"
end
if !channel.respond_to?(:puts)
raise TypeError, "channel must provide a puts method"
end
@channel = channel
@pfx = prefix
@verbose_level = verbose_level
@data = {}
end
# Output a message
#
# @param msg [String] message to output
# @param min_verbose_level [Integer]
# @return [void]
def log(msg, min_verbose_level=1)
@channel.puts "#@pfx #{msg}" if @verbose_level >= min_verbose_level
return nil
end
# Enable output from the Logger instance
#
# @param part [Float]
# - part = 0 => output at every call of {RGFA::Logger.progress_log}
# - 0 < part < 1 => output once per part of the total progress
# (e.g. 0.001 = log every 0.1% progress)
# - part = 1 => output only total elapsed time
# @return [void]
def enable_progress(part: 0.1)
if part < 0 or part > 1
raise ArgumentError, "part must be in range [0..1]"
end
@progress = true
@part = part
@channel.puts "#@pfx Progress logging enabled" if @verbose_level > 0
return nil
end
# Disable progress logging
# @return [void]
def disable_progress
@progress = false
@channel.puts "#@pfx Progress logging disabled" if @verbose_level > 0
return nil
end
# @!macro progress_init
# Initialize progress logging for a computation
# @param symbol [Symbol] a symbol assigned to the computation
# @param units [String] a string with the name of the units, in plural
# @param total [Integer] total number of units
# @param initmsg [String] an optional message to output at the beginning
# @return [void]
def progress_init(symbol, units, total, initmsg = nil)
return nil if !@progress or total == 0
str = "#@pfx 0.0% #{units} processed"
@data[symbol] = ProgressData.new(0, units, (@part*total).to_i, 1, total,
Time.now, str.size)
@channel.puts "#@pfx #{initmsg}" if initmsg
@channel.print str if @part != 1
return nil
end
# @!macro [new] progress_log
# Updates progress logging for a computation
# @!macro [new] prlog
# @param symbol [Symbol] the symbol assigned to the computation at
# init time
# @param keyargs [Hash] additional units to display, with their current
# value (e.g. segments_processed: 10000)
# @param progress [Integer] how many units were processed
# @return [void]
def progress_log(symbol, progress=1, **keyargs)
return nil if !@progress or @part == 1
data = @data[symbol]
return nil if data.nil?
data.counter += progress
if data.counter == data.total
progress_end(symbol)
elsif data.partsize == 0 or
(data.counter / data.partsize).to_i > data.lastpart
return nil if data.partsize == 0 and @part > 0
# this means total is very small
data.lastpart = data.counter / data.partsize if data.partsize > 0
done = data.counter.to_f / data.total
t = Time.now - data.starttime
eta = (t / done) - t
tstr= ("Elapsed: %02dh %02dmin %02ds" % [t/3600, t/60%60, t%60])
etastr = ("ETA: %02dh %02dmin %02ds" % [eta/3600, eta/60%60, eta%60])
donestr = "%.1f" % (done*100)
keystr = ""
keyargs.each {|k,v| keystr << "; #{k}: #{v}"}
str = "#@pfx #{donestr}% #{data.units} processed "+
"[#{tstr}; #{etastr}#{keystr}]"
if str.size > data.strlen
data.strlen = str.size
spacediff = ""
else
spacediff = " "*(data.strlen-str.size)
end
@channel.print "\r#{str}#{spacediff}"
@channel.flush
end
return nil
end
# @!macro [new] progress_end
# Completes progress logging for a computation
# @!macro prlog
# @return [void]
def progress_end(symbol, **keyargs)
return if !@progress
data = @data[symbol]
return if data.nil?
t = Time.now - data.starttime
tstr= ("Elapsed time: %02dh %02dmin %02ds" % [t/3600, t/60%60, t%60])
quantity = @part == 1 ? data.total.to_s : "100.0%"
keystr = ""
keyargs.each {|k,v| keystr << "; #{k}: #{v}"}
str = "#@pfx #{quantity} #{data.units} processed [#{tstr}#{keystr}]"
spacediff = " "*([data.strlen - str.size,0].max)
@channel.print "\r" if @part != 1
@channel.puts "#{str}#{spacediff}"
@channel.flush
@data.delete(symbol)
return nil
end
end
# Progress logging related-methods for RGFA class
module RGFA::LoggerSupport
# Activate logging of progress
# @return [RGFA] self
def enable_progress_logging(part: 0.1, channel: STDERR)
@progress = RGFA::Logger.new(channel: channel)
@progress.enable_progress(part: part)
return self
end
# @!macro progress_init
# @return [RGFA] self
# @api private
def progress_log_init(symbol, units, total, initmsg = nil)
@progress.progress_init(symbol, units, total, initmsg) if @progress
return self
end
# @!macro progress_log
# @return [RGFA] self
# @api private
def progress_log(symbol, progress=1, **keyargs)
@progress.progress_log(symbol, progress) if @progress
return self
end
# @!macro progress_end
# @return [RGFA] self
# @api private
def progress_log_end(symbol, **keyargs)
@progress.progress_end(symbol) if @progress
return self
end
end
|