Statistics
| Branch: | Tag: | Revision:

root / lib / utils / log.py @ 638ac34b

History | View | Annotate | Download (8.3 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 8cabf472 Michael Hanselmann
  @rtype: callable
204 8cabf472 Michael Hanselmann
  @return: Function reopening all open log files when called
205 1ae4c5a1 Michael Hanselmann

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