Statistics
| Branch: | Tag: | Revision:

root / lib / utils / log.py @ 9a6813ac

History | View | Annotate | Download (8.2 kB)

1 1ae4c5a1 Michael Hanselmann
#
2 1ae4c5a1 Michael Hanselmann
#
3 1ae4c5a1 Michael Hanselmann
4 1ae4c5a1 Michael Hanselmann
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 1ae4c5a1 Michael Hanselmann
#
6 1ae4c5a1 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 1ae4c5a1 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 1ae4c5a1 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 1ae4c5a1 Michael Hanselmann
# (at your option) any later version.
10 1ae4c5a1 Michael Hanselmann
#
11 1ae4c5a1 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 1ae4c5a1 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 1ae4c5a1 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 1ae4c5a1 Michael Hanselmann
# General Public License for more details.
15 1ae4c5a1 Michael Hanselmann
#
16 1ae4c5a1 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 1ae4c5a1 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 1ae4c5a1 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 1ae4c5a1 Michael Hanselmann
# 02110-1301, USA.
20 1ae4c5a1 Michael Hanselmann
21 1ae4c5a1 Michael Hanselmann
"""Utility functions for logging.
22 1ae4c5a1 Michael Hanselmann

23 1ae4c5a1 Michael Hanselmann
"""
24 1ae4c5a1 Michael Hanselmann
25 cfcc79c6 Michael Hanselmann
import os.path
26 1ae4c5a1 Michael Hanselmann
import logging
27 1ae4c5a1 Michael Hanselmann
import logging.handlers
28 1ae4c5a1 Michael Hanselmann
29 1ae4c5a1 Michael Hanselmann
from ganeti import constants
30 9a6813ac Michael Hanselmann
from ganeti import compat
31 1ae4c5a1 Michael Hanselmann
32 1ae4c5a1 Michael Hanselmann
33 b6fa9a44 Michael Hanselmann
class _ReopenableLogHandler(logging.handlers.BaseRotatingHandler):
34 b6fa9a44 Michael Hanselmann
  """Log handler with ability to reopen log file on request.
35 1ae4c5a1 Michael Hanselmann

36 b6fa9a44 Michael Hanselmann
  In combination with a SIGHUP handler this class can reopen the log file on
37 b6fa9a44 Michael Hanselmann
  user request.
38 1ae4c5a1 Michael Hanselmann

39 1ae4c5a1 Michael Hanselmann
  """
40 b6fa9a44 Michael Hanselmann
  def __init__(self, filename):
41 b6fa9a44 Michael Hanselmann
    """Initializes this class.
42 1ae4c5a1 Michael Hanselmann

43 b6fa9a44 Michael Hanselmann
    @type filename: string
44 b6fa9a44 Michael Hanselmann
    @param filename: Path to logfile
45 1ae4c5a1 Michael Hanselmann

46 1ae4c5a1 Michael Hanselmann
    """
47 b6fa9a44 Michael Hanselmann
    logging.handlers.BaseRotatingHandler.__init__(self, filename, "a")
48 1ae4c5a1 Michael Hanselmann
49 b6fa9a44 Michael Hanselmann
    assert self.encoding is None, "Encoding not supported for logging"
50 b6fa9a44 Michael Hanselmann
    assert not hasattr(self, "_reopen"), "Base class has '_reopen' attribute"
51 1ae4c5a1 Michael Hanselmann
52 b6fa9a44 Michael Hanselmann
    self._reopen = False
53 b6fa9a44 Michael Hanselmann
54 b6fa9a44 Michael Hanselmann
  def shouldRollover(self, _): # pylint: disable-msg=C0103
55 b6fa9a44 Michael Hanselmann
    """Determine whether log file should be reopened.
56 b6fa9a44 Michael Hanselmann

57 b6fa9a44 Michael Hanselmann
    """
58 b6fa9a44 Michael Hanselmann
    return self._reopen or not self.stream
59 b6fa9a44 Michael Hanselmann
60 b6fa9a44 Michael Hanselmann
  def doRollover(self): # pylint: disable-msg=C0103
