Statistics
| Branch: | Tag: | Revision:

root / lib / utils / log.py @ ad88650c

History | View | Annotate | Download (8.4 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 ad88650c Michael Hanselmann
    # Don't reopen on the next message
74 ad88650c Michael Hanselmann
    self._reopen = False
75 ad88650c Michael Hanselmann
76 b6fa9a44 Michael Hanselmann
  def RequestReopen(self):
77 b6fa9a44 Michael Hanselmann
    """Register a request to reopen the file.
78 b6fa9a44 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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