]> git.cworth.org Git - sup/commitdiff
rewrite logging to have multiple levels: debug, info, etc.
authorWilliam Morgan <wmorgan-sup@masanjin.net>
Mon, 17 Aug 2009 15:47:20 +0000 (11:47 -0400)
committerWilliam Morgan <wmorgan-sup@masanjin.net>
Mon, 17 Aug 2009 18:47:13 +0000 (14:47 -0400)
Level is now set by SUP_LOG_LEVEL environment variable.

Big patch here, but most of it is replacing the old Redwood::log calls.

Some work on poll-mode, which subclasses log-mode. Remind me never to use
inheritance again.

27 files changed:
bin/sup
lib/sup.rb
lib/sup/colormap.rb
lib/sup/crypto.rb
lib/sup/ferret_index.rb
lib/sup/hook.rb
lib/sup/imap.rb
lib/sup/index.rb
lib/sup/logger.rb
lib/sup/maildir.rb
lib/sup/mbox.rb
lib/sup/mbox/ssh-file.rb
lib/sup/message-chunks.rb
lib/sup/message.rb
lib/sup/mode.rb
lib/sup/modes/edit-message-mode.rb
lib/sup/modes/label-list-mode.rb
lib/sup/modes/log-mode.rb
lib/sup/modes/poll-mode.rb
lib/sup/modes/reply-mode.rb
lib/sup/modes/thread-view-mode.rb
lib/sup/poll.rb
lib/sup/sent.rb
lib/sup/source.rb
lib/sup/textfield.rb
lib/sup/util.rb
lib/sup/xapian_index.rb

diff --git a/bin/sup b/bin/sup
index 0fb28fa4e3aa7a8657c88c1ab886e5480b049241..155bb65b49193d73cbf2123baae2a5ec54f6ebc1 100755 (executable)
--- a/bin/sup
+++ b/bin/sup
@@ -95,17 +95,17 @@ module LibC
     else; "libc.so.6"
   end
 
-  Redwood::log "dynamically loading setlocale() from #{setlocale_lib}"
+  debug "dynamically loading setlocale() from #{setlocale_lib}"
   begin
     dlload setlocale_lib
     extern "void setlocale(int, const char *)"
-    Redwood::log "setting locale..."
+    debug "setting locale..."
     LibC.setlocale(6, "")  # LC_ALL == 6
   rescue RuntimeError => e
-    Redwood::log "cannot dlload setlocale(); ncurses wide character support probably broken."
-    Redwood::log "dlload error was #{e.class}: #{e.message}"
+    warn "cannot dlload setlocale(); ncurses wide character support probably broken."
+    warn "dlload error was #{e.class}: #{e.message}"
     if Config::CONFIG['arch'] =~ /bsd/
-      Redwood::log "BSD variant detected. You may have to install a compat6x package to acquire libc."
+      warn "BSD variant detected. You may have to install a compat6x package to acquire libc."
     end
   end
 end
@@ -163,7 +163,7 @@ begin
   if(s = Redwood::SourceManager.source_for DraftManager.source_name)
     DraftManager.source = s
   else
-    Redwood::log "no draft source, auto-adding..."
+    debug "no draft source, auto-adding..."
     Redwood::SourceManager.add_source DraftManager.new_source
   end
 
@@ -175,18 +175,24 @@ begin
 
   HookManager.run "startup"
 
-  log "starting curses"
+  debug "starting curses"
+  Redwood::Logger.remove_sink $stderr
   start_cursing
 
   bm = BufferManager.init
   Colormap.new.populate_colormap
 
-  log "initializing mail index buffer"
+  debug "initializing log buffer"
+  lmode = Redwood::LogMode.new "system log"
+  lmode.on_kill { Logger.clear! }
+  Logger.add_sink lmode
+  Logger.force_message "Welcome to Sup! Log level is set to #{Logger.level}."
+
+  debug "initializing inbox buffer"
   imode = InboxMode.new
   ibuf = bm.spawn "Inbox", imode
 
-  log "ready for interaction!"
-  Logger.make_buf
+  debug "ready for interaction!"
 
   bm.draw_screen
 
@@ -196,7 +202,7 @@ begin
       begin
         s.connect
       rescue SourceError => e
-        Redwood::log "fatal error loading from #{s}: #{e.message}"
+        error "fatal error loading from #{s}: #{e.message}"
       end
     end
   end unless $opts[:no_initial_poll]
@@ -320,17 +326,19 @@ ensure
 
   Redwood::finish
   stop_cursing
-  Redwood::log "stopped cursing"
+  Redwood::Logger.remove_all_sinks!
+  Redwood::Logger.add_sink $stderr, false
+  debug "stopped cursing"
 
   if SuicideManager.instantiated? && SuicideManager.die?
-    Redwood::log "I've been ordered to commit seppuku. I obey!"
+    info "I've been ordered to commit seppuku. I obey!"
   end
 
   if Redwood::exceptions.empty?
-    Redwood::log "no fatal errors. good job, william."
+    debug "no fatal errors. good job, william."
     Index.save
   else
-    Redwood::log "oh crap, an exception"
+    error "oh crap, an exception"
   end
 
   Index.unlock
index 00ad87cad7aa6bce62c35cce2eb162fc6287db4b..ed144589a705abd854573e9b9f6eddf247ec458a 100644 (file)
@@ -233,28 +233,25 @@ require "sup/hook"
 Redwood::HookManager.init Redwood::HOOK_DIR
 
 ## everything we need to get logging working