61 b6fa9a44 Michael Hanselmann
    """Reopens the log file.
62 b6fa9a44 Michael Hanselmann

63 b6fa9a44 Michael Hanselmann
    """
64 b6fa9a44 Michael Hanselmann
    if self.stream:
65 b6fa9a44 Michael Hanselmann
      self.stream.flush()
66 b6fa9a44 Michael Hanselmann
      self.stream.close()
67 b6fa9a44 Michael Hanselmann
      self.stream = None
68 b6fa9a44 Michael Hanselmann
69 b6fa9a44 Michael Hanselmann
    # Reopen file
70 b6fa9a44 Michael Hanselmann
    # TODO: Handle errors?
71 b6fa9a44 Michael Hanselmann
    self.stream = open(self.baseFilename, "a")
72 b6fa9a44 Michael Hanselmann
73 b6fa9a44 Michael Hanselmann
  def RequestReopen(self):
74 b6fa9a44 Michael Hanselmann
    """Register a request to reopen the file.
75 b6fa9a44 Michael Hanselmann

76 b6fa9a44 Michael Hanselmann
    The file will be reopened before writing the next log record.
77 b6fa9a44 Michael Hanselmann

78 b6fa9a44 Michael Hanselmann
    """
79 b6fa9a44 Michael Hanselmann
    self._reopen = True
80 b6fa9a44 Michael Hanselmann
81 b6fa9a44 Michael Hanselmann
82 b6fa9a44 Michael Hanselmann
def _LogErrorsToConsole(base):
83 b6fa9a44 Michael Hanselmann
  """Create wrapper class writing errors to console.
84 b6fa9a44 Michael Hanselmann

85 b6fa9a44 Michael Hanselmann
  This needs to be in a function for unittesting.
86 b6fa9a44 Michael Hanselmann

87 b6fa9a44 Michael Hanselmann
  """
88 b6fa9a44 Michael Hanselmann
  class wrapped(base): # pylint: disable-msg=C0103
89 b6fa9a44 Michael Hanselmann
    """Log handler that doesn't fallback to stderr.
90 b6fa9a44 Michael Hanselmann

91 b6fa9a44 Michael Hanselmann
    When an error occurs while writing on the logfile, logging.FileHandler
92 b6fa9a44 Michael Hanselmann
    tries to log on stderr. This doesn't work in Ganeti since stderr is
93 b6fa9a44 Michael Hanselmann
    redirected to a logfile. This class avoids failures by reporting errors to
94 1ae4c5a1 Michael Hanselmann
    /dev/console.
95 1ae4c5a1 Michael Hanselmann

96 1ae4c5a1 Michael Hanselmann
    """
97 b6fa9a44 Michael Hanselmann
    def __init__(self, console, *args, **kwargs):
98 b6fa9a44 Michael Hanselmann
      """Initializes this class.
99 b6fa9a44 Michael Hanselmann

100 b6fa9a44 Michael Hanselmann
      @type console: file-like object or None
101 b6fa9a44 Michael Hanselmann
      @param console: Open file-like object for console
102 b6fa9a44 Michael Hanselmann

103 b6fa9a44 Michael Hanselmann
      """
104 b6fa9a44 Michael Hanselmann
      base.__init__(self, *args, **kwargs)
105 b6fa9a44 Michael Hanselmann
      assert not hasattr(self, "_console")
106 b6fa9a44 Michael Hanselmann
      self._console = console
107 b6fa9a44 Michael Hanselmann
108 b6fa9a44 Michael Hanselmann
    def handleError(self, record): # pylint: disable-msg=C0103
109 b6fa9a44 Michael Hanselmann
      """Handle errors which occur during an emit() call.
110 b6fa9a44 Michael Hanselmann

111 b6fa9a44 Michael Hanselmann
      Try to handle errors with FileHandler method, if it fails write to
112 b6fa9a44 Michael Hanselmann
      /dev/console.
113 b6fa9a44 Michael Hanselmann

114 b6fa9a44 Michael Hanselmann
      """
