Statistics
| Branch: | Tag: | Revision:

root / lib / utils / log.py @ aa0cc3e5

History | View | Annotate | Download (7.7 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 1ae4c5a1 Michael Hanselmann
import logging
26 1ae4c5a1 Michael Hanselmann
import logging.handlers
27 1ae4c5a1 Michael Hanselmann
28 1ae4c5a1 Michael Hanselmann
from ganeti import constants
29 1ae4c5a1 Michael Hanselmann
30 1ae4c5a1 Michael Hanselmann
31 b6fa9a44 Michael Hanselmann
class _ReopenableLogHandler(logging.handlers.BaseRotatingHandler):
32 b6fa9a44 Michael Hanselmann
  """Log handler with ability to reopen log file on request.
33 1ae4c5a1 Michael Hanselmann

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

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

41 b6fa9a44 Michael Hanselmann
    @type filename: string
42 b6fa9a44 Michael Hanselmann
    @param filename: Path to logfile
43 1ae4c5a1 Michael Hanselmann

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

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

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

74 b6fa9a44 Michael Hanselmann
    The file will be reopened before writing the next log record.
75 b6fa9a44 Michael Hanselmann

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

83 b6fa9a44 Michael Hanselmann
  This needs to be in a function for unittesting.
84 b6fa9a44 Michael Hanselmann

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

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

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

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

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

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

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

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

140 d24bc000 Michael Hanselmann
  """
141 d24bc000 Michael Hanselmann
  parts = []
142 d24bc000 Michael Hanselmann
143 d24bc000 Michael Hanselmann
  if syslog:
144 d24bc000 Michael Hanselmann
    parts.append(program + "[%(process)d]:")
145 d24bc000 Michael Hanselmann
  else:
146 d24bc000 Michael Hanselmann
    parts.append("%(asctime)s: " + program + " pid=%(process)d")
147 d24bc000 Michael Hanselmann
148 d24bc000 Michael Hanselmann
  if multithreaded:
149 d24bc000 Michael Hanselmann
    if syslog:
150 d24bc000 Michael Hanselmann
      parts.append(" (%(threadName)s)")
151 d24bc000 Michael Hanselmann
    else:
152 d24bc000 Michael Hanselmann
      parts.append("/%(threadName)s")
153 d24bc000 Michael Hanselmann
154 d24bc000 Michael Hanselmann
  # Add debug info for non-syslog loggers
155 d24bc000 Michael Hanselmann
  if debug and not syslog:
156 d24bc000 Michael Hanselmann
    parts.append(" %(module)s:%(lineno)s")
157 d24bc000 Michael Hanselmann
158 d24bc000 Michael Hanselmann
  # Ses, we do want the textual level, as remote syslog will probably lose the
159 d24bc000 Michael Hanselmann
  # error level, and it's easier to grep for it.
160 d24bc000 Michael Hanselmann
  parts.append(" %(levelname)s %(message)s")
161 d24bc000 Michael Hanselmann
162 d24bc000 Michael Hanselmann
  return logging.Formatter("".join(parts))
163 d24bc000 Michael Hanselmann
164 d24bc000 Michael Hanselmann
165 1ae4c5a1 Michael Hanselmann
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
166 1ae4c5a1 Michael Hanselmann
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
167 1ae4c5a1 Michael Hanselmann
                 console_logging=False):
168 1ae4c5a1 Michael Hanselmann
  """Configures the logging module.
169 1ae4c5a1 Michael Hanselmann

170 1ae4c5a1 Michael Hanselmann
  @type logfile: str
171 1ae4c5a1 Michael Hanselmann
  @param logfile: the filename to which we should log
172 1ae4c5a1 Michael Hanselmann
  @type debug: integer
173 1ae4c5a1 Michael Hanselmann
  @param debug: if greater than zero, enable debug messages, otherwise
174 1ae4c5a1 Michael Hanselmann
      only those at C{INFO} and above level
175 1ae4c5a1 Michael Hanselmann
  @type stderr_logging: boolean
176 1ae4c5a1 Michael Hanselmann
  @param stderr_logging: whether we should also log to the standard error
177 1ae4c5a1 Michael Hanselmann
  @type program: str
178 1ae4c5a1 Michael Hanselmann
  @param program: the name under which we should log messages
179 1ae4c5a1 Michael Hanselmann
  @type multithreaded: boolean
180 1ae4c5a1 Michael Hanselmann
  @param multithreaded: if True, will add the thread name to the log file
181 1ae4c5a1 Michael Hanselmann
  @type syslog: string
182 1ae4c5a1 Michael Hanselmann
  @param syslog: one of 'no', 'yes', 'only':
183 1ae4c5a1 Michael Hanselmann
      - if no, syslog is not used
184 1ae4c5a1 Michael Hanselmann
      - if yes, syslog is used (in addition to file-logging)
185 1ae4c5a1 Michael Hanselmann
      - if only, only syslog is used
186 1ae4c5a1 Michael Hanselmann
  @type console_logging: boolean
187 1ae4c5a1 Michael Hanselmann
  @param console_logging: if True, will use a FileHandler which falls back to
188 1ae4c5a1 Michael Hanselmann
      the system console if logging fails
189 1ae4c5a1 Michael Hanselmann
  @raise EnvironmentError: if we can't open the log file and
190 1ae4c5a1 Michael Hanselmann
      syslog/stderr logging is disabled
191 1ae4c5a1 Michael Hanselmann

192 1ae4c5a1 Michael Hanselmann
  """