-require "sup/buffer"
-require "sup/keymap"
-require "sup/mode"
-require "sup/modes/scroll-mode"
-require "sup/modes/text-mode"
-require "sup/modes/log-mode"
 require "sup/logger"
-module Redwood
-  def log s; Logger.log s; end
-  module_function :log
-end
+Redwood::Logger.init.add_sink $stderr
+include Redwood::LogsStuff
 
 ## determine encoding and character set
   $encoding = Locale.current.charset
   if $encoding
-    Redwood::log "using character set encoding #{$encoding.inspect}"
+    debug "using character set encoding #{$encoding.inspect}"
   else
-    Redwood::log "warning: can't find character set by using locale, defaulting to utf-8"
+    warn "can't find character set by using locale, defaulting to utf-8"
     $encoding = "UTF-8"
   end
 
-## now everything else (which can feel free to call Redwood::log at load time)
+require "sup/buffer"
+require "sup/keymap"
+require "sup/mode"
+require "sup/modes/scroll-mode"
+require "sup/modes/text-mode"
+require "sup/modes/log-mode"
 require "sup/update"
 require "sup/suicide"
 require "sup/message-chunks"
index 38787cddc41374a9f9adf583160627a5dfb448e0..fbbbfc9e9405d1be3eb88d4dd647b6e96e9e85d8 100644 (file)
@@ -129,7 +129,7 @@ class Colormap
       @next_id = (@next_id + 1) % NUM_COLORS
       @next_id += 1 if @next_id == 0 # 0 is always white on black
       id = @next_id
-      Redwood::log "colormap: for color #{sym}, using id #{id} -> #{fg}, #{bg}"
+      debug "colormap: for color #{sym}, using id #{id} -> #{fg}, #{bg}"
       Curses.init_pair id, fg, bg or raise ArgumentError,
         "couldn't initialize curses color pair #{fg}, #{bg} (key #{id})"
 
@@ -137,7 +137,7 @@ class Colormap
       ## delete the old mapping, if it exists
       if @users[cp]
         @users[cp].each do |usym|
-          Redwood::log "dropping color #{usym} (#{id})"
+          warn "dropping color #{usym} (#{id})"
           @entries[usym][3] = nil
         end
         @users[cp] = []
@@ -155,7 +155,7 @@ class Colormap
   ## to the default ones.
   def populate_colormap
     user_colors = if File.exists? Redwood::COLOR_FN
-      Redwood::log "loading user colors from #{Redwood::COLOR_FN}"
+      debug "loading user colors from #{Redwood::COLOR_FN}"
       Redwood::load_yaml_obj Redwood::COLOR_FN
     end
 
@@ -171,7 +171,7 @@ class Colormap
             fg = Curses.const_get "COLOR_#{ufg.upcase}"
           rescue NameError
             error ||= "Warning: there is no color named \"#{ufg}\", using fallback."
-            Redwood::log "Warning: there is no color named \"#{ufg}\""
+            warn "there is no color named \"#{ufg}\""
           end
         end
 
@@ -180,7 +180,7 @@ class Colormap
             bg = Curses.const_get "COLOR_#{ubg.upcase}"
           rescue NameError
             error ||= "Warning: there is no color named \"#{ubg}\", using fallback."
-            Redwood::log "Warning: there is no color named \"#{ubg}\""
+            warn "there is no color named \"#{ubg}\""
           end
         end
 
@@ -190,7 +190,7 @@ class Colormap
               Curses.const_get "A_#{a.upcase}"
             rescue NameError
               error ||= "Warning: there is no attribute named \"#{a}\", using fallback."
-              Redwood::log "Warning: there is no attribute named \"#{a}\", using fallback."
+              warn "there is no attribute named \"#{a}\", using fallback."
             end
           end
         end
index a3f7afcd2099214bc674340eaea445a394bd4b98..7f044b99428d0beb80bfb22fa1f4cda6bc127011 100644 (file)
@@ -18,10 +18,10 @@ class CryptoManager
     @cmd =
       case bin
       when /\S/
-        Redwood::log "crypto: detected gpg binary in #{bin}"
+        debug "crypto: detected gpg binary in #{bin}"
         "#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
       else
-        Redwood::log "crypto: no gpg binary detected"
+        debug "crypto: no gpg binary detected"
         nil
       end
   end
@@ -154,9 +154,7 @@ private
 
   def run_gpg args
     cmd = "#{@cmd} #{args} 2> /dev/null"
-    #Redwood::log "crypto: running: #{cmd}"
     output = `#{cmd}`
-    #Redwood::log "crypto: output: #{output.inspect}" unless $?.success?
     output
   end
 end
index 3655d7a1d3d4d00765d57620ac65feecf9c5e458..df1139d97b67d878fa84f9440a173206f11cf26f 100644 (file)
@@ -18,13 +18,13 @@ class FerretIndex < BaseIndex
 
   def load_index dir=File.join(@dir, "ferret")
     if File.exists? dir
-      Redwood::log "loading index..."
+      debug "loading index..."
       @index_mutex.synchronize do
         @index = Ferret::Index::Index.new(:path => dir, :analyzer => @analyzer, :id_field => 'message_id')
-        Redwood::log "loaded index of #{@index.size} messages"
+        debug "loaded index of #{@index.size} messages"
       end
     else
-      Redwood::log "creating index..."
+      debug "creating index..."
       @index_mutex.synchronize do
         field_infos = Ferret::Index::FieldInfos.new :store => :yes
         field_infos.add_field :message_id, :index => :untokenized
@@ -90,9 +90,9 @@ class FerretIndex < BaseIndex
     if entry[:source_id] && entry[:source_info] && entry[:label] &&
       ((entry[:source_id].to_i > source_id) || (entry[:source_info].to_i < m.source_info))
       labels += entry[:label].to_set_of_symbols
