Ignore man/*.in.
[ganeti-local] / lib / logger.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 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
22 """Logging for Ganeti
23
24 This module abstracts the logging handling away from the rest of the
25 Ganeti code. It offers some utility functions for easy logging.
26 """
27
28 # pylint: disable-msg=W0603,C0103
29
30 import sys
31 import logging
32 import os, os.path
33
34 from ganeti import constants
35
36 _program = '(unknown)'
37 _errlog = None
38 _inflog = None
39 _dbglog = None
40 _stdout = None
41 _stderr = None
42 _debug = False
43
44
45 def _SetDestination(name, filename, stream=None):
46   """Configure the destination for a given logger
47
48   This function configures the logging destination for a given loger.
49   Parameters:
50     - name: the logger name
51     - filename: if not empty, log messages will be written (also) to this file
52     - stream: if not none, log messages will be output (also) to this stream
53
54   Returns:
55     - the logger identified by the `name` argument
56   """
57   ret = logging.getLogger(name)
58
59   if filename:
60     fmtr = logging.Formatter('%(asctime)s %(message)s')
61
62     hdlr = logging.FileHandler(filename)
63     hdlr.setFormatter(fmtr)
64     ret.addHandler(hdlr)
65
66   if stream:
67     if name in ('error', 'info', 'debug'):
68       fmtr = logging.Formatter('%(asctime)s %(message)s')
69     else:
70       fmtr = logging.Formatter('%(message)s')
71     hdlr = logging.StreamHandler(stream)
72     hdlr.setFormatter(fmtr)
73     ret.addHandler(hdlr)
74
75   ret.setLevel(logging.INFO)
76
77   return ret
78
79
80 def _GenericSetup(program, errfile, inffile, dbgfile,
81                   twisted_workaround=False):
82   """Configure logging based on arguments
83
84   Arguments:
85     - name of program
86     - error log filename
87     - info log filename
88     - debug log filename
89     - twisted_workaround: if true, emit all messages to stderr
90   """
91   global _program
92   global _errlog
93   global _inflog
94   global _dbglog
95   global _stdout
96   global _stderr
97
98   _program = program
99   if twisted_workaround:
100     _errlog = _SetDestination('error', None, sys.stderr)
101     _inflog = _SetDestination('info', None, sys.stderr)
102     _dbglog = _SetDestination('debug', None, sys.stderr)
103   else:
104     _errlog = _SetDestination('error', errfile)
105     _inflog = _SetDestination('info', inffile)
106     _dbglog = _SetDestination('debug', dbgfile)
107
108   _stdout = _SetDestination('user', None, sys.stdout)
109   _stderr = _SetDestination('stderr', None, sys.stderr)
110
111
112 def SetupLogging(twisted_workaround=False, debug=False, program='ganeti'):
113   """Setup logging for ganeti
114
115   On failure, a check is made whether process is run by root or not,
116   and an appropriate error message is printed on stderr, then process
117   exits.
118
119   This function is just a wraper over `_GenericSetup()` using specific
120   arguments.
121
122   Parameter:
123     twisted_workaround: passed to `_GenericSetup()`
124
125   """
126   try:
127     _GenericSetup(program,
128                   os.path.join(constants.LOG_DIR, "errors"),
129                   os.path.join(constants.LOG_DIR, "info"),
130                   os.path.join(constants.LOG_DIR, "debug"),
131                   twisted_workaround)
132   except IOError:
133     # The major reason to end up here is that we're being run as a
134     # non-root user.  We might also get here if xen has not been
135     # installed properly.  This is not the correct place to enforce
136     # being run by root; nevertheless, here makes sense because here
137     # is where we first notice it.
138     if os.getuid() != 0:
139       sys.stderr.write('This program must be run by the superuser.\n')
140     else:
141       sys.stderr.write('Unable to open log files.  Incomplete system?\n')
142
143     sys.exit(2)
144
145   global _debug
146   _debug = debug
147
148
149 def _WriteEntry(log, txt):
150   """
151   Write a message to a given log.
152   Splits multi-line messages up into a series of log writes, to
153   keep consistent format on lines in file.
154
155   Parameters:
156     - log: the destination log
157     - txt: the message
158
159   """
160   if log is None:
161     sys.stderr.write("Logging system not initialized while processing"
162                      " message:\n")
163     sys.stderr.write("%s\n" % txt)
164     return
165
166   lines = txt.split('\n')
167
168   spaces = ' ' * len(_program) + '| '
169
170   lines = ([ _program + ': ' + lines[0] ] +
171            map(lambda a: spaces + a, lines[1:]))
172
173   for line in lines:
174     log.log(logging.INFO, line)
175
176
177 def ToStdout(txt):
178   """Write a message to stdout only, bypassing the logging system
179
180   Parameters:
181     - txt: the message
182
183   """
184   sys.stdout.write(txt + '\n')
185   sys.stdout.flush()
186
187
188 def ToStderr(txt):
189   """Write a message to stderr only, bypassing the logging system
190
191   Parameters:
192     - txt: the message
193
194   """
195   sys.stderr.write(txt + '\n')
196   sys.stderr.flush()
197
198
199 def Error(txt):
200   """Write a message to our error log
201
202   Parameters:
203     - dbg: if true, the message will also be output to stderr
204     - txt: the log message
205
206   """
207   _WriteEntry(_errlog, txt)
208   sys.stderr.write(txt + '\n')
209
210
211 def Info(txt):
212   """Write a message to our general messages log
213
214   If the global debug flag is true, the log message will also be
215   output to stderr.
216
217   Parameters:
218     - txt: the log message
219
220   """
221   _WriteEntry(_inflog, txt)
222   if _debug:
223     _WriteEntry(_stderr, txt)
224
225
226 def Debug(txt):
227   """Write a message to the debug log
228
229   If the global debug flag is true, the log message will also be
230   output to stderr.
231
232   Parameters:
233     - txt: the log message
234
235   """
236   _WriteEntry(_dbglog, txt)
237   if _debug:
238     _WriteEntry(_stderr, txt)