115 1ae4c5a1 Michael Hanselmann
      try:
116 b6fa9a44 Michael Hanselmann
        base.handleError(record)
117 1ae4c5a1 Michael Hanselmann
      except Exception: # pylint: disable-msg=W0703
118 b6fa9a44 Michael Hanselmann
        if self._console:
119 b6fa9a44 Michael Hanselmann
          try:
120 b6fa9a44 Michael Hanselmann
            # Ignore warning about "self.format", pylint: disable-msg=E1101
121 b6fa9a44 Michael Hanselmann
            self._console.write("Cannot log message:\n%s\n" %
122 b6fa9a44 Michael Hanselmann
                                self.format(record))
123 b6fa9a44 Michael Hanselmann
          except Exception: # pylint: disable-msg=W0703
124 b6fa9a44 Michael Hanselmann
            # Log handler tried everything it could, now just give up
125 b6fa9a44 Michael Hanselmann
            pass
126 b6fa9a44 Michael Hanselmann
127 b6fa9a44 Michael Hanselmann
  return wrapped
128 b6fa9a44 Michael Hanselmann
129 b6fa9a44 Michael Hanselmann
130 b6fa9a44 Michael Hanselmann
#: Custom log handler for writing to console with a reopenable handler
131 b6fa9a44 Michael Hanselmann
_LogHandler = _LogErrorsToConsole(_ReopenableLogHandler)
132 1ae4c5a1 Michael Hanselmann
133 1ae4c5a1 Michael Hanselmann
134 d24bc000 Michael Hanselmann
def _GetLogFormatter(program, multithreaded, debug, syslog):
135 d24bc000 Michael Hanselmann
  """Build log formatter.
136 d24bc000 Michael Hanselmann

137 d24bc000 Michael Hanselmann
  @param program: Program name
138 d24bc000 Michael Hanselmann
  @param multithreaded: Whether to add thread name to log messages
139 d24bc000 Michael Hanselmann
  @param debug: Whether to enable debug messages
140 d24bc000 Michael Hanselmann
  @param syslog: Whether the formatter will be used for syslog
141 d24bc000 Michael Hanselmann

142 d24bc000 Michael Hanselmann
  """
143 d24bc000 Michael Hanselmann
  parts = []
144 d24bc000 Michael Hanselmann
145 d24bc000 Michael Hanselmann
  if syslog:
146 d24bc000 Michael Hanselmann
    parts.append(program + "[%(process)d]:")
147 d24bc000 Michael Hanselmann
  else:
148 d24bc000 Michael Hanselmann
    parts.append("%(asctime)s: " + program + " pid=%(process)d")
149 d24bc000 Michael Hanselmann
150 d24bc000 Michael Hanselmann
  if multithreaded:
151 d24bc000 Michael Hanselmann
    if syslog:
152 d24bc000 Michael Hanselmann
      parts.append(" (%(threadName)s)")
153 d24bc000 Michael Hanselmann
    else:
154 d24bc000 Michael Hanselmann
      parts.append("/%(threadName)s")
155 d24bc000 Michael Hanselmann
156 d24bc000 Michael Hanselmann
  # Add debug info for non-syslog loggers
157 d24bc000 Michael Hanselmann
  if debug and not syslog:
158 d24bc000 Michael Hanselmann
    parts.append(" %(module)s:%(lineno)s")
159 d24bc000 Michael Hanselmann
160 d24bc000 Michael Hanselmann
  # Ses, we do want the textual level, as remote syslog will probably lose the
161 d24bc000 Michael Hanselmann
  # error level, and it's easier to grep for it.
162 d24bc000 Michael Hanselmann
  parts.append(" %(levelname)s %(message)s")
163 d24bc000 Michael Hanselmann
164 d24bc000 Michael Hanselmann
  return logging.Formatter("".join(parts))
165 d24bc000 Michael Hanselmann
166 d24bc000 Michael Hanselmann
167 9a6813ac Michael Hanselmann
def _ReopenLogFiles(handlers):
168 9a6813ac Michael Hanselmann
  """Wrapper for reopening all log handler's files in a sequence.
169 9a6813ac Michael Hanselmann

170 9a6813ac Michael Hanselmann
  """
