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 exception(self, message):
96 """Logs 'message' at ERR log level. Includes a backtrace when in
98 self.err(message, exc_info=True)
101 def init(log_file=None):
102 """Intializes the Vlog module. Causes Vlog to write to 'log_file' if
103 not None. Should be called after all Vlog objects have been created.
104 No logging will occur until this function is called."""
110 logging.raiseExceptions = False
111 Vlog.__log_file = log_file
113 logger = logging.getLogger(f)
114 logger.setLevel(logging.DEBUG)
118 logger.addHandler(logging.StreamHandler(sys.stderr))
120 logger.addHandler(logging.handlers.SysLogHandler(
122 facility=logging.handlers.SysLogHandler.LOG_DAEMON))
123 elif f == "file" and Vlog.__log_file:
124 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
125 logger.addHandler(Vlog.__file_handler)
126 except (IOError, socket.error):
127 logger.setLevel(logging.CRITICAL)
129 ovs.unixctl.command_register("vlog/reopen", "", 0, 0,
130 Vlog._unixctl_vlog_reopen, None)
131 ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxint,
132 Vlog._unixctl_vlog_set, None)
133 ovs.unixctl.command_register("vlog/list", "", 0, 0,
134 Vlog._unixctl_vlog_list, None)
137 def set_level(module, facility, level):
138 """ Sets the log level of the 'module'-'facility' tuple to 'level'.
139 All three arguments are strings which are interpreted the same as
140 arguments to the --verbose flag. Should be called after all Vlog
141 objects have already been created."""
143 module = module.lower()
144 facility = facility.lower()
145 level = level.lower()
147 if facility != "any" and facility not in FACILITIES:
150 if module != "any" and module not in Vlog.__mfl:
153 if level not in LEVELS:
157 modules = Vlog.__mfl.keys()
161 if facility == "any":
162 facilities = FACILITIES.keys()
164 facilities = [facility]
168 Vlog.__mfl[m][f] = level
171 def set_levels_from_string(s):
176 for word in [w.lower() for w in re.split('[ :]', s)]:
179 elif word in FACILITIES:
181 return "cannot specify multiple facilities"
185 return "cannot specify multiple levels"
187 elif word in Vlog.__mfl:
189 return "cannot specify multiple modules"
192 return "no facility, level, or module \"%s\"" % word
194 Vlog.set_level(module or "any", facility or "any", level or "any")
198 lines = [" console syslog file\n",
199 " ------- ------ ------\n"]
200 lines.extend(sorted(["%-16s %4s %4s %4s\n"
202 Vlog.__mfl[m]["console"],
203 Vlog.__mfl[m]["syslog"],
204 Vlog.__mfl[m]["file"]) for m in Vlog.__mfl]))
205 return ''.join(lines)
208 def reopen_log_file():
209 """Closes and then attempts to re-open the current log file. (This is
210 useful just after log rotation, to ensure that the new log file starts
214 logger = logging.getLogger("file")
215 logger.removeHandler(Vlog.__file_handler)
216 Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
217 logger.addHandler(Vlog.__file_handler)
220 def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
222 Vlog.reopen_log_file()
225 conn.reply("Logging to file not configured")
228 def _unixctl_vlog_set(conn, argv, unused_aux):
230 msg = Vlog.set_levels_from_string(arg)
237 def _unixctl_vlog_list(conn, unused_argv, unused_aux):
238 conn.reply(Vlog.get_levels())
240 def add_args(parser):
241 """Adds vlog related options to 'parser', an ArgumentParser object. The
242 resulting arguments parsed by 'parser' should be passed to handle_args."""
244 group = parser.add_argument_group(title="Logging Options")
245 group.add_argument("--log-file", nargs="?", const="default",
246 help="Enables logging to a file. Default log file"
247 " is used if LOG_FILE is omitted.")
248 group.add_argument("-v", "--verbose", nargs="*",
249 help="Sets logging levels, see ovs-vswitchd(8)."
253 def handle_args(args):
254 """ Handles command line arguments ('args') parsed by an ArgumentParser.
255 The ArgumentParser should have been primed by add_args(). Also takes care
256 of initializing the Vlog module."""
258 log_file = args.log_file
259 if log_file == "default":
260 log_file = "%s/%s.log" % (ovs.dirs.LOGDIR, ovs.util.PROGRAM_NAME)
262 if args.verbose is None:
264 elif args.verbose == []:
265 args.verbose = ["any:any:dbg"]
267 for verbose in args.verbose:
268 msg = Vlog.set_levels_from_string(verbose)
270 ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg))