X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fcore%2Firclog.rb;h=40266772c70cc6d8cf2e538da546cc60c9a99f5d;hb=783ffa4235330029d661752b1023db635b26f2b3;hp=39d0a1fb5a291f7062324339571e2d22ef1ab60d;hpb=58d7ea0fe4491225ad5856a49b1a965f2e5ee40c;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/core/irclog.rb b/lib/rbot/core/irclog.rb index 39d0a1fb..40266772 100644 --- a/lib/rbot/core/irclog.rb +++ b/lib/rbot/core/irclog.rb @@ -20,6 +20,12 @@ class IrcLogModule < CoreBotModule bot.plugins.delegate 'event_irclog_list_changed', bot.config['irclog.no_log'], v }, :desc => "List of channels and nicks for which logging is enabled. IRC patterns can be used too. This can be used to override wide patters in irclog.no_log") + 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 @@ -27,8 +33,11 @@ class IrcLogModule < CoreBotModule @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 def can_log_on(where) @@ -37,6 +46,10 @@ class IrcLogModule < CoreBotModule 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}" @@ -50,8 +63,8 @@ class IrcLogModule < CoreBotModule 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 @@ -140,7 +153,7 @@ class IrcLogModule < CoreBotModule 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 @@ -163,13 +176,13 @@ class IrcLogModule < CoreBotModule 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 @@ -232,6 +245,10 @@ class IrcLogModule < CoreBotModule 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 @@ -240,13 +257,21 @@ class IrcLogModule < CoreBotModule 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) + + # 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| @@ -255,14 +280,45 @@ class IrcLogModule < CoreBotModule logfile_close w, "idle since #{@logs[w][0]}" end end - f = File.new("#{@bot.botclass}/logs/#{where_str}", "a") - f.sync = true - f.puts "[#{stamp}] @ Log started by #{@bot.myself.nick}" + 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][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'