summaryrefslogtreecommitdiff
path: root/lib/rbot/logger.rb
blob: fd6d485fea3675b144a63e750f3c0ea7adefe713 (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
# 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 initialize
      @dateformat = "%Y/%m/%d %H:%M:%S"

      @logger = Logger.new(STDERR)
      @logger.datetime_format = @dateformat
      @logger.level = Logger::Severity::DEBUG
      @file_logger = nil

      @queue = Queue.new
      start_thread
    end

    def set_logfile(filename, keep, max_size)
      @file_logger = Logger.new(filename, keep, max_size*1024*1024)
      @file_logger.datetime_format = @dateformat
      @file_logger.level = @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)
      @logger.level = level
      if @file_logger
        @file_logger.level = level
      end
    end

    def sync_log(severity, message = nil, progname = nil)
      @logger.add(severity, message, progname)
      if @file_logger
        @file_logger.add(severity, message, progname)
      end
    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
      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