Statistics
| Branch: | Tag: | Revision:

root / lib / utils / log.py @ 0aee8ee9

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 b459a848 Andrea Spadaccini
  def shouldRollover(self, _): # pylint: disable=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 b459a848 Andrea Spadaccini
  def doRollover(self): # pylint: disable=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 b459a848 Andrea Spadaccini
  class wrapped(base): # pylint: disable=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 b459a848 Andrea Spadaccini
    def handleError(self, record): # pylint: disable=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 b459a848 Andrea Spadaccini
      except Exception: # pylint: disable=W0703
121 b6fa9a44 Michael Hanselmann
        if self._console:
122 b6fa9a44 Michael Hanselmann
          try:
123 b459a848 Andrea Spadaccini
            # Ignore warning about "self.format", pylint: disable=E1101
124 b6fa9a44 Michael Hanselmann
            self._console.write("Cannot log message:\n%s\n" %
125 b6fa9a44 Michael Hanselmann
                                self.format(record))
126 b459a848 Andrea Spadaccini
          except Exception: # pylint: disable=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 ee7f592e Iustin Pop
  logging.info("Received request to reopen log files")
177 9a6813ac Michael Hanselmann
178 9a6813ac Michael Hanselmann
179 cfcc79c6 Michael Hanselmann
def SetupLogging(logfile, program, debug=0, stderr_logging=False,
180 1ae4c5a1 Michael Hanselmann
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
181 9a6813ac Michael Hanselmann
                 console_logging=False, root_logger=None):
182 1ae4c5a1 Michael Hanselmann
  """Configures the logging module.
183 1ae4c5a1 Michael Hanselmann

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

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