+ irclog m.logmessage, ".unknown"
+ end
+
+ def logfilepath(where_str, now)
+ @bot.path('logs', now.strftime(@fn_format) % { :where => where_str })
+ end
+
+ protected
+ def loggers_thread
+ ls = nil
+ debug 'loggers_thread starting'
+ while ls = @queue.pop
+ message, where = ls
+ message = message.chomp
+ now = Time.now
+ stamp = timestamp(now)
+ if where.class <= Server
+ where_str = "server"
+ else
+ where_str = where.downcase.gsub(/[:!?$*()\/\\<>|"']/, "_")
+ end
+ next unless can_log_on(where_str)
+
+ # close the previous logfile if we're rotating
+ if @logs.has_key? where_str
+ fp = logfilepath(where_str, now)
+ logfile_close(where_str, 'log rotation') if fp != @logs[where_str][1].path
+ end
+
+ # (re)open the logfile if necessary
+ unless @logs.has_key? where_str
+ if @logs.size > @bot.config['irclog.max_open_files']
+ @logs.keys.sort do |a, b|
+ @logs[a][0] <=> @logs[b][0]
+ end.slice(0, @logs.size - @bot.config['irclog.max_open_files']).each do |w|
+ logfile_close w, "idle since #{@logs[w][0]}"
+ end
+ end
+ fp = logfilepath(where_str, now)
+ 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][0] = now
+ #debug "#{stamp} <#{where}> #{message}"
+ end
+ @logs.keys.each { |w| logfile_close(w, 'rescan or shutdown') }
+ debug 'loggers_thread terminating'