]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/logger.rb
fix: restart logger thread after fork
[user/henk/code/ruby/rbot.git] / lib / rbot / logger.rb
1 # encoding: UTF-8
2 #-- vim:sw=2:et
3 #++
4 #
5 # :title: rbot logger
6
7 require 'logger'
8 require 'thread'
9 require 'singleton'
10
11 module Irc
12 class Bot
13
14   class LoggerManager
15     include Singleton
16
17     def initialize
18       @dateformat = "%Y/%m/%d %H:%M:%S"
19
20       @logger = Logger.new(STDERR)
21       @logger.datetime_format = @dateformat
22       @logger.level = Logger::Severity::DEBUG
23       @file_logger = nil
24
25       @queue = Queue.new
26       start_thread
27     end
28
29     def set_logfile(filename, keep, max_size)
30       @file_logger = Logger.new(filename, keep, max_size*1024*1024)
31       @file_logger.datetime_format = @dateformat
32       @file_logger.level = @logger.level
33       # make sure the thread is running, which might be false after a fork
34       # (conveniently, we call set_logfile right after the fork)
35       start_thread
36     end
37
38     def set_level(level)
39       @logger.level = level
40       if @file_logger
41         @file_logger.level = level
42       end
43     end
44
45     def sync_log(severity, message = nil, progname = nil)
46       @logger.add(severity, message, progname)
47       if @file_logger
48         @file_logger.add(severity, message, progname)
49       end
50     end
51
52     def async_log(severity, message=nil, who_pos=1)
53       unless @thread
54         STDERR.puts('logger thread already destroyed, cannot log message!')
55       end
56
57       call_stack = caller
58       if call_stack.length > who_pos
59         who = call_stack[who_pos].sub(%r{(?:.+)/([^/]+):(\d+)(:in .*)?}) { "#{$1}:#{$2}#{$3}" }
60       else
61         who = "(unknown)"
62       end
63       # Output each line. To distinguish between separate messages and multi-line
64       # messages originating at the same time, we blank #{who} after the first message
65       # is output.
66       # Also, we output strings as-is but for other objects we use pretty_inspect
67       message = message.kind_of?(String) ? message : (message.pretty_inspect rescue '?')
68       qmsg = Array.new
69       message.each_line { |l|
70         qmsg.push [severity, l.chomp, who]
71         who = ' ' * who.size
72       }
73       @queue.push qmsg
74     end
75
76     def log_session_start
77       if @file_logger
78         @file_logger << "\n\n=== session started on #{Time.now.strftime(@dateformat)} ===\n\n"
79       end
80     end
81
82     def log_session_end
83       if @file_logger
84         @file_logger << "\n\n=== session ended on #{Time.now.strftime(@dateformat)} ===\n\n"
85       end
86     end
87
88     def halt_logger
89       if @thread and @thread.alive?
90         @queue << nil
91         @thread.join
92         @thread = nil
93       end
94     end
95
96     def flush
97       while @queue.size > 0
98         next
99       end
100     end
101
102     private
103
104     def start_thread
105       return if @thread and @thread.alive?
106       @thread = Thread.new do
107         lines = nil
108         while lines = @queue.pop
109           lines.each { |line|
110             sync_log(*line)
111           }
112         end
113       end
114     end
115
116   end
117
118 end
119 end
120
121 def debug(message=nil, who_pos=1)
122   Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::DEBUG, message, who_pos)
123 end
124
125 def log(message=nil, who_pos=1)
126   Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::INFO, message, who_pos)
127 end
128
129 def warning(message=nil, who_pos=1)
130   Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::WARN, message, who_pos)
131 end
132
133 def error(message=nil, who_pos=1)
134   Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::ERROR, message, who_pos)
135 end
136
137 def fatal(message=nil, who_pos=1)
138   Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::FATAL, message, who_pos)
139 end