193 d24bc000 Michael Hanselmann
  formatter = _GetLogFormatter(program, multithreaded, debug, False)
194 d24bc000 Michael Hanselmann
  syslog_fmt = _GetLogFormatter(program, multithreaded, debug, True)
195 1ae4c5a1 Michael Hanselmann
196 1ae4c5a1 Michael Hanselmann
  root_logger = logging.getLogger("")
197 1ae4c5a1 Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
198 1ae4c5a1 Michael Hanselmann
199 1ae4c5a1 Michael Hanselmann
  # Remove all previously setup handlers
200 1ae4c5a1 Michael Hanselmann
  for handler in root_logger.handlers:
201 1ae4c5a1 Michael Hanselmann
    handler.close()
202 1ae4c5a1 Michael Hanselmann
    root_logger.removeHandler(handler)
203 1ae4c5a1 Michael Hanselmann
204 1ae4c5a1 Michael Hanselmann
  if stderr_logging:
205 1ae4c5a1 Michael Hanselmann
    stderr_handler = logging.StreamHandler()
206 1ae4c5a1 Michael Hanselmann
    stderr_handler.setFormatter(formatter)
207 1ae4c5a1 Michael Hanselmann
    if debug:
208 1ae4c5a1 Michael Hanselmann
      stderr_handler.setLevel(logging.NOTSET)
209 1ae4c5a1 Michael Hanselmann
    else:
210 1ae4c5a1 Michael Hanselmann
      stderr_handler.setLevel(logging.CRITICAL)
211 1ae4c5a1 Michael Hanselmann
    root_logger.addHandler(stderr_handler)
212 1ae4c5a1 Michael Hanselmann
213 1ae4c5a1 Michael Hanselmann
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
214 1ae4c5a1 Michael Hanselmann
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
215 1ae4c5a1 Michael Hanselmann
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
216 1ae4c5a1 Michael Hanselmann
                                                    facility)
217 d24bc000 Michael Hanselmann
    syslog_handler.setFormatter(syslog_fmt)
218 1ae4c5a1 Michael Hanselmann
    # Never enable debug over syslog
219 1ae4c5a1 Michael Hanselmann
    syslog_handler.setLevel(logging.INFO)
220 1ae4c5a1 Michael Hanselmann
    root_logger.addHandler(syslog_handler)
221 1ae4c5a1 Michael Hanselmann
222 1ae4c5a1 Michael Hanselmann
  if syslog != constants.SYSLOG_ONLY:
223 1ae4c5a1 Michael Hanselmann
    # this can fail, if the logging directories are not setup or we have
224 1ae4c5a1 Michael Hanselmann
    # a permisssion problem; in this case, it's best to log but ignore
225 1ae4c5a1 Michael Hanselmann
    # the error if stderr_logging is True, and if false we re-raise the
226 1ae4c5a1 Michael Hanselmann
    # exception since otherwise we could run but without any logs at all
227 1ae4c5a1 Michael Hanselmann
    try:
228 1ae4c5a1 Michael Hanselmann
      if console_logging:
229 b6fa9a44 Michael Hanselmann
        logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"), logfile)
230 1ae4c5a1 Michael Hanselmann
      else:
231 b6fa9a44 Michael Hanselmann
        logfile_handler = _ReopenableLogHandler(logfile)
232 1ae4c5a1 Michael Hanselmann
    except EnvironmentError:
233 1ae4c5a1 Michael Hanselmann
      if stderr_logging or syslog == constants.SYSLOG_YES:
234 1ae4c5a1 Michael Hanselmann
        logging.exception("Failed to enable logging to file '%s'", logfile)
235 1ae4c5a1 Michael Hanselmann
      else:
236 1ae4c5a1 Michael Hanselmann
        # we need to re-raise the exception
237 1ae4c5a1 Michael Hanselmann
        raise
238 aa0cc3e5 Michael Hanselmann
239 aa0cc3e5 Michael Hanselmann
    logfile_handler.setFormatter(formatter)
240 aa0cc3e5 Michael Hanselmann
    if debug:
241 aa0cc3e5 Michael Hanselmann
      logfile_handler.setLevel(logging.DEBUG)
242 aa0cc3e5 Michael Hanselmann
    else:
243 aa0cc3e5 Michael Hanselmann
      logfile_handler.setLevel(logging.INFO)
244 aa0cc3e5 Michael Hanselmann
    root_logger.addHandler(logfile_handler)