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.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
64 syslog_message = ("%s|%s|%s|%s"
65 % (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)
74 message = "ovs|" + syslog_message
76 message = "%s|%s" % (now, syslog_message)
77 logging.getLogger(f).log(level, message, **kwargs)
79 def emer(self, message, **kwargs):
80 self.__log("EMER", message, **kwargs)
82 def err(self, message, **kwargs):
83 self.__log("ERR", message, **kwargs)
85 def warn(self, message, **kwargs):
86 self.__log("WARN", message, **kwargs)
88 def info(self, message, **kwargs):
89 self.__log("INFO", message, **kwargs)
91 def dbg(self, message, **kwargs):
92 self.__log("DBG", message, **kwargs)
94 def exception(self, message):
95 """Logs 'message' at ERR log level. Includes a backtrace when in
97 self.err(message, exc_info=True)
100 def init(log_file=None):
101 """Intializes the Vlog module. Causes Vlog to write to 'log_file' if
102 not None. Should be called after all Vlog objects have been created.
103 No logging will occur until this function is called."""
109 logging.raiseExceptions = False
110 Vlog.__log_file = log_file
112 logger = logging.getLogger(f)
113 logger.setLevel(logging.DEBUG)
117 logger.addHandler(logging.StreamHandler(sys.stderr))
119 logger.addHandler(logging.handlers.SysLogHandler(
121 facility=logging.handlers.SysLogHandler.LOG_DAEMON))
122 elif f == "file" and Vlog.__log_file:
123 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
124 logger.addHandler(Vlog.__file_handler)
125 except (IOError, socket.error):
126 logger.setLevel(logging.CRITICAL)
128 ovs.unixctl.command_register("vlog/reopen", "", 0, 0,
129 Vlog._unixctl_vlog_reopen, None)
130 ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxint,
131 Vlog._unixctl_vlog_set, None)
132 ovs.unixctl.command_register("vlog/list", "", 0, 0,
133 Vlog._unixctl_vlog_list, None)
136 def set_level(module, facility, level):
137 """ Sets the log level of the 'module'-'facility' tuple to 'level'.
138 All three arguments are strings which are interpreted the same as
139 arguments to the --verbose flag. Should be called after all Vlog
140 objects have already been created."""
142 module = module.lower()
143 facility = facility.lower()
144 level = level.lower()
146 if facility != "any" and facility not in FACILITIES:
149 if module != "any" and module not in Vlog.__mfl:
152 if level not in LEVELS:
156 modules = Vlog.__mfl.keys()
160 if facility == "any":
161 facilities = FACILITIES.keys()
163 facilities = [facility]
167 Vlog.__mfl[m][f] = level
170 def set_levels_from_string(s):
175 for word in [w.lower() for w in re.split('[ :]', s)]:
178 elif word in FACILITIES:
180 return "cannot specify multiple facilities"
184 return "cannot specify multiple levels"
186 elif word in Vlog.__mfl:
188 return "cannot specify multiple modules"
191 return "no facility, level, or module \"%s\"" % word
193 Vlog.set_level(module or "any", facility or "any", level or "any")
197 lines = [" console syslog file\n",
198 " ------- ------ ------\n"]
199 lines.extend(sorted(["%-16s %4s %4s %4s\n"
201 Vlog.__mfl[m]["console"],
202 Vlog.__mfl[m]["syslog"],
203 Vlog.__mfl[m]["file"]) for m in Vlog.__mfl]))
204 return ''.join(lines)
207 def reopen_log_file():
208 """Closes and then attempts to re-open the current log file. (This is
209 useful just after log rotation, to ensure that the new log file starts
213 logger = logging.getLogger("file")
214 logger.removeHandler(Vlog.__file_handler)
215 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
216 logger.addHandler(Vlog.__file_handler)
219 def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
221 Vlog.reopen_log_file()
224 conn.reply("Logging to file not configured")
227 def _unixctl_vlog_set(conn, argv, unused_aux):
229 msg = Vlog.set_levels_from_string(arg)
236 def _unixctl_vlog_list(conn, unused_argv, unused_aux):
237 conn.reply(Vlog.get_levels())
239 def add_args(parser):
240 """Adds vlog related options to 'parser', an ArgumentParser object. The
241 resulting arguments parsed by 'parser' should be passed to handle_args."""
243 group = parser.add_argument_group(title="Logging Options")
244 group.add_argument("--log-file", nargs="?", const="default",
245 help="Enables logging to a file. Default log file"
246 " is used if LOG_FILE is omitted.")
247 group.add_argument("-v", "--verbose", nargs="*",
248 help="Sets logging levels, see ovs-vswitchd(8)."
252 def handle_args(args):
253 """ Handles command line arguments ('args') parsed by an ArgumentParser.
254 The ArgumentParser should have been primed by add_args(). Also takes care
255 of initializing the Vlog module."""
257 log_file = args.log_file
258 if log_file == "default":
259 log_file = "%s/%s.log" % (ovs.dirs.LOGDIR, ovs.util.PROGRAM_NAME)
261 if args.verbose is None:
263 elif args.verbose == []:
264 args.verbose = ["any:any:dbg"]
266 for verbose in args.verbose:
267 msg = Vlog.set_levels_from_string(verbose)
269 ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg))