2 # Copyright (c) 2011, 2012 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 now = datetime.datetime.now().strftime("%b %d %H:%M:%S")
64 message = ("%s|%s|%s|%s|%s"
65 % (now, Vlog.__msg_num, self.name, level, message))
67 level = LEVELS.get(level.lower(), logging.DEBUG)
70 for f, f_level in Vlog.__mfl[self.name].iteritems():
71 f_level = LEVELS.get(f_level, logging.CRITICAL)
73 logging.getLogger(f).log(level, message, **kwargs)
75 def emer(self, message, **kwargs):
76 self.__log("EMER", message, **kwargs)
78 def err(self, message, **kwargs):
79 self.__log("ERR", message, **kwargs)
81 def warn(self, message, **kwargs):
82 self.__log("WARN", message, **kwargs)
84 def info(self, message, **kwargs):
85 self.__log("INFO", message, **kwargs)
87 def dbg(self, message, **kwargs):
88 self.__log("DBG", message, **kwargs)
90 def exception(self, message):
91 """Logs 'message' at ERR log level. Includes a backtrace when in
93 self.err(message, exc_info=True)
96 def init(log_file=None):
97 """Intializes the Vlog module. Causes Vlog to write to 'log_file' if
98 not None. Should be called after all Vlog objects have been created.
99 No logging will occur until this function is called."""
105 logging.raiseExceptions = False
106 Vlog.__log_file = log_file
108 logger = logging.getLogger(f)
109 logger.setLevel(logging.DEBUG)
113 logger.addHandler(logging.StreamHandler(sys.stderr))
115 logger.addHandler(logging.handlers.SysLogHandler(
117 facility=logging.handlers.SysLogHandler.LOG_DAEMON))
118 elif f == "file" and Vlog.__log_file:
119 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
120 logger.addHandler(Vlog.__file_handler)
121 except (IOError, socket.error):
122 logger.setLevel(logging.CRITICAL)
124 ovs.unixctl.command_register("vlog/reopen", "", 0, 0,
125 Vlog._unixctl_vlog_reopen, None)
126 ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxint,
127 Vlog._unixctl_vlog_set, None)
128 ovs.unixctl.command_register("vlog/list", "", 0, 0,
129 Vlog._unixctl_vlog_list, None)
132 def set_level(module, facility, level):
133 """ Sets the log level of the 'module'-'facility' tuple to 'level'.
134 All three arguments are strings which are interpreted the same as
135 arguments to the --verbose flag. Should be called after all Vlog
136 objects have already been created."""
138 module = module.lower()
139 facility = facility.lower()
140 level = level.lower()
142 if facility != "any" and facility not in FACILITIES:
145 if module != "any" and module not in Vlog.__mfl:
148 if level not in LEVELS:
152 modules = Vlog.__mfl.keys()
156 if facility == "any":
157 facilities = FACILITIES.keys()
159 facilities = [facility]
163 Vlog.__mfl[m][f] = level
166 def set_levels_from_string(s):
171 for word in [w.lower() for w in re.split('[ :]', s)]:
174 elif word in FACILITIES:
176 return "cannot specify multiple facilities"
180 return "cannot specify multiple levels"
182 elif word in Vlog.__mfl:
184 return "cannot specify multiple modules"
187 return "no facility, level, or module \"%s\"" % word
189 Vlog.set_level(module or "any", facility or "any", level or "any")
193 lines = [" console syslog file\n",
194 " ------- ------ ------\n"]
195 lines.extend(sorted(["%-16s %4s %4s %4s\n"
197 Vlog.__mfl[m]["console"],
198 Vlog.__mfl[m]["syslog"],
199 Vlog.__mfl[m]["file"]) for m in Vlog.__mfl]))
200 return ''.join(lines)
203 def reopen_log_file():
204 """Closes and then attempts to re-open the current log file. (This is
205 useful just after log rotation, to ensure that the new log file starts
209 logger = logging.getLogger("file")
210 logger.removeHandler(Vlog.__file_handler)
211 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
212 logger.addHandler(Vlog.__file_handler)
215 def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
217 Vlog.reopen_log_file()
220 conn.reply("Logging to file not configured")
223 def _unixctl_vlog_set(conn, argv, unused_aux):
225 msg = Vlog.set_levels_from_string(arg)
232 def _unixctl_vlog_list(conn, unused_argv, unused_aux):
233 conn.reply(Vlog.get_levels())
235 def add_args(parser):
236 """Adds vlog related options to 'parser', an ArgumentParser object. The
237 resulting arguments parsed by 'parser' should be passed to handle_args."""
239 group = parser.add_argument_group(title="Logging Options")
240 group.add_argument("--log-file", nargs="?", const="default",
241 help="Enables logging to a file. Default log file"
242 " is used if LOG_FILE is omitted.")
243 group.add_argument("-v", "--verbose", nargs="*",
244 help="Sets logging levels, see ovs-vswitchd(8)."
248 def handle_args(args):
249 """ Handles command line arguments ('args') parsed by an ArgumentParser.
250 The ArgumentParser should have been primed by add_args(). Also takes care
251 of initializing the Vlog module."""
253 log_file = args.log_file
254 if log_file == "default":
255 log_file = "%s/%s.log" % (ovs.dirs.LOGDIR, ovs.util.PROGRAM_NAME)
257 if args.verbose is None:
259 elif args.verbose == []:
260 args.verbose = ["any:any:dbg"]
262 for verbose in args.verbose:
263 msg = Vlog.set_levels_from_string(verbose)
265 ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg))