4 # Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 """Utility functions for logging.
26 import logging.handlers
28 from ganeti import constants
31 class LogFileHandler(logging.FileHandler):
32 """Log handler that doesn't fallback to stderr.
34 When an error occurs while writing on the logfile, logging.FileHandler tries
35 to log on stderr. This doesn't work in ganeti since stderr is redirected to
36 the logfile. This class avoids failures reporting errors to /dev/console.
39 def __init__(self, filename, mode="a", encoding=None):
40 """Open the specified file and use it as the stream for logging.
42 Also open /dev/console to report errors while logging.
45 logging.FileHandler.__init__(self, filename, mode, encoding)
46 self.console = open(constants.DEV_CONSOLE, "a")
48 def handleError(self, record): # pylint: disable-msg=C0103
49 """Handle errors which occur during an emit() call.
51 Try to handle errors with FileHandler method, if it fails write to
56 logging.FileHandler.handleError(self, record)
57 except Exception: # pylint: disable-msg=W0703
59 self.console.write("Cannot log message:\n%s\n" % self.format(record))
60 except Exception: # pylint: disable-msg=W0703
61 # Log handler tried everything it could, now just give up
65 def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
66 multithreaded=False, syslog=constants.SYSLOG_USAGE,
67 console_logging=False):
68 """Configures the logging module.
71 @param logfile: the filename to which we should log
73 @param debug: if greater than zero, enable debug messages, otherwise
74 only those at C{INFO} and above level
75 @type stderr_logging: boolean
76 @param stderr_logging: whether we should also log to the standard error
78 @param program: the name under which we should log messages
79 @type multithreaded: boolean
80 @param multithreaded: if True, will add the thread name to the log file
82 @param syslog: one of 'no', 'yes', 'only':
83 - if no, syslog is not used
84 - if yes, syslog is used (in addition to file-logging)
85 - if only, only syslog is used
86 @type console_logging: boolean
87 @param console_logging: if True, will use a FileHandler which falls back to
88 the system console if logging fails
89 @raise EnvironmentError: if we can't open the log file and
90 syslog/stderr logging is disabled
93 fmt = "%(asctime)s: " + program + " pid=%(process)d"
94 sft = program + "[%(process)d]:"
96 fmt += "/%(threadName)s"
97 sft += " (%(threadName)s)"
99 fmt += " %(module)s:%(lineno)s"
100 # no debug info for syslog loggers
101 fmt += " %(levelname)s %(message)s"
102 # yes, we do want the textual level, as remote syslog will probably
103 # lose the error level, and it's easier to grep for it
104 sft += " %(levelname)s %(message)s"
105 formatter = logging.Formatter(fmt)
106 sys_fmt = logging.Formatter(sft)
108 root_logger = logging.getLogger("")
109 root_logger.setLevel(logging.NOTSET)
111 # Remove all previously setup handlers
112 for handler in root_logger.handlers:
114 root_logger.removeHandler(handler)
117 stderr_handler = logging.StreamHandler()
118 stderr_handler.setFormatter(formatter)
120 stderr_handler.setLevel(logging.NOTSET)
122 stderr_handler.setLevel(logging.CRITICAL)
123 root_logger.addHandler(stderr_handler)
125 if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
126 facility = logging.handlers.SysLogHandler.LOG_DAEMON
127 syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
129 syslog_handler.setFormatter(sys_fmt)
130 # Never enable debug over syslog
131 syslog_handler.setLevel(logging.INFO)
132 root_logger.addHandler(syslog_handler)
134 if syslog != constants.SYSLOG_ONLY:
135 # this can fail, if the logging directories are not setup or we have
136 # a permisssion problem; in this case, it's best to log but ignore
137 # the error if stderr_logging is True, and if false we re-raise the
138 # exception since otherwise we could run but without any logs at all
141 logfile_handler = LogFileHandler(logfile)
143 logfile_handler = logging.FileHandler(logfile)
144 logfile_handler.setFormatter(formatter)
146 logfile_handler.setLevel(logging.DEBUG)
148 logfile_handler.setLevel(logging.INFO)
149 root_logger.addHandler(logfile_handler)
150 except EnvironmentError:
151 if stderr_logging or syslog == constants.SYSLOG_YES:
152 logging.exception("Failed to enable logging to file '%s'", logfile)
154 # we need to re-raise the exception