Package ganeti :: Package utils :: Module log
[hide private]
[frames] | no frames]

Source Code for Module ganeti.utils.log

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2010, 2011 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30  """Utility functions for logging. 
 31   
 32  """ 
 33   
 34  import os.path 
 35  import logging 
 36  import logging.handlers 
 37  from cStringIO import StringIO 
 38   
 39  from ganeti import constants 
 40  from ganeti import compat 
 41   
 42   
43 -class _ReopenableLogHandler(logging.handlers.BaseRotatingHandler):
44 """Log handler with ability to reopen log file on request. 45 46 In combination with a SIGHUP handler this class can reopen the log file on 47 user request. 48 49 """
50 - def __init__(self, filename):
51 """Initializes this class. 52 53 @type filename: string 54 @param filename: Path to logfile 55 56 """ 57 logging.handlers.BaseRotatingHandler.__init__(self, filename, "a") 58 59 assert self.encoding is None, "Encoding not supported for logging" 60 assert not hasattr(self, "_reopen"), "Base class has '_reopen' attribute" 61 62 self._reopen = False
63
64 - def shouldRollover(self, _): # pylint: disable=C0103
65 """Determine whether log file should be reopened. 66 67 """ 68 return self._reopen or not self.stream
69
70 - def doRollover(self): # pylint: disable=C0103
71 """Reopens the log file. 72 73 """ 74 if self.stream: 75 self.stream.flush() 76 self.stream.close() 77 self.stream = None 78 79 # Reopen file 80 # TODO: Handle errors? 81 self.stream = open(self.baseFilename, "a") 82 83 # Don't reopen on the next message 84 self._reopen = False 85
86 - def RequestReopen(self):
87 """Register a request to reopen the file. 88 89 The file will be reopened before writing the next log record. 90 91 """ 92 self._reopen = True
93 94
95 -def _LogErrorsToConsole(base):
96 """Create wrapper class writing errors to console. 97 98 This needs to be in a function for unittesting. 99 100 """ 101 class wrapped(base): # pylint: disable=C0103 102 """Log handler that doesn't fallback to stderr. 103 104 When an error occurs while writing on the logfile, logging.FileHandler 105 tries to log on stderr. This doesn't work in Ganeti since stderr is 106 redirected to a logfile. This class avoids failures by reporting errors to 107 /dev/console. 108 109 """ 110 def __init__(self, console, *args, **kwargs): 111 """Initializes this class. 112 113 @type console: file-like object or None 114 @param console: Open file-like object for console 115 116 """ 117 base.__init__(self, *args, **kwargs) 118 assert not hasattr(self, "_console") 119 self._console = console
120 121 def handleError(self, record): # pylint: disable=C0103 122 """Handle errors which occur during an emit() call. 123 124 Try to handle errors with FileHandler method, if it fails write to 125 /dev/console. 126 127 """ 128 try: 129 base.handleError(record) 130 except Exception: # pylint: disable=W0703 131 if self._console: 132 try: 133 # Ignore warning about "self.format", pylint: disable=E1101 134 self._console.write("Cannot log message:\n%s\n" % 135 self.format(record)) 136 except Exception: # pylint: disable=W0703 137 # Log handler tried everything it could, now just give up 138 pass 139 140 return wrapped 141 142 143 #: Custom log handler for writing to console with a reopenable handler 144 _LogHandler = _LogErrorsToConsole(_ReopenableLogHandler) 145 146
147 -def _GetLogFormatter(program, multithreaded, debug, syslog):
148 """Build log formatter. 149 150 @param program: Program name 151 @param multithreaded: Whether to add thread name to log messages 152 @param debug: Whether to enable debug messages 153 @param syslog: Whether the formatter will be used for syslog 154 155 """ 156 parts = [] 157 158 if syslog: 159 parts.append(program + "[%(process)d]:") 160 else: 161 parts.append("%(asctime)s: " + program + " pid=%(process)d") 162 163 if multithreaded: 164 if syslog: 165 parts.append(" (%(threadName)s)") 166 else: 167 parts.append("/%(threadName)s") 168 169 # Add debug info for non-syslog loggers 170 if debug and not syslog: 171 parts.append(" %(module)s:%(lineno)s") 172 173 # Ses, we do want the textual level, as remote syslog will probably lose the 174 # error level, and it's easier to grep for it. 175 parts.append(" %(levelname)s %(message)s") 176 177 return logging.Formatter("".join(parts))
178 179
180 -def _ReopenLogFiles(handlers):
181 """Wrapper for reopening all log handler's files in a sequence. 182 183 """ 184 for handler in handlers: 185 handler.RequestReopen() 186 logging.info("Received request to reopen log files")
187 188
189 -def SetupLogging(logfile, program, debug=0, stderr_logging=False, 190 multithreaded=False, syslog=constants.SYSLOG_USAGE, 191 console_logging=False, root_logger=None):
192 """Configures the logging module. 193 194 @type logfile: str 195 @param logfile: the filename to which we should log 196 @type program: str 197 @param program: the name under which we should log messages 198 @type debug: integer 199 @param debug: if greater than zero, enable debug messages, otherwise 200 only those at C{INFO} and above level 201 @type stderr_logging: boolean 202 @param stderr_logging: whether we should also log to the standard error 203 @type multithreaded: boolean 204 @param multithreaded: if True, will add the thread name to the log file 205 @type syslog: string 206 @param syslog: one of 'no', 'yes', 'only': 207 - if no, syslog is not used 208 - if yes, syslog is used (in addition to file-logging) 209 - if only, only syslog is used 210 @type console_logging: boolean 211 @param console_logging: if True, will use a FileHandler which falls back to 212 the system console if logging fails 213 @type root_logger: logging.Logger 214 @param root_logger: Root logger to use (for unittests) 215 @raise EnvironmentError: if we can't open the log file and 216 syslog/stderr logging is disabled 217 @rtype: callable 218 @return: Function reopening all open log files when called 219 220 """ 221 progname = os.path.basename(program) 222 223 formatter = _GetLogFormatter(progname, multithreaded, debug, False) 224 syslog_fmt = _GetLogFormatter(progname, multithreaded, debug, True) 225 226 reopen_handlers = [] 227 228 if root_logger is None: 229 root_logger = logging.getLogger("") 230 root_logger.setLevel(logging.NOTSET) 231 232 # Remove all previously setup handlers 233 for handler in root_logger.handlers: 234 handler.close() 235 root_logger.removeHandler(handler) 236 237 if stderr_logging: 238 stderr_handler = logging.StreamHandler() 239 stderr_handler.setFormatter(formatter) 240 if debug: 241 stderr_handler.setLevel(logging.NOTSET) 242 else: 243 stderr_handler.setLevel(logging.CRITICAL) 244 root_logger.addHandler(stderr_handler) 245 246 if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY): 247 facility = logging.handlers.SysLogHandler.LOG_DAEMON 248 syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET, 249 facility) 250 syslog_handler.setFormatter(syslog_fmt) 251 # Never enable debug over syslog 252 syslog_handler.setLevel(logging.INFO) 253 root_logger.addHandler(syslog_handler) 254 255 if syslog != constants.SYSLOG_ONLY: 256 # this can fail, if the logging directories are not setup or we have 257 # a permisssion problem; in this case, it's best to log but ignore 258 # the error if stderr_logging is True, and if false we re-raise the 259 # exception since otherwise we could run but without any logs at all 260 try: 261 if console_logging: 262 logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"), 263 logfile) 264 else: 265 logfile_handler = _ReopenableLogHandler(logfile) 266 267 logfile_handler.setFormatter(formatter) 268 if debug: 269 logfile_handler.setLevel(logging.DEBUG) 270 else: 271 logfile_handler.setLevel(logging.INFO) 272 root_logger.addHandler(logfile_handler) 273 reopen_handlers.append(logfile_handler) 274 except EnvironmentError: 275 if stderr_logging or syslog == constants.SYSLOG_YES: 276 logging.exception("Failed to enable logging to file '%s'", logfile) 277 else: 278 # we need to re-raise the exception 279 raise 280 281 return compat.partial(_ReopenLogFiles, reopen_handlers)
282 283
284 -def SetupToolLogging(debug, verbose, threadname=False, 285 _root_logger=None, _stream=None):
286 """Configures the logging module for tools. 287 288 All log messages are sent to stderr. 289 290 @type debug: boolean 291 @param debug: Disable log message filtering 292 @type verbose: boolean 293 @param verbose: Enable verbose log messages 294 @type threadname: boolean 295 @param threadname: Whether to include thread name in output 296 297 """ 298 if _root_logger is None: 299 root_logger = logging.getLogger("") 300 else: 301 root_logger = _root_logger 302 303 fmt = StringIO() 304 fmt.write("%(asctime)s:") 305 306 if threadname: 307 fmt.write(" %(threadName)s") 308 309 if debug or verbose: 310 fmt.write(" %(levelname)s") 311 312 fmt.write(" %(message)s") 313 314 formatter = logging.Formatter(fmt.getvalue()) 315 316 stderr_handler = logging.StreamHandler(_stream) 317 stderr_handler.setFormatter(formatter) 318 if debug: 319 stderr_handler.setLevel(logging.NOTSET) 320 elif verbose: 321 stderr_handler.setLevel(logging.INFO) 322 else: 323 stderr_handler.setLevel(logging.WARNING) 324 325 root_logger.setLevel(logging.NOTSET) 326 root_logger.addHandler(stderr_handler)
327