Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (5.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Utility functions for logging.
22

23
"""
24

    
25
import logging
26
import logging.handlers
27

    
28
from ganeti import constants
29

    
30

    
31
class LogFileHandler(logging.FileHandler):
32
  """Log handler that doesn't fallback to stderr.
33

34
  When an error occurs while writing on the logfile, logging.FileHandler tries
35
  to log on stderr. This doesn't work in ganeti since stderr is redirected to
36
  the logfile. This class avoids failures reporting errors to /dev/console.
37

38
  """
39
  def __init__(self, filename, mode="a", encoding=None):
40
    """Open the specified file and use it as the stream for logging.
41

42
    Also open /dev/console to report errors while logging.
43

44
    """
45
    logging.FileHandler.__init__(self, filename, mode, encoding)
46
    self.console = open(constants.DEV_CONSOLE, "a")
47

    
48
  def handleError(self, record): # pylint: disable-msg=C0103
49
    """Handle errors which occur during an emit() call.
50

51
    Try to handle errors with FileHandler method, if it fails write to
52
    /dev/console.
53

54
    """
55
    try:
56
      logging.FileHandler.handleError(self, record)
57
    except Exception: # pylint: disable-msg=W0703
58
      try:
59
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
60
      except Exception: # pylint: disable-msg=W0703
61
        # Log handler tried everything it could, now just give up
62
        pass
63

    
64

    
65
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
66
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
67
                 console_logging=False):
68
  """Configures the logging module.
69

70
  @type logfile: str
71
  @param logfile: the filename to which we should log
72
  @type debug: integer
73
  @param debug: if greater than zero, enable debug messages, otherwise
74
      only those at C{INFO} and above level
75
  @type stderr_logging: boolean
76
  @param stderr_logging: whether we should also log to the standard error
77
  @type program: str
78
  @param program: the name under which we should log messages
79
  @type multithreaded: boolean
80
  @param multithreaded: if True, will add the thread name to the log file
81
  @type syslog: string
82
  @param syslog: one of 'no', 'yes', 'only':
83
      - if no, syslog is not used
84
      - if yes, syslog is used (in addition to file-logging)
85
      - if only, only syslog is used
86
  @type console_logging: boolean
87
  @param console_logging: if True, will use a FileHandler which falls back to
88
      the system console if logging fails
89
  @raise EnvironmentError: if we can't open the log file and
90
      syslog/stderr logging is disabled
91

92
  """
93
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
94
  sft = program + "[%(process)d]:"
95
  if multithreaded:
96
    fmt += "/%(threadName)s"
97
    sft += " (%(threadName)s)"
98
  if debug:
99
    fmt += " %(module)s:%(lineno)s"
100
    # no debug info for syslog loggers
101
  fmt += " %(levelname)s %(message)s"
102
  # yes, we do want the textual level, as remote syslog will probably
103
  # lose the error level, and it's easier to grep for it
104
  sft += " %(levelname)s %(message)s"
105
  formatter = logging.Formatter(fmt)
106
  sys_fmt = logging.Formatter(sft)
107

    
108
  root_logger = logging.getLogger("")
109
  root_logger.setLevel(logging.NOTSET)
110

    
111
  # Remove all previously setup handlers
112
  for handler in root_logger.handlers:
113
    handler.close()
114
    root_logger.removeHandler(handler)
115

    
116
  if stderr_logging:
117
    stderr_handler = logging.StreamHandler()
118
    stderr_handler.setFormatter(formatter)
119
    if debug:
120
      stderr_handler.setLevel(logging.NOTSET)
121
    else:
122
      stderr_handler.setLevel(logging.CRITICAL)
123
    root_logger.addHandler(stderr_handler)
124

    
125
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
126
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
127
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
128
                                                    facility)
129
    syslog_handler.setFormatter(sys_fmt)
130
    # Never enable debug over syslog
131
    syslog_handler.setLevel(logging.INFO)
132
    root_logger.addHandler(syslog_handler)
133

    
134
  if syslog != constants.SYSLOG_ONLY:
135
    # this can fail, if the logging directories are not setup or we have
136
    # a permisssion problem; in this case, it's best to log but ignore
137
    # the error if stderr_logging is True, and if false we re-raise the
138
    # exception since otherwise we could run but without any logs at all
139
    try:
140
      if console_logging:
141
        logfile_handler = LogFileHandler(logfile)
142
      else:
143
        logfile_handler = logging.FileHandler(logfile)
144
      logfile_handler.setFormatter(formatter)
145
      if debug:
146
        logfile_handler.setLevel(logging.DEBUG)
147
      else:
148
        logfile_handler.setLevel(logging.INFO)
149
      root_logger.addHandler(logfile_handler)
150
    except EnvironmentError:
151
      if stderr_logging or syslog == constants.SYSLOG_YES:
152
        logging.exception("Failed to enable logging to file '%s'", logfile)
153
      else:
154
        # we need to re-raise the exception
155
        raise