diff --git a/Makefile.am b/Makefile.am index 9ba8d0baea6f84e613c431b1ac9673114c89e3c6..a8f133e99341f6b18b71d149c1b0ee416251cb7e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -214,6 +214,7 @@ server_PYTHON = \ utils_PYTHON = \ lib/utils/__init__.py \ lib/utils/algo.py \ + lib/utils/log.py \ lib/utils/mlock.py \ lib/utils/retry.py \ lib/utils/text.py diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 2090f02cb804aefff5a9f85c88daa9b423aec084..f72a24988fbd66d44f43d8f234b831416e49f3cb 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -42,7 +42,6 @@ import select import fcntl import resource import logging -import logging.handlers import signal import OpenSSL import datetime @@ -59,6 +58,7 @@ from ganeti.utils.algo import * # pylint: disable-msg=W0401 from ganeti.utils.retry import * # pylint: disable-msg=W0401 from ganeti.utils.text import * # pylint: disable-msg=W0401 from ganeti.utils.mlock import * # pylint: disable-msg=W0401 +from ganeti.utils.log import * # pylint: disable-msg=W0401 _locksheld = [] @@ -2276,133 +2276,6 @@ def MergeTime(timetuple): return float(seconds) + (float(microseconds) * 0.000001) -class LogFileHandler(logging.FileHandler): - """Log handler that doesn't fallback to stderr. - - When an error occurs while writing on the logfile, logging.FileHandler tries - to log on stderr. This doesn't work in ganeti since stderr is redirected to - the logfile. This class avoids failures reporting errors to /dev/console. - - """ - def __init__(self, filename, mode="a", encoding=None): - """Open the specified file and use it as the stream for logging. - - Also open /dev/console to report errors while logging. - - """ - logging.FileHandler.__init__(self, filename, mode, encoding) - self.console = open(constants.DEV_CONSOLE, "a") - - def handleError(self, record): # pylint: disable-msg=C0103 - """Handle errors which occur during an emit() call. - - Try to handle errors with FileHandler method, if it fails write to - /dev/console. - - """ - try: - logging.FileHandler.handleError(self, record) - except Exception: # pylint: disable-msg=W0703 - try: - self.console.write("Cannot log message:\n%s\n" % self.format(record)) - except Exception: # pylint: disable-msg=W0703 - # Log handler tried everything it could, now just give up - pass - - -def SetupLogging(logfile, debug=0, stderr_logging=False, program="", - multithreaded=False, syslog=constants.SYSLOG_USAGE, - console_logging=False): - """Configures the logging module. - - @type logfile: str - @param logfile: the filename to which we should log - @type debug: integer - @param debug: if greater than zero, enable debug messages, otherwise - only those at C{INFO} and above level - @type stderr_logging: boolean - @param stderr_logging: whether we should also log to the standard error - @type program: str - @param program: the name under which we should log messages - @type multithreaded: boolean - @param multithreaded: if True, will add the thread name to the log file - @type syslog: string - @param syslog: one of 'no', 'yes', 'only': - - if no, syslog is not used - - if yes, syslog is used (in addition to file-logging) - - if only, only syslog is used - @type console_logging: boolean - @param console_logging: if True, will use a FileHandler which falls back to - the system console if logging fails - @raise EnvironmentError: if we can't open the log file and - syslog/stderr logging is disabled - - """ - fmt = "%(asctime)s: " + program + " pid=%(process)d" - sft = program + "[%(process)d]:" - if multithreaded: - fmt += "/%(threadName)s" - sft += " (%(threadName)s)" - if debug: - fmt += " %(module)s:%(lineno)s" - # no debug info for syslog loggers - fmt += " %(levelname)s %(message)s" - # yes, we do want the textual level, as remote syslog will probably - # lose the error level, and it's easier to grep for it - sft += " %(levelname)s %(message)s" - formatter = logging.Formatter(fmt) - sys_fmt = logging.Formatter(sft) - - root_logger = logging.getLogger("") - root_logger.setLevel(logging.NOTSET) - - # Remove all previously setup handlers - for handler in root_logger.handlers: - handler.close() - root_logger.removeHandler(handler) - - if stderr_logging: - stderr_handler = logging.StreamHandler() - stderr_handler.setFormatter(formatter) - if debug: - stderr_handler.setLevel(logging.NOTSET) - else: - stderr_handler.setLevel(logging.CRITICAL) - root_logger.addHandler(stderr_handler) - - if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY): - facility = logging.handlers.SysLogHandler.LOG_DAEMON - syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET, - facility) - syslog_handler.setFormatter(sys_fmt) - # Never enable debug over syslog - syslog_handler.setLevel(logging.INFO) - root_logger.addHandler(syslog_handler) - - if syslog != constants.SYSLOG_ONLY: - # this can fail, if the logging directories are not setup or we have - # a permisssion problem; in this case, it's best to log but ignore - # the error if stderr_logging is True, and if false we re-raise the - # exception since otherwise we could run but without any logs at all - try: - if console_logging: - logfile_handler = LogFileHandler(logfile) - else: - logfile_handler = logging.FileHandler(logfile) - logfile_handler.setFormatter(formatter) - if debug: - logfile_handler.setLevel(logging.DEBUG) - else: - logfile_handler.setLevel(logging.INFO) - root_logger.addHandler(logfile_handler) - except EnvironmentError: - if stderr_logging or syslog == constants.SYSLOG_YES: - logging.exception("Failed to enable logging to file '%s'", logfile) - else: - # we need to re-raise the exception - raise - - def IsNormAbsPath(path): """Check whether a path is absolute and also normalized diff --git a/lib/utils/log.py b/lib/utils/log.py new file mode 100644 index 0000000000000000000000000000000000000000..58fc8e41e688be2c184661a7690865c1ce253a9a --- /dev/null +++ b/lib/utils/log.py @@ -0,0 +1,155 @@ +# +# + +# Copyright (C) 2006, 2007, 2010, 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +"""Utility functions for logging. + +""" + +import logging +import logging.handlers + +from ganeti import constants + + +class LogFileHandler(logging.FileHandler): + """Log handler that doesn't fallback to stderr. + + When an error occurs while writing on the logfile, logging.FileHandler tries + to log on stderr. This doesn't work in ganeti since stderr is redirected to + the logfile. This class avoids failures reporting errors to /dev/console. + + """ + def __init__(self, filename, mode="a", encoding=None): + """Open the specified file and use it as the stream for logging. + + Also open /dev/console to report errors while logging. + + """ + logging.FileHandler.__init__(self, filename, mode, encoding) + self.console = open(constants.DEV_CONSOLE, "a") + + def handleError(self, record): # pylint: disable-msg=C0103 + """Handle errors which occur during an emit() call. + + Try to handle errors with FileHandler method, if it fails write to + /dev/console. + + """ + try: + logging.FileHandler.handleError(self, record) + except Exception: # pylint: disable-msg=W0703 + try: + self.console.write("Cannot log message:\n%s\n" % self.format(record)) + except Exception: # pylint: disable-msg=W0703 + # Log handler tried everything it could, now just give up + pass + + +def SetupLogging(logfile, debug=0, stderr_logging=False, program="", + multithreaded=False, syslog=constants.SYSLOG_USAGE, + console_logging=False): + """Configures the logging module. + + @type logfile: str + @param logfile: the filename to which we should log + @type debug: integer + @param debug: if greater than zero, enable debug messages, otherwise + only those at C{INFO} and above level + @type stderr_logging: boolean + @param stderr_logging: whether we should also log to the standard error + @type program: str + @param program: the name under which we should log messages + @type multithreaded: boolean + @param multithreaded: if True, will add the thread name to the log file + @type syslog: string + @param syslog: one of 'no', 'yes', 'only': + - if no, syslog is not used + - if yes, syslog is used (in addition to file-logging) + - if only, only syslog is used + @type console_logging: boolean + @param console_logging: if True, will use a FileHandler which falls back to + the system console if logging fails + @raise EnvironmentError: if we can't open the log file and + syslog/stderr logging is disabled + + """ + fmt = "%(asctime)s: " + program + " pid=%(process)d" + sft = program + "[%(process)d]:" + if multithreaded: + fmt += "/%(threadName)s" + sft += " (%(threadName)s)" + if debug: + fmt += " %(module)s:%(lineno)s" + # no debug info for syslog loggers + fmt += " %(levelname)s %(message)s" + # yes, we do want the textual level, as remote syslog will probably + # lose the error level, and it's easier to grep for it + sft += " %(levelname)s %(message)s" + formatter = logging.Formatter(fmt) + sys_fmt = logging.Formatter(sft) + + root_logger = logging.getLogger("") + root_logger.setLevel(logging.NOTSET) + + # Remove all previously setup handlers + for handler in root_logger.handlers: + handler.close() + root_logger.removeHandler(handler) + + if stderr_logging: + stderr_handler = logging.StreamHandler() + stderr_handler.setFormatter(formatter) + if debug: + stderr_handler.setLevel(logging.NOTSET) + else: + stderr_handler.setLevel(logging.CRITICAL) + root_logger.addHandler(stderr_handler) + + if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY): + facility = logging.handlers.SysLogHandler.LOG_DAEMON + syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET, + facility) + syslog_handler.setFormatter(sys_fmt) + # Never enable debug over syslog + syslog_handler.setLevel(logging.INFO) + root_logger.addHandler(syslog_handler) + + if syslog != constants.SYSLOG_ONLY: + # this can fail, if the logging directories are not setup or we have + # a permisssion problem; in this case, it's best to log but ignore + # the error if stderr_logging is True, and if false we re-raise the + # exception since otherwise we could run but without any logs at all + try: + if console_logging: + logfile_handler = LogFileHandler(logfile) + else: + logfile_handler = logging.FileHandler(logfile) + logfile_handler.setFormatter(formatter) + if debug: + logfile_handler.setLevel(logging.DEBUG) + else: + logfile_handler.setLevel(logging.INFO) + root_logger.addHandler(logfile_handler) + except EnvironmentError: + if stderr_logging or syslog == constants.SYSLOG_YES: + logging.exception("Failed to enable logging to file '%s'", logfile) + else: + # we need to re-raise the exception + raise