171 9a6813ac Michael Hanselmann
  for handler in handlers:
172 9a6813ac Michael Hanselmann
    handler.RequestReopen()
173 9a6813ac Michael Hanselmann
174 9a6813ac Michael Hanselmann
175 cfcc79c6 Michael Hanselmann
def SetupLogging(logfile, program, debug=0, stderr_logging=False,
176 1ae4c5a1 Michael Hanselmann
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
177 9a6813ac Michael Hanselmann
                 console_logging=False, root_logger=None):
178 1ae4c5a1 Michael Hanselmann
  """Configures the logging module.
179 1ae4c5a1 Michael Hanselmann

180 1ae4c5a1 Michael Hanselmann
  @type logfile: str
181 1ae4c5a1 Michael Hanselmann
  @param logfile: the filename to which we should log
182 cfcc79c6 Michael Hanselmann
  @type program: str
183 cfcc79c6 Michael Hanselmann
  @param program: the name under which we should log messages
184 1ae4c5a1 Michael Hanselmann
  @type debug: integer
185 1ae4c5a1 Michael Hanselmann
  @param debug: if greater than zero, enable debug messages, otherwise
186 1ae4c5a1 Michael Hanselmann
      only those at C{INFO} and above level
187 1ae4c5a1 Michael Hanselmann
  @type stderr_logging: boolean
188 1ae4c5a1 Michael Hanselmann
  @param stderr_logging: whether we should also log to the standard error
189 1ae4c5a1 Michael Hanselmann
  @type multithreaded: boolean
190 1ae4c5a1 Michael Hanselmann
  @param multithreaded: if True, will add the thread name to the log file
191 1ae4c5a1 Michael Hanselmann
  @type syslog: string
192 1ae4c5a1 Michael Hanselmann
  @param syslog: one of 'no', 'yes', 'only':
193 1ae4c5a1 Michael Hanselmann
      - if no, syslog is not used
194 1ae4c5a1 Michael Hanselmann
      - if yes, syslog is used (in addition to file-logging)
195 1ae4c5a1 Michael Hanselmann
      - if only, only syslog is used
196 1ae4c5a1 Michael Hanselmann
  @type console_logging: boolean
197 1ae4c5a1 Michael Hanselmann
  @param console_logging: if True, will use a FileHandler which falls back to
198 1ae4c5a1 Michael Hanselmann
      the system console if logging fails
199 9a6813ac Michael Hanselmann
  @type root_logger: logging.Logger
200 9a6813ac Michael Hanselmann
  @param root_logger: Root logger to use (for unittests)
201 1ae4c5a1 Michael Hanselmann
  @raise EnvironmentError: if we can't open the log file and
202 1ae4c5a1 Michael Hanselmann
      syslog/stderr logging is disabled
203 1ae4c5a1 Michael Hanselmann

204 1ae4c5a1 Michael Hanselmann
  """
205 cfcc79c6 Michael Hanselmann
  progname = os.path.basename(program)
206 cfcc79c6 Michael Hanselmann
207 cfcc79c6 Michael Hanselmann
  formatter = _GetLogFormatter(progname, multithreaded, debug, False)
208 cfcc79c6 Michael Hanselmann
  syslog_fmt = _GetLogFormatter(progname, multithreaded, debug, True)
209 1ae4c5a1 Michael Hanselmann
210 9a6813ac Michael Hanselmann
  reopen_handlers = []
211 9a6813ac Michael Hanselmann
212 9a6813ac Michael Hanselmann
  if root_logger is None:
213 9a6813ac Michael Hanselmann
    root_logger = logging.getLogger("")
214 1ae4c5a1 Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
215 1ae4c5a1 Michael Hanselmann
216 1ae4c5a1 Michael Hanselmann
  # Remove all previously setup handlers
217 1ae4c5a1 Michael Hanselmann
  for handler in root_logger.handlers:
