Statistics
| Branch: | Tag: | Revision:

root / lib / utils / log.py @ 1cc324f0

History | View | Annotate | Download (9.5 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 796b5152 Michael Hanselmann
from cStringIO import StringIO
29 1ae4c5a1 Michael Hanselmann
30 1ae4c5a1 Michael Hanselmann
from ganeti import constants
31 9a6813ac Michael Hanselmann
from ganeti import compat
32 1ae4c5a1 Michael Hanselmann
33 1ae4c5a1 Michael Hanselmann
34 b6fa9a44 Michael Hanselmann
class _ReopenableLogHandler(logging.handlers.BaseRotatingHandler):
35 b6fa9a44 Michael Hanselmann
  """Log handler with ability to reopen log file on request.
36 1ae4c5a1 Michael Hanselmann

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

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

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

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

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

64 b6fa9a44 Michael Hanselmann
    """
65 b6fa9a44 Michael Hanselmann
    if self.stream:
66 b6fa9a44 Michael Hanselmann
      self.stream.flush()
67 b6fa9a44 Michael Hanselmann
      self.stream.close()
68 b6fa9a44 Michael Hanselmann
      self.stream = None
69 b6fa9a44 Michael Hanselmann
70 b6fa9a44 Michael Hanselmann
    # Reopen file
71 b6fa9a44 Michael Hanselmann
    # TODO: Handle errors?
72 b6fa9a44 Michael Hanselmann
    self.stream = open(self.baseFilename, "a")
73 b6fa9a44 Michael Hanselmann
74 ad88650c Michael Hanselmann
    # Don't reopen on the next message
75 ad88650c Michael Hanselmann
    self._reopen = False
76 ad88650c Michael Hanselmann
77 b6fa9a44 Michael Hanselmann
  def RequestReopen(self):
78 b6fa9a44 Michael Hanselmann
    """Register a request to reopen the file.
79 b6fa9a44 Michael Hanselmann

80 b6fa9a44 Michael Hanselmann
    The file will be reopened before writing the next log record.
81 b6fa9a44 Michael Hanselmann

82 b6fa9a44 Michael Hanselmann
    """
83 b6fa9a44 Michael Hanselmann
    self._reopen = True
84 b6fa9a44 Michael Hanselmann
85 b6fa9a44 Michael Hanselmann
86 b6fa9a44 Michael Hanselmann
def _LogErrorsToConsole(base):
87 b6fa9a44 Michael Hanselmann
  """Create wrapper class writing errors to console.
88 b6fa9a44 Michael Hanselmann

89 b6fa9a44 Michael Hanselmann
  This needs to be in a function for unittesting.
90 b6fa9a44 Michael Hanselmann

91 b6fa9a44 Michael Hanselmann
  """
92 b459a848 Andrea Spadaccini
  class wrapped(base): # pylint: disable=C0103
93 b6fa9a44 Michael Hanselmann
    """Log handler that doesn't fallback to stderr.
94 b6fa9a44 Michael Hanselmann

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

100 1ae4c5a1 Michael Hanselmann
    """
101 b6fa9a44 Michael Hanselmann
    def __init__(self, console, *args, **kwargs):
102 b6fa9a44 Michael Hanselmann
      """Initializes this class.
103 b6fa9a44 Michael Hanselmann

104 b6fa9a44 Michael Hanselmann
      @type console: file-like object or None
105 b6fa9a44 Michael Hanselmann
      @param console: Open file-like object for console
106 b6fa9a44 Michael Hanselmann

107 b6fa9a44 Michael Hanselmann
      """
108 b6fa9a44 Michael Hanselmann
      base.__init__(self, *args, **kwargs)
109 b6fa9a44 Michael Hanselmann
      assert not hasattr(self, "_console")
110 b6fa9a44 Michael Hanselmann
      self._console = console
111 b6fa9a44 Michael Hanselmann
112 b459a848 Andrea Spadaccini
    def handleError(self, record): # pylint: disable=C0103
113 b6fa9a44 Michael Hanselmann
      """Handle errors which occur during an emit() call.
114 b6fa9a44 Michael Hanselmann

115 b6fa9a44 Michael Hanselmann
      Try to handle errors with FileHandler method, if it fails write to
116 b6fa9a44 Michael Hanselmann
      /dev/console.
117 b6fa9a44 Michael Hanselmann

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

141 d24bc000 Michael Hanselmann
  @param program: Program name
142 d24bc000 Michael Hanselmann
  @param multithreaded: Whether to add thread name to log messages
143 d24bc000 Michael Hanselmann
  @param debug: Whether to enable debug messages
144 d24bc000 Michael Hanselmann
  @param syslog: Whether the formatter will be used for syslog
145 d24bc000 Michael Hanselmann

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

174 9a6813ac Michael Hanselmann
  """
175 9a6813ac Michael Hanselmann
  for handler in handlers:
176 9a6813ac Michael Hanselmann
    handler.RequestReopen()
177 ee7f592e Iustin Pop
  logging.info("Received request to reopen log files")
178 9a6813ac Michael Hanselmann
179 9a6813ac Michael Hanselmann
180 cfcc79c6 Michael Hanselmann
def SetupLogging(logfile, program, debug=0, stderr_logging=False,
181 1ae4c5a1 Michael Hanselmann
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
182 9a6813ac Michael Hanselmann
                 console_logging=False, root_logger=None):
183 1ae4c5a1 Michael Hanselmann
  """Configures the logging module.
184 1ae4c5a1 Michael Hanselmann

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

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

279 796b5152 Michael Hanselmann
  All log messages are sent to stderr.
280 796b5152 Michael Hanselmann

281 796b5152 Michael Hanselmann
  @type debug: boolean
282 796b5152 Michael Hanselmann
  @param debug: Disable log message filtering
283 796b5152 Michael Hanselmann
  @type verbose: boolean
284 796b5152 Michael Hanselmann
  @param verbose: Enable verbose log messages
285 796b5152 Michael Hanselmann
  @type threadname: boolean
286 796b5152 Michael Hanselmann
  @param threadname: Whether to include thread name in output
287 796b5152 Michael Hanselmann

288 796b5152 Michael Hanselmann
  """
289 796b5152 Michael Hanselmann
  if _root_logger is None:
290 796b5152 Michael Hanselmann
    root_logger = logging.getLogger("")
291 796b5152 Michael Hanselmann
  else:
292 796b5152 Michael Hanselmann
    root_logger = _root_logger
293 796b5152 Michael Hanselmann
294 796b5152 Michael Hanselmann
  fmt = StringIO()
295 796b5152 Michael Hanselmann
  fmt.write("%(asctime)s:")
296 796b5152 Michael Hanselmann
297 796b5152 Michael Hanselmann
  if threadname:
298 796b5152 Michael Hanselmann
    fmt.write(" %(threadName)s")
299 796b5152 Michael Hanselmann
300 796b5152 Michael Hanselmann
  if debug or verbose:
301 796b5152 Michael Hanselmann
    fmt.write(" %(levelname)s")
302 796b5152 Michael Hanselmann
303 796b5152 Michael Hanselmann
  fmt.write(" %(message)s")
304 796b5152 Michael Hanselmann
305 796b5152 Michael Hanselmann
  formatter = logging.Formatter(fmt.getvalue())
306 796b5152 Michael Hanselmann
307 796b5152 Michael Hanselmann
  stderr_handler = logging.StreamHandler(_stream)
308 796b5152 Michael Hanselmann
  stderr_handler.setFormatter(formatter)
309 796b5152 Michael Hanselmann
  if debug:
310 796b5152 Michael Hanselmann
    stderr_handler.setLevel(logging.NOTSET)
311 796b5152 Michael Hanselmann
  elif verbose:
312 796b5152 Michael Hanselmann
    stderr_handler.setLevel(logging.INFO)
313 796b5152 Michael Hanselmann
  else:
314 796b5152 Michael Hanselmann
    stderr_handler.setLevel(logging.WARNING)
315 796b5152 Michael Hanselmann
316 796b5152 Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
317 796b5152 Michael Hanselmann
  root_logger.addHandler(stderr_handler)