summaryrefslogtreecommitdiff
path: root/lib/rbot/logger.rb
blob: 4189151c77f9b5a3477f869b3f4e40ca636da091 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# encoding: UTF-8
#-- vim:sw=2:et
#++
#
# :title: rbot logger

require 'logger'
require 'thread'
require 'singleton'

module Irc
class Bot

  class LoggerManager
    include Singleton

    def enable_console_logger
      @console_logger = Logger.new(STDERR)
      @console_logger.datetime_format = @dateformat
      @console_logger.level = Logger::Severity::DEBUG
    end

    def disable_console_logger
      @console_logger.close if @console_logger
      @console_logger = nil
    end

    def initialize
      @dateformat = "%Y/%m/%d %H:%M:%S"

      enable_console_logger

      @file_logger = nil

      @queue = Queue.new
      start_thread
    end

    def set_logfile(filename, keep, max_size)
      # close previous file logger if present
      @file_logger.close if @file_logger

      @file_logger = Logger.new(filename, keep, max_size*1024*1024)
      @file_logger.datetime_format = @dateformat
      @file_logger.level = @console_logger.level

      # make sure the thread is running, which might be false after a fork
      # (conveniently, we call set_logfile right after the fork)
      start_thread
    end

    def set_level(level)
      @console_logger.level = level if @console_logger
      @file_logger.level = level if @file_logger
    end

    def sync_log(severity, message = nil, progname = nil)
      @console_logger.add(severity, message, progname) if @console_logger
      @file_logger.add(severity, message, progname) if @file_logger
    end

    def async_log(severity, message=nil, who_pos=1)
      unless @thread
        STDERR.puts('logger thread already destroyed, cannot log message!')
      end

      call_stack = caller
      if call_stack.length > who_pos
        who = call_stack[who_pos].sub(%r{(?:.+)/([^/]+):(\d+)(:in .*)?}) { "#{$1}:#{$2}#{$3}" }
      else
        who = "(unknown)"
      end
      # Output each line. To distinguish between separate messages and multi-line
      # messages originating at the same time, we blank #{who} after the first message
      # is output.
      # Also, we output strings as-is but for other objects we use pretty_inspect
      message = message.kind_of?(String) ? message : (message.pretty_inspect rescue '?')
      qmsg = Array.new
      message.each_line { |l|
        qmsg.push [severity, l.chomp, who]
        who = ' ' * who.size
      }
      @queue.push qmsg
    end

    def log_session_start
      if @file_logger
        @file_logger << "\n\n=== session started on #{Time.now.strftime(@dateformat)} ===\n\n"
      end
    end

    def log_session_end
      if @file_logger
        @file_logger << "\n\n=== session ended on #{Time.now.strftime(@dateformat)} ===\n\n"
      end
    end

    def halt_logger
      if @thread and @thread.alive?
        @queue << nil
        @thread.join
        @thread = nil
        log_session_end
      end
    end

    def flush
      while @queue.size > 0
        next
      end
    end

    private

    def start_thread
      return if @thread and @thread.alive?
      @thread = Thread.new do
        lines = nil
        while lines = @queue.pop
          lines.each { |line|
            sync_log(*line)
          }
        end
      end
    end

  end

end
end

def debug(message=nil, who_pos=1)
  Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::DEBUG, message, who_pos)
end

def log(message=nil, who_pos=1)
  Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::INFO, message, who_pos)
end

def warning(message=nil, who_pos=1)
  Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::WARN, message, who_pos)
end

def error(message=nil, who_pos=1)
  Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::ERROR, message, who_pos)
end

def fatal(message=nil, who_pos=1)
  Irc::Bot::LoggerManager.instance.async_log(Logger::Severity::FATAL, message, who_pos)
end