Config.register Config::StringValue.new('irclog.filename_format',
:default => '%%{where}', :requires_rescan => true,
:desc => "filename pattern for the IRC log. You can put typical strftime keys such as %Y for year and %m for month, plus the special %%{where} key for location (channel name or user nick)")
+ Config.register Config::StringValue.new('irclog.timestamp_format',
+ :default => '[%Y/%m/%d %H:%M:%S]', :requires_rescan => true,
+ :desc => "timestamp pattern for the IRC log, using typical strftime keys")
attr :nolog_rx, :dolog_rx
def initialize
@queue = Queue.new
@thread = Thread.new { loggers_thread }
@logs = Hash.new
- Dir.mkdir("#{@bot.botclass}/logs") unless File.exist?("#{@bot.botclass}/logs")
+ logdir = @bot.path 'logs'
+ Dir.mkdir(logdir) unless File.exist?(logdir)
+ # TODO what shall we do if the logdir couldn't be created? (e.g. it existed as a file)
event_irclog_list_changed(@bot.config['irclog.no_log'], @bot.config['irclog.do_log'])
@fn_format = @bot.config['irclog.filename_format']
end
return true
end
+ def timestamp(time)
+ return time.strftime(@bot.config['irclog.timestamp_format'])
+ end
+
def event_irclog_list_changed(nolist, dolist)
@nolog_rx = nolist.empty? ? nil : Regexp.union(*(nolist.map { |r| r.to_irc_regexp }))
debug "no log: #{@nolog_rx}"
def logfile_close(where_str, reason = 'unknown reason')
f = @logs.delete(where_str) or return
- stamp = Time.now.strftime '%Y/%m/%d %H:%M:%S'
- f[1].puts "[#{stamp}] @ Log closed by #{@bot.myself.nick} (#{reason})"
+ stamp = timestamp(Time.now)
+ f[1].puts "#{stamp} @ Log closed by #{@bot.myself.nick} (#{reason})"
f[1].close
end
irclog "@ #{m.source} asked #{who} about #{[m.ctcp, m.message].join(' ')}", logtarget
end
else
- if m.public?
+ if m.public?
irclog "<#{m.source}> #{m.logmessage}", m.target
else
irclog "<#{m.source}(#{m.sourceaddress})> #{m.logmessage}", m.source
end
def log_nick(m)
- m.is_on.each { |ch|
+ (m.is_on & @bot.myself.channels).each { |ch|
irclog "@ #{m.oldnick} is now known as #{m.newnick}", ch
}
end
def log_quit(m)
- m.was_on.each { |ch|
+ (m.was_on & @bot.myself.channels).each { |ch|
irclog "@ Quit: #{m.source}: #{m.logmessage}", ch
}
end
end
def logfilepath(where_str, now)
- File.join(@bot.botclass, 'logs', now.strftime(@fn_format) % { :where => where_str })
+ @bot.path('logs', now.strftime(@fn_format) % { :where => where_str })
end
protected
message, where = ls
message = message.chomp
now = Time.now
- stamp = now.strftime("%Y/%m/%d %H:%M:%S")
+ stamp = timestamp(now)
if where.class <= Server
where_str = "server"
else
where_str = where.downcase.gsub(/[:!?$*()\/\\<>|"']/, "_")
end
- return unless can_log_on(where_str)
+ next unless can_log_on(where_str)
# close the previous logfile if we're rotating
if @logs.has_key? where_str
end
end
fp = logfilepath(where_str, now)
- FileUtils.mkdir_p File.dirname(fp)
- f = File.new(fp, "a")
- f.sync = true
- f.puts "[#{stamp}] @ Log started by #{@bot.myself.nick}"
+ begin
+ dir = File.dirname(fp)
+ # first of all, we check we're not trying to build a directory structure
+ # where one of the components exists already as a file, so we
+ # backtrack along dir until we come across the topmost existing name.
+ # If it's a file, we rename to filename.old.filedate
+ up = dir.dup
+ until File.exist? up
+ up.replace(File.dirname(up))
+ end
+ unless File.directory? up
+ backup = up.dup
+ backup << ".old." << File.atime(up).strftime('%Y%m%d%H%M%S')
+ debug "#{up} is not a directory! renaming to #{backup}"
+ File.rename(up, backup)
+ end
+ FileUtils.mkdir_p(dir)
+ # conversely, it may happen that fp exists and is a directory, in
+ # which case we rename the directory instead
+ if File.directory? fp
+ backup = fp.dup
+ backup << ".old." << File.atime(fp).strftime('%Y%m%d%H%M%S')
+ debug "#{fp} is not a file! renaming to #{backup}"
+ File.rename(fp, backup)
+ end
+ # it should be fine to create the file now
+ f = File.new(fp, "a")
+ f.sync = true
+ f.puts "#{stamp} @ Log started by #{@bot.myself.nick}"
+ rescue Exception => e
+ error e
+ next
+ end
@logs[where_str] = [now, f]
end
- @logs[where_str][1].puts "[#{stamp}] #{message}"
+ @logs[where_str][1].puts "#{stamp} #{message}"
@logs[where_str][0] = now
- #debug "[#{stamp}] <#{where}> #{message}"
+ #debug "#{stamp} <#{where}> #{message}"
end
@logs.keys.each { |w| logfile_close(w, 'rescan or shutdown') }
debug 'loggers_thread terminating'