-      #Redwood::log "found updated version of message #{m.id}: #{m.subj}"
-      #Redwood::log "previous version was at #{entry[:source_id].inspect}:#{entry[:source_info].inspect}, this version at #{source_id.inspect}:#{m.source_info.inspect}"
-      #Redwood::log "merged labels are #{labels.inspect} (index #{entry[:label].inspect}, message #{m.labels.inspect})"
+      #debug "found updated version of message #{m.id}: #{m.subj}"
+      #debug "previous version was at #{entry[:source_id].inspect}:#{entry[:source_info].inspect}, this version at #{source_id.inspect}:#{m.source_info.inspect}"
+      #debug "merged labels are #{labels.inspect} (index #{entry[:label].inspect}, message #{m.labels.inspect})"
       entry = {}
     end
 
@@ -151,7 +151,7 @@ class FerretIndex < BaseIndex
     while true
       limit = (query[:limit])? [EACH_BY_DATE_NUM, query[:limit] - offset].min : EACH_BY_DATE_NUM
       results = @index_mutex.synchronize { @index.search ferret_query, :sort => "date DESC", :limit => limit, :offset => offset }
-      Redwood::log "got #{results.total_hits} results for query (offset #{offset}) #{ferret_query.inspect}"
+      debug "got #{results.total_hits} results for query (offset #{offset}) #{ferret_query.inspect}"
       results.hits.each do |hit|
         yield @index_mutex.synchronize { @index[hit.doc][:message_id] }, lambda { build_message hit.doc }
       end
@@ -170,7 +170,7 @@ class FerretIndex < BaseIndex
   SAME_SUBJECT_DATE_LIMIT = 7
   MAX_CLAUSES = 1000
   def each_message_in_thread_for m, opts={}
-    #Redwood::log "Building thread for #{m.id}: #{m.subj}"
+    #debug "Building thread for #{m.id}: #{m.subj}"
     messages = {}
     searched = {}
     num_queries = 0
@@ -191,10 +191,10 @@ class FerretIndex < BaseIndex
       q = build_ferret_query :qobj => q
 
       p1 = @index_mutex.synchronize { @index.search(q).hits.map { |hit| @index[hit.doc][:message_id] } }
-      Redwood::log "found #{p1.size} results for subject query #{q}"
+      debug "found #{p1.size} results for subject query #{q}"
 
       p2 = @index_mutex.synchronize { @index.search(q.to_s, :limit => :all).hits.map { |hit| @index[hit.doc][:message_id] } }
-      Redwood::log "found #{p2.size} results in string form"
+      debug "found #{p2.size} results in string form"
 
       pending = (pending + p1 + p2).uniq
     end
@@ -225,7 +225,7 @@ class FerretIndex < BaseIndex
           end
           mid = @index[docid][:message_id]
           unless messages.member?(mid)
-            #Redwood::log "got #{mid} as a child of #{id}"
+            #debug "got #{mid} as a child of #{id}"
             messages[mid] ||= lambda { build_message docid }
             refs = @index[docid][:refs].split
             pending += refs.select { |id| !searched[id] }
@@ -235,10 +235,10 @@ class FerretIndex < BaseIndex
     end
 
     if killed
-      #Redwood::log "thread for #{m.id} is killed, ignoring"
+      #debug "thread for #{m.id} is killed, ignoring"
       false
     else
-      #Redwood::log "ran #{num_queries} queries to build thread of #{messages.size} messages for #{m.id}: #{m.subj}" if num_queries > 0
+      #debug "ran #{num_queries} queries to build thread of #{messages.size} messages for #{m.id}: #{m.subj}" if num_queries > 0
       messages.each { |mid, builder| yield mid, builder }
       true
     end
@@ -285,13 +285,13 @@ class FerretIndex < BaseIndex
     end
     q.add_query Ferret::Search::TermQuery.new(:label, "spam"), :must_not
 
-    Redwood::log "contact search: #{q}"
+    debug "contact search: #{q}"
     contacts = {}
     num = h[:num] || 20
     @index_mutex.synchronize do
       @index.search_each q, :sort => "date DESC", :limit => :all do |docid, score|
         break if contacts.size >= num
-        #Redwood::log "got message #{docid} to: #{@index[docid][:to].inspect} and from: #{@index[docid][:from].inspect}"
+        #debug "got message #{docid} to: #{@index[docid][:to].inspect} and from: #{@index[docid][:from].inspect}"
         f = @index[docid][:from]
         t = @index[docid][:to]
 
@@ -381,10 +381,10 @@ class FerretIndex < BaseIndex
       field, name = $1, ($3 || $4)
       case field
       when "filename"
-        Redwood::log "filename - translated #{field}:#{name} to attachments:(#{name.downcase})"
+        debug "filename: translated #{field}:#{name} to attachments:(#{name.downcase})"
         "attachments:(#{name.downcase})"
       when "filetype"
-        Redwood::log "filetype - translated #{field}:#{name} to attachments:(*.#{name.downcase})"
+        debug "filetype: translated #{field}:#{name} to attachments:(*.#{name.downcase})"
         "attachments:(*.#{name.downcase})"
       end
     end
@@ -396,13 +396,13 @@ class FerretIndex < BaseIndex
         if realdate
           case field
           when "after"
-            Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.end}"
+            debug "chronic: translated #{field}:#{datestr} to #{realdate.end}"
             "date:(>= #{sprintf "%012d", realdate.end.to_i})"
           when "before"
