2 # Copyright (c) 2011, 2012, 2013 Nicira, Inc.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 import logging.handlers
27 FACILITIES = {"console": "info", "file": "info", "syslog": "info"}
31 "warn": logging.WARNING,
33 "emer": logging.CRITICAL,
34 "off": logging.CRITICAL
38 def get_level(level_str):
39 return LEVELS.get(level_str.lower())
45 __mfl = {} # Module -> facility -> level
49 def __init__(self, name):
50 """Creates a new Vlog object representing a module called 'name'. The
51 created Vlog object will do nothing until the Vlog.init() static method
52 is called. Once called, no more Vlog objects may be created."""
54 assert not Vlog.__inited
55 self.name = name.lower()
56 if name not in Vlog.__mfl:
57 Vlog.__mfl[self.name] = FACILITIES.copy()
59 def __log(self, level, message, **kwargs):
63 dt = datetime.datetime.utcnow();
64 now = dt.strftime("%Y-%m-%dT%H:%M:%S.%%03iZ") % (dt.microsecond/1000)
65 syslog_message = ("%s|%s|%s|%s"
66 % (Vlog.__msg_num, self.name, level, message))
68 level = LEVELS.get(level.lower(), logging.DEBUG)
71 for f, f_level in Vlog.__mfl[self.name].iteritems():
72 f_level = LEVELS.get(f_level, logging.CRITICAL)
75 message = "ovs|" + syslog_message
77 message = "%s|%s" % (now, syslog_message)
78 logging.getLogger(f).log(level, message, **kwargs)
80 def emer(self, message, **kwargs):
81 self.__log("EMER", message, **kwargs)
83 def err(self, message, **kwargs):
84 self.__log("ERR", message, **kwargs)
86 def warn(self, message, **kwargs):
87 self.__log("WARN", message, **kwargs)
89 def info(self, message, **kwargs):
90 self.__log("INFO", message, **kwargs)
92 def dbg(self, message, **kwargs):
93 self.__log("DBG", message, **kwargs)
95 def __is_enabled(self, level):
96 level = LEVELS.get(level.lower(), logging.DEBUG)
97 for f, f_level in Vlog.__mfl[self.name].iteritems():
98 f_level = LEVELS.get(f_level, logging.CRITICAL)
103 def emer_is_enabled(self):
104 return self.__is_enabled("EMER")
106 def err_is_enabled(self):
107 return self.__is_enabled("ERR")
109 def warn_is_enabled(self):
110 return self.__is_enabled("WARN")
112 def info_is_enabled(self):
113 return self.__is_enabled("INFO")
115 def dbg_is_enabled(self):
116 return self.__is_enabled("DBG")
118 def exception(self, message):
119 """Logs 'message' at ERR log level. Includes a backtrace when in
120 exception context."""
121 self.err(message, exc_info=True)
124 def init(log_file=None):
125 """Intializes the Vlog module. Causes Vlog to write to 'log_file' if
126 not None. Should be called after all Vlog objects have been created.
127 No logging will occur until this function is called."""
133 logging.raiseExceptions = False
134 Vlog.__log_file = log_file
136 logger = logging.getLogger(f)
137 logger.setLevel(logging.DEBUG)
141 logger.addHandler(logging.StreamHandler(sys.stderr))
143 logger.addHandler(logging.handlers.SysLogHandler(
145 facility=logging.handlers.SysLogHandler.LOG_DAEMON))
146 elif f == "file" and Vlog.__log_file:
147 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
148 logger.addHandler(Vlog.__file_handler)
149 except (IOError, socket.error):
150 logger.setLevel(logging.CRITICAL)
152 ovs.unixctl.command_register("vlog/reopen", "", 0, 0,
153 Vlog._unixctl_vlog_reopen, None)
154 ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxint,
155 Vlog._unixctl_vlog_set, None)
156 ovs.unixctl.command_register("vlog/list", "", 0, 0,
157 Vlog._unixctl_vlog_list, None)
160 def set_level(module, facility, level):
161 """ Sets the log level of the 'module'-'facility' tuple to 'level'.
162 All three arguments are strings which are interpreted the same as
163 arguments to the --verbose flag. Should be called after all Vlog
164 objects have already been created."""
166 module = module.lower()
167 facility = facility.lower()
168 level = level.lower()
170 if facility != "any" and facility not in FACILITIES:
173 if module != "any" and module not in Vlog.__mfl:
176 if level not in LEVELS:
180 modules = Vlog.__mfl.keys()
184 if facility == "any":
185 facilities = FACILITIES.keys()
187 facilities = [facility]
191 Vlog.__mfl[m][f] = level
194 def set_levels_from_string(s):
199 for word in [w.lower() for w in re.split('[ :]', s)]:
202 elif word in FACILITIES:
204 return "cannot specify multiple facilities"
208 return "cannot specify multiple levels"
210 elif word in Vlog.__mfl:
212 return "cannot specify multiple modules"
215 return "no facility, level, or module \"%s\"" % word
217 Vlog.set_level(module or "any", facility or "any", level or "any")
221 lines = [" console syslog file\n",
222 " ------- ------ ------\n"]
223 lines.extend(sorted(["%-16s %4s %4s %4s\n"
225 Vlog.__mfl[m]["console"],
226 Vlog.__mfl[m]["syslog"],
227 Vlog.__mfl[m]["file"]) for m in Vlog.__mfl]))
228 return ''.join(lines)
231 def reopen_log_file():
232 """Closes and then attempts to re-open the current log file. (This is
233 useful just after log rotation, to ensure that the new log file starts
237 logger = logging.getLogger("file")
238 logger.removeHandler(Vlog.__file_handler)
239 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
240 logger.addHandler(Vlog.__file_handler)
243 def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
245 Vlog.reopen_log_file()
248 conn.reply("Logging to file not configured")
251 def _unixctl_vlog_set(conn, argv, unused_aux):
253 msg = Vlog.set_levels_from_string(arg)
260 def _unixctl_vlog_list(conn, unused_argv, unused_aux):
261 conn.reply(Vlog.get_levels())
263 def add_args(parser):
264 """Adds vlog related options to 'parser', an ArgumentParser object. The
265 resulting arguments parsed by 'parser' should be passed to handle_args."""
267 group = parser.add_argument_group(title="Logging Options")
268 group.add_argument("--log-file", nargs="?", const="default",
269 help="Enables logging to a file. Default log file"
270 " is used if LOG_FILE is omitted.")
271 group.add_argument("-v", "--verbose", nargs="*",
272 help="Sets logging levels, see ovs-vswitchd(8)."
276 def handle_args(args):
277 """ Handles command line arguments ('args') parsed by an ArgumentParser.
278 The ArgumentParser should have been primed by add_args(). Also takes care
279 of initializing the Vlog module."""
281 log_file = args.log_file
282 if log_file == "default":
283 log_file = "%s/%s.log" % (ovs.dirs.LOGDIR, ovs.util.PROGRAM_NAME)
285 if args.verbose is None:
287 elif args.verbose == []:
288 args.verbose = ["any:any:dbg"]
290 for verbose in args.verbose:
291 msg = Vlog.set_levels_from_string(verbose)
293 ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg))