1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 """Utility functions for logging.
31
32 """
33
34 import os.path
35 import logging
36 import logging.handlers
37 from cStringIO import StringIO
38
39 from ganeti import constants
40 from ganeti import compat
41
42
44 """Log handler with ability to reopen log file on request.
45
46 In combination with a SIGHUP handler this class can reopen the log file on
47 user request.
48
49 """
51 """Initializes this class.
52
53 @type filename: string
54 @param filename: Path to logfile
55
56 """
57 logging.handlers.BaseRotatingHandler.__init__(self, filename, "a")
58
59 assert self.encoding is None, "Encoding not supported for logging"
60 assert not hasattr(self, "_reopen"), "Base class has '_reopen' attribute"
61
62 self._reopen = False
63
65 """Determine whether log file should be reopened.
66
67 """
68 return self._reopen or not self.stream
69
71 """Reopens the log file.
72
73 """
74 if self.stream:
75 self.stream.flush()
76 self.stream.close()
77 self.stream = None
78
79
80
81 self.stream = open(self.baseFilename, "a")
82
83
84 self._reopen = False
85
87 """Register a request to reopen the file.
88
89 The file will be reopened before writing the next log record.
90
91 """
92 self._reopen = True
93
94
96 """Create wrapper class writing errors to console.
97
98 This needs to be in a function for unittesting.
99
100 """
101 class wrapped(base):
102 """Log handler that doesn't fallback to stderr.
103
104 When an error occurs while writing on the logfile, logging.FileHandler
105 tries to log on stderr. This doesn't work in Ganeti since stderr is
106 redirected to a logfile. This class avoids failures by reporting errors to
107 /dev/console.
108
109 """
110 def __init__(self, console, *args, **kwargs):
111 """Initializes this class.
112
113 @type console: file-like object or None
114 @param console: Open file-like object for console
115
116 """
117 base.__init__(self, *args, **kwargs)
118 assert not hasattr(self, "_console")
119 self._console = console
120
121 def handleError(self, record):
122 """Handle errors which occur during an emit() call.
123
124 Try to handle errors with FileHandler method, if it fails write to
125 /dev/console.
126
127 """
128 try:
129 base.handleError(record)
130 except Exception:
131 if self._console:
132 try:
133
134 self._console.write("Cannot log message:\n%s\n" %
135 self.format(record))
136 except Exception:
137
138 pass
139
140 return wrapped
141
142
143
144 _LogHandler = _LogErrorsToConsole(_ReopenableLogHandler)
145
146
178
179
181 """Wrapper for reopening all log handler's files in a sequence.
182
183 """
184 for handler in handlers:
185 handler.RequestReopen()
186 logging.info("Received request to reopen log files")
187
188
189 -def SetupLogging(logfile, program, debug=0, stderr_logging=False,
190 multithreaded=False, syslog=constants.SYSLOG_USAGE,
191 console_logging=False, root_logger=None):
192 """Configures the logging module.
193
194 @type logfile: str
195 @param logfile: the filename to which we should log
196 @type program: str
197 @param program: the name under which we should log messages
198 @type debug: integer
199 @param debug: if greater than zero, enable debug messages, otherwise
200 only those at C{INFO} and above level
201 @type stderr_logging: boolean
202 @param stderr_logging: whether we should also log to the standard error
203 @type multithreaded: boolean
204 @param multithreaded: if True, will add the thread name to the log file
205 @type syslog: string
206 @param syslog: one of 'no', 'yes', 'only':
207 - if no, syslog is not used
208 - if yes, syslog is used (in addition to file-logging)
209 - if only, only syslog is used
210 @type console_logging: boolean
211 @param console_logging: if True, will use a FileHandler which falls back to
212 the system console if logging fails
213 @type root_logger: logging.Logger
214 @param root_logger: Root logger to use (for unittests)
215 @raise EnvironmentError: if we can't open the log file and
216 syslog/stderr logging is disabled
217 @rtype: callable
218 @return: Function reopening all open log files when called
219
220 """
221 progname = os.path.basename(program)
222
223 formatter = _GetLogFormatter(progname, multithreaded, debug, False)
224 syslog_fmt = _GetLogFormatter(progname, multithreaded, debug, True)
225
226 reopen_handlers = []
227
228 if root_logger is None:
229 root_logger = logging.getLogger("")
230 root_logger.setLevel(logging.NOTSET)
231
232
233 for handler in root_logger.handlers:
234 handler.close()
235 root_logger.removeHandler(handler)
236
237 if stderr_logging:
238 stderr_handler = logging.StreamHandler()
239 stderr_handler.setFormatter(formatter)
240 if debug:
241 stderr_handler.setLevel(logging.NOTSET)
242 else:
243 stderr_handler.setLevel(logging.CRITICAL)
244 root_logger.addHandler(stderr_handler)
245
246 if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
247 facility = logging.handlers.SysLogHandler.LOG_DAEMON
248 syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
249 facility)
250 syslog_handler.setFormatter(syslog_fmt)
251
252 syslog_handler.setLevel(logging.INFO)
253 root_logger.addHandler(syslog_handler)
254
255 if syslog != constants.SYSLOG_ONLY:
256
257
258
259
260 try:
261 if console_logging:
262 logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"),
263 logfile)
264 else:
265 logfile_handler = _ReopenableLogHandler(logfile)
266
267 logfile_handler.setFormatter(formatter)
268 if debug:
269 logfile_handler.setLevel(logging.DEBUG)
270 else:
271 logfile_handler.setLevel(logging.INFO)
272 root_logger.addHandler(logfile_handler)
273 reopen_handlers.append(logfile_handler)
274 except EnvironmentError:
275 if stderr_logging or syslog == constants.SYSLOG_YES:
276 logging.exception("Failed to enable logging to file '%s'", logfile)
277 else:
278
279 raise
280
281 return compat.partial(_ReopenLogFiles, reopen_handlers)
282
283
327