-            Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
+            debug "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
             "date:(<= #{sprintf "%012d", realdate.begin.to_i})"
           else
-            Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate}"
+            debug "chronic: translated #{field}:#{datestr} to #{realdate}"
             "date:(<= #{sprintf "%012d", realdate.end.to_i}) date:(>= #{sprintf "%012d", realdate.begin.to_i})"
           end
         else
index a492a07858abbd444916e88b5f39f35867f5e5fd..0c411626c43d1ad31e94493860f919b2ebf631e5 100644 (file)
@@ -40,7 +40,7 @@ class HookManager
     end
 
     def log s
-      Redwood::log "hook[#@__name]: #{s}"
+      info "hook[#@__name]: #{s}"
     end
 
     def ask_yes_or_no q
@@ -146,7 +146,7 @@ private
   end
 
   def log m
-    Redwood::log("hook: " + m)
+    info("hook: " + m)
   end
 end
 
index 14acdb775764655152a0062f3010dc7b9d38f7b9..c2904fb8a4a8774921f0b6fe2ea66508b8c89ad9 100644 (file)
@@ -160,13 +160,13 @@ class IMAP < Source
     return if last_id == @ids.length
 
     range = (@ids.length + 1) .. last_id
-    Redwood::log "fetching IMAP headers #{range}"
+    debug "fetching IMAP headers #{range}"
     fetch(range, ['RFC822.SIZE', 'INTERNALDATE', 'FLAGS']).each do |v|
       id = make_id v
       @ids << id
       @imap_state[id] = { :id => v.seqno, :flags => v.attr["FLAGS"] }
     end
-    Redwood::log "done fetching IMAP headers"
+    debug "done fetching IMAP headers"
   end
   synchronized :scan_mailbox
 
@@ -225,7 +225,7 @@ private
     if good_results.empty?
       raise FatalSourceError, "no IMAP response for #{ids} containing all fields #{fields.join(', ')} (got #{results.size} results)"
     elsif good_results.size < results.size
-      Redwood::log "Your IMAP server sucks. It sent #{results.size} results for a request for #{good_results.size} messages. What are you using, Binc?"
+      warn "Your IMAP server sucks. It sent #{results.size} results for a request for #{good_results.size} messages. What are you using, Binc?"
     end
 
     good_results
@@ -253,12 +253,12 @@ private
           raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=CRAM-MD5"
           @imap.authenticate 'CRAM-MD5', @username, @password
         rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
-          Redwood::log "CRAM-MD5 authentication failed: #{e.class}. Trying LOGIN auth..."
+          debug "CRAM-MD5 authentication failed: #{e.class}. Trying LOGIN auth..."
           begin
             raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=LOGIN"
             @imap.authenticate 'LOGIN', @username, @password
           rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
-            Redwood::log "LOGIN authentication failed: #{e.class}. Trying plain-text LOGIN..."
+            debug "LOGIN authentication failed: #{e.class}. Trying plain-text LOGIN..."
             @imap.login @username, @password
           end
         end
@@ -275,7 +275,7 @@ private
 
   def say s
     @say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
-    Redwood::log s
+    debug s
   end
 
   def shutup
@@ -332,7 +332,7 @@ private
       rescue *RECOVERABLE_ERRORS => e
         if (retries += 1) <= 3
           @imap = nil
-          Redwood::log "got #{e.class.name}: #{e.message.inspect}"
+          warn "got #{e.class.name}: #{e.message.inspect}"
           sleep 2
           retry
         end
index 19d753e321dd5126d665db428fc18e13a7cc80f2..dfaeee819c1db70f410de829f001ccf21c19280c 100644 (file)
@@ -6,7 +6,7 @@ begin
   require 'chronic'
   $have_chronic = true
 rescue LoadError => e
-  Redwood::log "optional 'chronic' library not found (run 'gem install chronic' to install)"
+  debug "optional 'chronic' library not found; date-time query restrictions disabled"
   $have_chronic = false
 end
 
@@ -31,7 +31,7 @@ class BaseIndex
   def lockfile; File.join @dir, "lock" end
 
   def lock
-    Redwood::log "locking #{lockfile}..."
+    debug "locking #{lockfile}..."
     begin
       @lock.lock
     rescue Lockfile::MaxTriesLockError
@@ -91,7 +91,7 @@ EOS
 
   def unlock
     if @lock && @lock.locked?
-      Redwood::log "unlocking #{lockfile}..."
+      debug "unlocking #{lockfile}..."
       @lock.unlock
     end
   end
@@ -102,7 +102,7 @@ EOS
   end
 
   def save
-    Redwood::log "saving index and sources..."
+    debug "saving index and sources..."
     FileUtils.mkdir_p @dir unless File.exists? @dir
     SourceManager.save_sources
     save_index
@@ -215,6 +215,6 @@ case index_name
   else fail "unknown index type #{index_name.inspect}"
 end
 Index = Redwood.const_get "#{index_name.capitalize}Index"
-Redwood::log "using index #{Index.name}"
+debug "using index #{Index.name}"
 
 end
index 4ac6551bff21e50ab45704f295d6d930edf1ce6d..ccaeae0c0728f15c08898a948f70b64b547f708b 100644 (file)
@@ -1,54 +1,73 @@
+require "sup"
+require 'stringio'
+require 'thread'
+
 module Redwood
 
+## simple centralized logger. outputs to multiple sinks by calling << on them.
+## also keeps a record of all messages, so that adding a new sink will send all
+## previous messages to it by default.
 class Logger
-  @@instance = nil
+  include Singleton
 
