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
38 from ganeti import constants
39 from ganeti import compat
40 from ganeti import pathutils
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 verbose=True):
193 """Configures the logging module.
194
195 @type logfile: str
196 @param logfile: the filename to which we should log
197 @type program: str
198 @param program: the name under which we should log messages
199 @type debug: integer
200 @param debug: if greater than zero, enable debug messages, otherwise
201 only those at C{INFO} and above level
202 @type stderr_logging: boolean
203 @param stderr_logging: whether we should also log to the standard error
204 @type multithreaded: boolean
205 @param multithreaded: if True, will add the thread name to the log file
206 @type syslog: string
207 @param syslog: one of 'no', 'yes', 'only':
208 - if no, syslog is not used
209 - if yes, syslog is used (in addition to file-logging)
210 - if only, only syslog is used
211 @type console_logging: boolean
212 @param console_logging: if True, will use a FileHandler which falls back to
213 the system console if logging fails
214 @type root_logger: logging.Logger
215 @param root_logger: Root logger to use (for unittests)
216 @type verbose: boolean
217 @param verbose: whether to log at 'info' level already (logfile logging only)
218 @raise EnvironmentError: if we can't open the log file and
219 syslog/stderr logging is disabled
220 @rtype: callable
221 @return: Function reopening all open log files when called
222
223 """
224 progname = os.path.basename(program)
225
226 formatter = _GetLogFormatter(progname, multithreaded, debug, False)
227 syslog_fmt = _GetLogFormatter(progname, multithreaded, debug, True)
228
229 reopen_handlers = []
230
231 if root_logger is None:
232 root_logger = logging.getLogger("")
233 root_logger.setLevel(logging.NOTSET)
234
235
236 for handler in root_logger.handlers:
237 handler.close()
238 root_logger.removeHandler(handler)
239
240 if stderr_logging:
241 stderr_handler = logging.StreamHandler()
242 stderr_handler.setFormatter(formatter)
243 if debug:
244 stderr_handler.setLevel(logging.NOTSET)
245 else:
246 stderr_handler.setLevel(logging.CRITICAL)
247 root_logger.addHandler(stderr_handler)
248
249 if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
250 facility = logging.handlers.SysLogHandler.LOG_DAEMON
251 syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
252 facility)
253 syslog_handler.setFormatter(syslog_fmt)
254
255 syslog_handler.setLevel(logging.INFO)
256 root_logger.addHandler(syslog_handler)
257
258 if syslog != constants.SYSLOG_ONLY and logfile:
259
260
261
262
263 try:
264 if console_logging:
265 logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"),
266 logfile)
267 else:
268 logfile_handler = _ReopenableLogHandler(logfile)
269
270 logfile_handler.setFormatter(formatter)
271 if debug:
272 logfile_handler.setLevel(logging.DEBUG)
273 elif verbose:
274 logfile_handler.setLevel(logging.INFO)
275 else:
276 logfile_handler.setLevel(logging.WARN)
277 root_logger.addHandler(logfile_handler)
278 reopen_handlers.append(logfile_handler)
279 except EnvironmentError:
280 if stderr_logging or syslog == constants.SYSLOG_YES:
281 logging.exception("Failed to enable logging to file '%s'", logfile)
282 else:
283
284 raise
285
286 return compat.partial(_ReopenLogFiles, reopen_handlers)
287
288
324