Update version according to OSCI-856
[packages/precise/mcollective.git] / lib / mcollective / log.rb
1 module MCollective
2   # A simple class that allows logging at various levels.
3   class Log
4     class << self
5       @logger = nil
6
7       VALID_LEVELS = [:error, :fatal, :debug, :warn, :info]
8
9       # Obtain the class name of the currently configured logger
10       def logger
11         @logger.class
12       end
13
14       # Logs at info level
15       def info(msg)
16         log(:info, msg)
17       end
18
19       # Logs at warn level
20       def warn(msg)
21         log(:warn, msg)
22       end
23
24       # Logs at debug level
25       def debug(msg)
26         log(:debug, msg)
27       end
28
29       # Logs at fatal level
30       def fatal(msg)
31         log(:fatal, msg)
32       end
33
34       # Logs at error level
35       def error(msg)
36         log(:error, msg)
37       end
38
39       # handle old code that relied on this class being a singleton
40       def instance
41         self
42       end
43
44       # increments the active log level
45       def cycle_level
46         @logger.cycle_level if @configured
47       end
48
49       def config_and_check_level(level)
50         configure unless @configured
51         check_level(level)
52         @logger.should_log?(level)
53       end
54
55       def check_level(level)
56         raise "Unknown log level" unless valid_level?(level)
57       end
58
59       def valid_level?(level)
60         VALID_LEVELS.include?(level)
61       end
62
63       def message_for(msgid, args={})
64         "%s: %s" % [msgid, Util.t(msgid, args)]
65       end
66
67       def logexception(msgid, level, e, backtrace=false, args={})
68         return false unless config_and_check_level(level)
69
70         path, line, method = e.backtrace[1].split(/:(\d+)/)
71         origin = "%s:%s%s" % [File.basename(path), line, method]
72
73         if e.is_a?(CodedError)
74           msg = "%s: %s" % [e.code, e.to_s]
75         else
76           error_string = "%s: %s" % [e.class, e.to_s]
77           msg = message_for(msgid, args.merge(:error => error_string))
78         end
79
80         log(level, msg, origin)
81
82         if backtrace
83           e.backtrace.each do |line|
84             log(level, "%s:          %s" % [msgid, line], origin)
85           end
86         end
87       end
88
89       # Logs a message at a certain level, the message must be
90       # a token that will be looked up from the i18n localization
91       # database
92       #
93       # Messages can interprolate strings from the args hash, a
94       # message with "foo %{bar}" in the localization database
95       # will use args[:bar] for the value there, the interprolation
96       # is handled by the i18n library itself
97       def logmsg(msgid, default, level, args={})
98         return false unless config_and_check_level(level)
99
100         msg = message_for(msgid, {:default => default}.merge(args))
101
102         log(level, msg)
103       end
104
105       # logs a message at a certain level
106       def log(level, msg, origin=nil)
107         return unless config_and_check_level(level)
108
109         origin = from unless origin
110
111         if @logger
112           @logger.log(level, origin, msg)
113         else
114           t = Time.new.strftime("%H:%M:%S")
115
116           STDERR.puts "#{t}: #{level}: #{origin}: #{msg}"
117         end
118       end
119
120       # sets the logger class to use
121       def set_logger(logger)
122         @logger = logger
123       end
124
125       # configures the logger class, if the config has not yet been loaded
126       # we default to the console logging class and do not set @configured
127       # so that future calls to the log method will keep attempting to configure
128       # the logger till we eventually get a logging preference from the config
129       # module
130       def configure(logger=nil)
131         unless logger
132           logger_type = "console"
133
134           config = Config.instance
135
136           if config.configured
137             logger_type = config.logger_type
138             @configured = true
139           end
140
141           require "mcollective/logger/%s_logger" % logger_type.downcase
142
143           logger_class = MCollective::Logger.const_get("%s_logger" % logger_type.capitalize)
144
145           set_logger(logger_class.new)
146         else
147           set_logger(logger)
148           @configured = true
149         end
150
151
152         @logger.start
153       rescue Exception => e
154         @configured = false
155         STDERR.puts "Could not start logger: #{e.class} #{e}"
156       end
157
158       def unconfigure
159         @configured = false
160         set_logger(nil)
161       end
162
163       # figures out the filename that called us
164       def from
165         path, line, method = execution_stack[3].split(/:(\d+)/)
166         "%s:%s%s" % [File.basename(path), line, method]
167       end
168
169       # this method is here to facilitate testing
170       def execution_stack
171         caller
172       end
173     end
174   end
175 end