-  attr_reader :buf
+  LEVELS = %w(debug info warn error) # in order!
 
-  def initialize
-    raise "only one Log can be defined" if @@instance
-    @@instance = self
-    @mode = LogMode.new
-    @respawn = true
-    @spawning = false # to prevent infinite loops!
+  def initialize level=nil
+    level ||= ENV["SUP_LOG_LEVEL"] || "info"
+    @level = LEVELS.index(level) or raise ArgumentError, "invalid log level #{level.inspect}: should be one of #{LEVELS * ', '}"
+    @mutex = Mutex.new
+    @buf = StringIO.new
+    @sinks = []
   end
 
-  ## must be called if you want to see anything!
-  ## once called, will respawn if killed...
-  def make_buf
-    return if @mode.buffer || !BufferManager.instantiated? || !@respawn || @spawning
-    @spawning = true
-    @mode.buffer = BufferManager.instance.spawn "log", @mode, :hidden => true, :system => true
-    @spawning = false
+  def level; LEVELS[@level] end
+
+  def add_sink s, copy_current=true
+    @mutex.synchronize do
+      @sinks << s
+      s << @buf.string if copy_current
+    end
   end
 
-  def log s
-#    $stderr.puts s
-    make_buf
-    prefix = "#{Time.now}: "
-    padding = " " * prefix.length
-    first = true
-    s.split(/[\r\n]/).each do |l|
-      l = l.chomp
-      if first
-        first = false
-        @mode << "#{prefix}#{l}\n"
-      else
-        @mode << "#{padding}#{l}\n"
+  def remove_sink s; @mutex.synchronize { @sinks.delete s } end
+  def remove_all_sinks!; @mutex.synchronize { @sinks.clear } end
+  def clear!; @mutex.synchronize { @buf = StringIO.new } end
+
+  LEVELS.each_with_index do |l, method_level|
+    define_method(l) do |s|
+      if method_level >= @level
+        send_message format_message(l, Time.now, s)
       end
     end
-    $stderr.puts "[#{Time.now}] #{s.chomp}" unless BufferManager.instantiated? && @mode.buffer
   end
-  
-  def self.method_missing m, *a
-    @@instance = Logger.new unless @@instance
-    @@instance.send m, *a
+
+  ## send a message regardless of the current logging level
+  def force_message m; send_message format_message(nil, Time.now, m) end
+
+private
+
+  ## level can be nil!
+  def format_message level, time, msg
+    prefix = case level
+      when "warn"; "WARNING: "
+      when "error"; "ERROR: "
+      else ""
+    end
+    "[#{time.to_s}] #{prefix}#{msg}\n"
   end
 
-  def self.buffer
-    @@instance.buf
+  ## actually distribute the message
+  def send_message m
+    @mutex.synchronize do
+      @sinks.each { |sink| sink << m }
+      @buf << m
+    end
   end
 end
 
+## include me to have top-level #debug, #info, etc. methods.
+module LogsStuff
+  Logger::LEVELS.each { |l| define_method(l) { |s| Logger.instance.send(l, s) } }
 end
 
+end
index 80903e28ca6cd6231df0563dae4977c0aa5cfe34..3661abbce40eb703c2314a37f6dd106e414ffd62 100644 (file)
@@ -115,7 +115,7 @@ class Maildir < Source
 
     initial_poll = @ids.empty?
 
-    Redwood::log "scanning maildir #@dir..."
+    debug "scanning maildir #@dir..."
     begin
       @mtimes.each_key do |d|
        subdir = File.join(@dir, d)
@@ -134,7 +134,7 @@ class Maildir < Source
            @ids_to_fns[id] = fn
          end
        else
-         Redwood::log "no poll on #{d}.  mtime on indicates no new messages."
+         debug "no poll on #{d}.  mtime on indicates no new messages."
        end
       end
       @ids = @dir_ids.values.flatten.uniq.sort!
@@ -142,7 +142,7 @@ class Maildir < Source
       raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
     end
     
-    Redwood::log "done scanning maildir"
+    debug "done scanning maildir"
     @last_scan = Time.now
   end
   synchronized :scan_mailbox
index e1e3a4d1aed75c0a3d190cdc9ce8257ff46a56e4..3f3abadefeb6f3833970abf027e6e8f77ce7dd45 100644 (file)
@@ -15,7 +15,7 @@ module MBox
       Time.parse time, 0
       true
     rescue NoMethodError
-      Redwood::log "found invalid date in potential mbox split line, not splitting: #{l.inspect}"
+      warn "found invalid date in potential mbox split line, not splitting: #{l.inspect}"
       false
     end
   end
index d47463692e07bedce205e4c3527f87f345c528c6..4ae4bbadf6f875b8655dbe5b461bca2b31afef22 100644 (file)
@@ -16,12 +16,6 @@ class SSHFileError < StandardError; end
 ## all of the methods here can throw SSHFileErrors, SocketErrors,
 ## Net::SSH::Exceptions and Errno::ENOENTs.
 
-## debugging TODO: remove me
-def debug s
-  Redwood::log s
-end
-module_function :debug
-
 ## a simple buffer of contiguous data
 class Buffer
   def initialize
@@ -154,7 +148,7 @@ private
   ## TODO: share this code with imap
   def say s
     @say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
-    Redwood::log s
+    info s
   end
 
   def shutup
index 0d742d99e746a9ce96a4c11eddcd57506fcae22f..1eda17438df45e08eaa6f5269fca073e1714bb19 100644 (file)
@@ -132,7 +132,7 @@ EOS
     def viewable?; @lines.nil? end
     def view_default! path
       cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}' 2>/dev/null"