218 1ae4c5a1 Michael Hanselmann
    handler.close()
219 1ae4c5a1 Michael Hanselmann
    root_logger.removeHandler(handler)
220 1ae4c5a1 Michael Hanselmann
221 1ae4c5a1 Michael Hanselmann
  if stderr_logging:
222 1ae4c5a1 Michael Hanselmann
    stderr_handler = logging.StreamHandler()
223 1ae4c5a1 Michael Hanselmann
    stderr_handler.setFormatter(formatter)
224 1ae4c5a1 Michael Hanselmann
    if debug:
225 1ae4c5a1 Michael Hanselmann
      stderr_handler.setLevel(logging.NOTSET)
226 1ae4c5a1 Michael Hanselmann
    else:
227 1ae4c5a1 Michael Hanselmann
      stderr_handler.setLevel(logging.CRITICAL)
228 1ae4c5a1 Michael Hanselmann
    root_logger.addHandler(stderr_handler)
229 1ae4c5a1 Michael Hanselmann
230 1ae4c5a1 Michael Hanselmann
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
231 1ae4c5a1 Michael Hanselmann
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
232 1ae4c5a1 Michael Hanselmann
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
233 1ae4c5a1 Michael Hanselmann
                                                    facility)
234 d24bc000 Michael Hanselmann
    syslog_handler.setFormatter(syslog_fmt)
235 1ae4c5a1 Michael Hanselmann
    # Never enable debug over syslog
236 1ae4c5a1 Michael Hanselmann
    syslog_handler.setLevel(logging.INFO)
237 1ae4c5a1 Michael Hanselmann
    root_logger.addHandler(syslog_handler)
238 1ae4c5a1 Michael Hanselmann
239 1ae4c5a1 Michael Hanselmann
  if syslog != constants.SYSLOG_ONLY:
240 1ae4c5a1 Michael Hanselmann
    # this can fail, if the logging directories are not setup or we have
241 1ae4c5a1 Michael Hanselmann
    # a permisssion problem; in this case, it's best to log but ignore
242 1ae4c5a1 Michael Hanselmann
    # the error if stderr_logging is True, and if false we re-raise the
243 1ae4c5a1 Michael Hanselmann
    # exception since otherwise we could run but without any logs at all
244 1ae4c5a1 Michael Hanselmann
    try:
245 1ae4c5a1 Michael Hanselmann
      if console_logging:
246 b6fa9a44 Michael Hanselmann
        logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"), logfile)
247 1ae4c5a1 Michael Hanselmann
      else:
248 b6fa9a44 Michael Hanselmann
        logfile_handler = _ReopenableLogHandler(logfile)
249 1ae4c5a1 Michael Hanselmann
    except EnvironmentError:
250 1ae4c5a1 Michael Hanselmann
      if stderr_logging or syslog == constants.SYSLOG_YES:
251 1ae4c5a1 Michael Hanselmann
        logging.exception("Failed to enable logging to file '%s'", logfile)
252 1ae4c5a1 Michael Hanselmann
      else:
253 1ae4c5a1 Michael Hanselmann
        # we need to re-raise the exception
254 1ae4c5a1 Michael Hanselmann
        raise
255 aa0cc3e5 Michael Hanselmann
256 aa0cc3e5 Michael Hanselmann
    logfile_handler.setFormatter(formatter)
257 aa0cc3e5 Michael Hanselmann
    if debug:
258 aa0cc3e5 Michael Hanselmann
      logfile_handler.setLevel(logging.DEBUG)
259 aa0cc3e5 Michael Hanselmann
    else:
260 aa0cc3e5 Michael Hanselmann
      logfile_handler.setLevel(logging.INFO)
261 aa0cc3e5 Michael Hanselmann
    root_logger.addHandler(logfile_handler)
262 9a6813ac Michael Hanselmann
263 9a6813ac Michael Hanselmann
    reopen_handlers.append(logfile_handler)
264 9a6813ac Michael Hanselmann
265 9a6813ac Michael Hanselmann
  return compat.partial(_ReopenLogFiles, reopen_handlers)