Revision 9a6813ac

b/lib/utils/log.py
27 27
import logging.handlers
28 28

  
29 29
from ganeti import constants
30
from ganeti import compat
30 31

  
31 32

  
32 33
class _ReopenableLogHandler(logging.handlers.BaseRotatingHandler):
......
163 164
  return logging.Formatter("".join(parts))
164 165

  
165 166

  
167
def _ReopenLogFiles(handlers):
168
  """Wrapper for reopening all log handler's files in a sequence.
169

  
170
  """
171
  for handler in handlers:
172
    handler.RequestReopen()
173

  
174

  
166 175
def SetupLogging(logfile, program, debug=0, stderr_logging=False,
167 176
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
168
                 console_logging=False):
177
                 console_logging=False, root_logger=None):
169 178
  """Configures the logging module.
170 179

  
171 180
  @type logfile: str
......
187 196
  @type console_logging: boolean
188 197
  @param console_logging: if True, will use a FileHandler which falls back to
189 198
      the system console if logging fails
199
  @type root_logger: logging.Logger
200
  @param root_logger: Root logger to use (for unittests)
190 201
  @raise EnvironmentError: if we can't open the log file and
191 202
      syslog/stderr logging is disabled
192 203

  
......
196 207
  formatter = _GetLogFormatter(progname, multithreaded, debug, False)
197 208
  syslog_fmt = _GetLogFormatter(progname, multithreaded, debug, True)
198 209

  
199
  root_logger = logging.getLogger("")
210
  reopen_handlers = []
211

  
212
  if root_logger is None:
213
    root_logger = logging.getLogger("")
200 214
  root_logger.setLevel(logging.NOTSET)
201 215

  
202 216
  # Remove all previously setup handlers
......
245 259
    else:
246 260
      logfile_handler.setLevel(logging.INFO)
247 261
    root_logger.addHandler(logfile_handler)
262

  
263
    reopen_handlers.append(logfile_handler)
264

  
265
  return compat.partial(_ReopenLogFiles, reopen_handlers)
b/test/ganeti.utils.log_unittest.py
25 25
import unittest
26 26
import logging
27 27
import tempfile
28
import shutil
28 29

  
29 30
from ganeti import constants
30 31
from ganeti import errors
......
133 134
      raise Exception
134 135

  
135 136

  
137
class TestSetupLogging(unittest.TestCase):
138
  def setUp(self):
139
    self.tmpdir = tempfile.mkdtemp()
140

  
141
  def tearDown(self):
142
    shutil.rmtree(self.tmpdir)
143

  
144
  def testSimple(self):
145
    logfile = utils.PathJoin(self.tmpdir, "basic.log")
146
    logger = logging.Logger("TestLogger")
147
    self.assertTrue(callable(utils.SetupLogging(logfile, "test",
148
                                                console_logging=False,
149
                                                syslog=constants.SYSLOG_NO,
150
                                                stderr_logging=False,
151
                                                multithreaded=False,
152
                                                root_logger=logger)))
153
    self.assertEqual(utils.ReadFile(logfile), "")
154
    logger.error("This is a test")
155

  
156
    # Ensure SetupLogging used custom logger
157
    logging.error("This message should not show up in the test log file")
158

  
159
    self.assertTrue(utils.ReadFile(logfile).endswith("This is a test\n"))
160

  
161
  def testReopen(self):
162
    logfile = utils.PathJoin(self.tmpdir, "reopen.log")
163
    logfile2 = utils.PathJoin(self.tmpdir, "reopen.log.OLD")
164
    logger = logging.Logger("TestLogger")
165
    reopen_fn = utils.SetupLogging(logfile, "test",
166
                                   console_logging=False,
167
                                   syslog=constants.SYSLOG_NO,
168
                                   stderr_logging=False,
169
                                   multithreaded=False,
170
                                   root_logger=logger)
171
    self.assertTrue(callable(reopen_fn))
172

  
173
    self.assertEqual(utils.ReadFile(logfile), "")
174
    logger.error("This is a test")
175
    self.assertTrue(utils.ReadFile(logfile).endswith("This is a test\n"))
176

  
177
    os.rename(logfile, logfile2)
178
    assert not os.path.exists(logfile)
179

  
180
    # Notify logger to reopen on the next message
181
    reopen_fn()
182
    assert not os.path.exists(logfile)
183

  
184
    # Provoke actual reopen
185
    logger.error("First message")
186

  
187
    self.assertTrue(utils.ReadFile(logfile).endswith("First message\n"))
188
    self.assertTrue(utils.ReadFile(logfile2).endswith("This is a test\n"))
189

  
190

  
136 191
if __name__ == "__main__":
137 192
  testutils.GanetiTestProgram()

Also available in: Unified diff