-      Redwood::log "running: #{cmd.inspect}"
+      debug "running: #{cmd.inspect}"
       system cmd
       $? == 0
     end
index 1e9c659c9a28930ec3567c9880e61c2ebf127669..ed27d3dccfba056410bd90225604479f8e788797 100644 (file)
@@ -73,7 +73,7 @@ class Message
     else
       id = "sup-faked-" + Digest::MD5.hexdigest(raw_header)
       from = header["from"]
-      #Redwood::log "faking non-existent message-id for message from #{from}: #{id}"
+      #debug "faking non-existent message-id for message from #{from}: #{id}"
       id
     end
 
@@ -81,7 +81,7 @@ class Message
       header["from"]
     else
       name = "Sup Auto-generated Fake Sender <sup@fake.sender.example.com>"
-      #Redwood::log "faking non-existent sender for message #@id: #{name}"
+      #debug "faking non-existent sender for message #@id: #{name}"
       name
     end)
 
@@ -92,11 +92,11 @@ class Message
       begin
         Time.parse date
       rescue ArgumentError => e
-        #Redwood::log "faking mangled date header for #{@id} (orig #{header['date'].inspect} gave error: #{e.message})"
+        #debug "faking mangled date header for #{@id} (orig #{header['date'].inspect} gave error: #{e.message})"
         Time.now
       end
     else
-      #Redwood::log "faking non-existent date header for #{@id}"
+      #debug "faking non-existent date header for #{@id}"
       Time.now
     end
 
@@ -210,7 +210,7 @@ class Message
           parse_header @source.load_header(@source_info)
           message_to_chunks @source.load_message(@source_info)
         rescue SourceError, SocketError => e
-          Redwood::log "problem getting messages from #{@source}: #{e.message}"
+          warn "problem getting messages from #{@source}: #{e.message}"
           ## we need force_to_top here otherwise this window will cover
           ## up the error message one
           @source.error ||= e
@@ -244,7 +244,7 @@ EOS
     begin
       yield
     rescue SourceError => e
-      Redwood::log "problem getting messages from #{@source}: #{e.message}"
+      warn "problem getting messages from #{@source}: #{e.message}"
       @source.error ||= e
       Redwood::report_broken_sources :force_to_top => true
       error_message e.message
@@ -335,25 +335,25 @@ private
 
   def multipart_signed_to_chunks m
     if m.body.size != 2
-      Redwood::log "warning: multipart/signed with #{m.body.size} parts (expecting 2)"
+      warn "multipart/signed with #{m.body.size} parts (expecting 2)"
       return
     end
 
     payload, signature = m.body
     if signature.multipart?
-      Redwood::log "warning: multipart/signed with payload multipart #{payload.multipart?} and signature multipart #{signature.multipart?}"
+      warn "multipart/signed with payload multipart #{payload.multipart?} and signature multipart #{signature.multipart?}"
       return
     end
 
     ## this probably will never happen
     if payload.header.content_type == "application/pgp-signature"
-      Redwood::log "warning: multipart/signed with payload content type #{payload.header.content_type}"
+      warn "multipart/signed with payload content type #{payload.header.content_type}"
       return
     end
 
     if signature.header.content_type != "application/pgp-signature"
       ## unknown signature type; just ignore.
-      #Redwood::log "warning: multipart/signed with signature content type #{signature.header.content_type}"
+      #warn "multipart/signed with signature content type #{signature.header.content_type}"
       return
     end
 
@@ -362,23 +362,23 @@ private
 
   def multipart_encrypted_to_chunks m
     if m.body.size != 2
-      Redwood::log "warning: multipart/encrypted with #{m.body.size} parts (expecting 2)"
+      warn "multipart/encrypted with #{m.body.size} parts (expecting 2)"
       return
     end
 
     control, payload = m.body
     if control.multipart?
-      Redwood::log "warning: multipart/encrypted with control multipart #{control.multipart?} and payload multipart #{payload.multipart?}"
+      warn "multipart/encrypted with control multipart #{control.multipart?} and payload multipart #{payload.multipart?}"
       return
     end
 
     if payload.header.content_type != "application/octet-stream"
-      Redwood::log "warning: multipart/encrypted with payload content type #{payload.header.content_type}"
+      warn "multipart/encrypted with payload content type #{payload.header.content_type}"
       return
     end
 
     if control.header.content_type != "application/pgp-encrypted"
-      Redwood::log "warning: multipart/encrypted with control content type #{signature.header.content_type}"
+      warn "multipart/encrypted with control content type #{signature.header.content_type}"
       return
     end
 
index 6433492c4d50acb187c1ae426620fde6a13b36be..209ca45e44bb36333159627335bd64dd7f8b50ea 100644 (file)
@@ -92,10 +92,10 @@ EOS
       unless err.empty?
         message = err.first.read
         if message =~ /^\s*$/
-          Redwood::log "error running #{command} (but no error message)"
+          warn "error running #{command} (but no error message)"
           BufferManager.flash "Error running #{command}!"
         else
-          Redwood::log "error running #{command}: #{message}"
+          warn "error running #{command}: #{message}"
           BufferManager.flash "Error: #{message}"
         end
         return
index a48930aea521fd73d7c83f2bf9846d8be6a29025..8da316fe00593f8085adb167e0200b5a6c650d07 100644 (file)
@@ -325,7 +325,7 @@ protected
       BufferManager.flash "Message sent!"
       true
     rescue SystemCallError, SendmailCommandFailed, CryptoManager::Error => e
-      Redwood::log "Problem sending mail: #{e.message}"
+      warn "Problem sending mail: #{e.message}"
       BufferManager.flash "Problem sending mail: #{e.message}"
       false
     end
index 53287c1156611dc835ab87c571a27b52d7c092b6..f65ec2e2080f2f823b1fa102fb77d2cc5f4cbae5 100644 (file)
@@ -73,7 +73,7 @@ protected
       ##   TODO make the labelmanager responsible for label counts
       ## and then it can listen to labeled and unlabeled events, etc.
       if total == 0 && !LabelManager::RESERVED_LABELS.include?(label) && !LabelManager.new_label?(label)
-        Redwood::log "no hits for label #{label}, deleting"
+        debug "no hits for label #{label}, deleting"
         LabelManager.delete label
         next
       end
index de16b5e6635b988fa90329b7d66ef046d3793372..07fa9dd6cda8d4780676119839ee053baee41560 100644 (file)
@@ -1,36 +1,40 @@
+require 'stringio'
 module Redwood
 
+## a variant of text mode that allows the user to automatically follow text,
+## and respawns when << is called if necessary.
+
 class LogMode < TextMode
   register_keymap do |k|
     k.add :toggle_follow, "Toggle follow mode", 'f'
   end
 
-  def initialize
+  def initialize buffer_name
     @follow = true
-    super
+    @buffer_name = buffer_name
+    @on_kill = []
+    super()
   end
 
+  ## register callbacks for when the buffer is killed
+  def on_kill &b; @on_kill << b end
+
   def toggle_follow
     @follow = !@follow
-    if buffer
-      if @follow
-        jump_to_line lines - buffer.content_height + 1 # leave an empty line at bottom
-      end
-      buffer.mark_dirty
+    if @follow
+      jump_to_line(lines - buffer.content_height + 1) # leave an empty line at bottom
     end
+    buffer.mark_dirty
   end
 
-  def text= t
-    super
-    if buffer && @follow
-      follow_top = lines - buffer.content_height + 1
-      jump_to_line follow_top if topline < follow_top
+  def << s
+    unless buffer
+      BufferManager.spawn @buffer_name, self, :hidden => true, :system => true
     end
-  end
 
-  def << line
-    super
-    if buffer && @follow
+    s.split("\n").each { |l| super(l + "\n") } # insane. different << semantics.
+
+    if @follow
       follow_top = lines - buffer.content_height + 1
       jump_to_line follow_top if topline < follow_top
     end
@@ -39,6 +43,12 @@ class LogMode < TextMode
   def status
     super + " (follow: #@follow)"
   end
+
+  def cleanup
+    @on_kill.each { |cb| cb.call self }
+    self.text = ""
+    super
+  end
 end
 
 end
index 5849f3eed922e96a59519c2e99d972e8a0e98b4a..cf6134302a4992dca05557753ded1d5792df166c 100644 (file)
@@ -3,18 +3,16 @@ module Redwood
 class PollMode < LogMode
   def initialize
     @new = true
-    super
-  end
-
-  def puts s=""
-    self << s + "\n"
+    super "poll for new messages"
   end
 
   def poll
-    puts unless @new
-    @new = false
-    puts "Poll started at #{Time.now}"
-    PollManager.do_poll { |s| puts s }
+    unless @new
+      @new = false
+      self << "\n"
+    end
+    self << "Poll started at #{Time.now}\n"
+    PollManager.do_poll { |s| self << (s + "\n") }
   end
 end
 
index c79c5dbe51dd59df0426572c523a2b79492f524f..700dfc1f99d310a0290e0bfcab4e8478957ca678 100644 (file)
@@ -56,7 +56,7 @@ EOS
     ## don't check that it's an Account, though; assume they know what they're
     ## doing.
     if hook_reply_from && !(hook_reply_from.is_a? Person)
-      Redwood::log "reply-from returned non-Person, using default from."
+      info "reply-from returned non-Person, using default from."
       hook_reply_from = nil
     end
 
index 737f6f1ff309a5bd87ebdeb1d213c77c88b86bc2..bac4792e832daafb1ba9964d0e3cb2387c1bc9b6 100644 (file)
@@ -212,14 +212,14 @@ EOS
     bt = to.size > 1 ? "#{to.size} recipients" : to.to_s
 
     if BufferManager.ask_yes_or_no "Really bounce to #{bt}?"
-      Redwood::log "Bounce Command: #{cmd}"
+      debug "bounce command: #{cmd}"
       begin
         IO.popen(cmd, 'w') do |sm|
           sm.puts m.raw_message
         end
         raise SendmailCommandFailed, "Couldn't execute #{cmd}" unless $? == 0
       rescue SystemCallError, SendmailCommandFailed => e
-        Redwood::log "Problem sending mail: #{e.message}"
+        warn "problem sending mail: #{e.message}"
         BufferManager.flash "Problem sending mail: #{e.message}"
       end
     end
index a36a4c44721dd324d57c72792a15a08247db8f7a..c348fb2296cf152f408d72360411c2537cd7716f 100644 (file)
@@ -35,20 +35,17 @@ EOS
     @thread = nil
     @last_poll = nil
     @polling = false
-  end
-
-  def buffer
-    b, new = BufferManager.spawn_unless_exists("poll for new messages", :hidden => true, :system => true) { PollMode.new }
-    b
+    @mode = nil
   end
 
   def poll
     return if @polling
     @polling = true
+    @mode ||= PollMode.new
     HookManager.run "before-poll"
 
     BufferManager.flash "Polling for new messages..."
-    num, numi, from_and_subj, from_and_subj_inbox = buffer.mode.poll
+    num, numi, from_and_subj, from_and_subj_inbox = @mode.poll
     if num > 0
       BufferManager.flash "Loaded #{num.pluralize 'new message'}, #{numi} to inbox." 
     else
@@ -86,7 +83,7 @@ EOS
         begin
           yield "Loading from #{source}... " unless source.done? || (source.respond_to?(:has_errors?) && source.has_errors?)
         rescue SourceError => e
-          Redwood::log "problem getting messages from #{source}: #{e.message}"
+          warn "problem getting messages from #{source}: #{e.message}"
           Redwood::report_broken_sources :force_to_top => true
           next
         end
@@ -142,7 +139,7 @@ EOS
 
       source.each do |offset, source_labels|
         if source.has_errors?
-          Redwood::log "error loading messages from #{source}: #{source.error.message}"
+          warn "error loading messages from #{source}: #{source.error.message}"
           return
         end
 
@@ -155,7 +152,7 @@ EOS
         yield m
       end
     rescue SourceError => e
-      Redwood::log "problem getting messages from #{source}: #{e.message}"
+      warn "problem getting messages from #{source}: #{e.message}"
       Redwood::report_broken_sources :force_to_top => true
     end
   end
index d381395104a5575e1011c88560021cc2c0820764..9203dd666816a770d8537c18c6281ae8f17ed14a 100644 (file)
@@ -8,7 +8,6 @@ class SentManager
   def initialize source_uri
     @source = nil
     @source_uri = source_uri
-    Redwood::log "SentManager intialized with source uri: #@source_uri"
   end
 
   def source_id; @source.id; end
@@ -21,7 +20,6 @@ class SentManager
 
   def default_source
     @source = Recoverable.new SentLoader.new
-    Redwood::log "SentManager initializing default source: #@source."
     @source_uri = @source.uri
     @source
   end
index 7c9a2657316752019683780bb14ced56c26b61ba..8154591f6adce27c071e16d11784f8dd77b05888 100644 (file)
@@ -139,7 +139,7 @@ class Source
       header[k] = begin
         Rfc2047.decode_to $encoding, v
       rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e
-        #Redwood::log "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
+        #debug "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
         v
       end
     end
index c748c7a653a6a3405a089c2cfe4149a17ba82de4..b8dec59d368af5508ee85b40ae78a3dade452f83 100644 (file)
@@ -112,11 +112,11 @@ class TextField
         unless @history.empty?
           value = get_cursed_value
           @i ||= @history.size
-          #Redwood::log "history before #{@history.inspect}"
+          #debug "history before #{@history.inspect}"
           @history[@i] = value #unless value =~ /^\s*$/
           @i = (@i + (c == Ncurses::KEY_UP ? -1 : 1)) % @history.size
           @value = @history[@i]
-          #Redwood::log "history after #{@history.inspect}"
+          #debug "history after #{@history.inspect}"
           set_cursed_value @value
           Ncurses::Form::REQ_END_FIELD
         end
index 5d57361e990da24abe6c470ded4fa31a3800f51b..3f6b8402b88c85a54e793d2edf57ee111ed797cd 100644 (file)
@@ -653,7 +653,7 @@ class Iconv
     begin
       Iconv.iconv(target + "//IGNORE", charset, text + " ").join[0 .. -2]
     rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::InvalidCharacter, Iconv::IllegalSequence => e
-      Redwood::log "warning: error (#{e.class.name}) decoding text from #{charset} to #{target}: #{text[0 ... 20]}"
+      info "couldn't transcode text from #{charset} to #{target} (\"#{text[0 ... 20]}\"...)"
       text
     end
   end
index f79b05503f914d7b3c28dd3d030615402847d88d..dbf66431bc263ada5420b9a981f766c9ca5675d4 100644 (file)
@@ -221,10 +221,10 @@ class XapianIndex < BaseIndex
       field, name = $1, ($3 || $4)
       case field
       when "filename"
-        Redwood::log "filename - translated #{field}:#{name} to attachment:\"#{name.downcase}\""
+        debug "filename: translated #{field}:#{name} to attachment:\"#{name.downcase}\""
         "attachment:\"#{name.downcase}\""
       when "filetype"
-        Redwood::log "filetype - translated #{field}:#{name} to attachment_extension:#{name.downcase}"
+        debug "filetype: translated #{field}:#{name} to attachment_extension:#{name.downcase}"
         "attachment_extension:#{name.downcase}"
       end
     end
@@ -238,13 +238,13 @@ class XapianIndex < BaseIndex
         if realdate
           case field
           when "after"
-            Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.end}"
+            debug "chronic: translated #{field}:#{datestr} to #{realdate.end}"
             "date:#{realdate.end.to_i}..#{lastdate}"
           when "before"
-            Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
+            debug "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
             "date:#{firstdate}..#{realdate.end.to_i}"
           else
-            Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate}"
+            debug "chronic: translated #{field}:#{datestr} to #{realdate}"
             "date:#{realdate.begin.to_i}..#{realdate.end.to_i}"
           end
         else
@@ -413,10 +413,10 @@ class XapianIndex < BaseIndex
     m.attachments.each { |a| text << [a, PREFIX['attachment']] }
 
     truncated_date = if m.date < MIN_DATE
-      Redwood::log "warning: adjusting too-low date #{m.date} for indexing"
+      debug "warning: adjusting too-low date #{m.date} for indexing"
       MIN_DATE
     elsif m.date > MAX_DATE
-      Redwood::log "warning: adjusting too-high date #{m.date} for indexing"
+      debug "warning: adjusting too-high date #{m.date} for indexing"
       MAX_DATE
     else
       m.date