|
View:
New views
1 Messages
—
Rating Filter:
Alert me
|
|
|
SVN: ZConfig/trunk/ add support for reopening all log files opened using the ZConfig FileHandlerLog message for revision 76855:
add support for reopening all log files opened using the ZConfig FileHandler Changed: U ZConfig/trunk/ZConfig/components/logger/loghandler.py U ZConfig/trunk/ZConfig/components/logger/tests/test_logger.py U ZConfig/trunk/doc/zconfig.pdf U ZConfig/trunk/doc/zconfig.tex -=- Modified: ZConfig/trunk/ZConfig/components/logger/loghandler.py =================================================================== --- ZConfig/trunk/ZConfig/components/logger/loghandler.py 2007-06-20 18:45:27 UTC (rev 76854) +++ ZConfig/trunk/ZConfig/components/logger/loghandler.py 2007-06-20 19:12:32 UTC (rev 76855) @@ -15,6 +15,7 @@ import os import sys +import weakref from logging import Handler, StreamHandler from logging.handlers import SysLogHandler, BufferingHandler @@ -22,6 +23,24 @@ from logging.handlers import NTEventLogHandler as Win32EventLogHandler +_reopenable_handlers = [] + +def reopenFiles(): + """Reopen all logfiles managed by ZConfig configuration.""" + for wr in _reopenable_handlers[:]: + h = wr() + if h is None: + try: + _reopenable_handlers.remove(wr) + except ValueError: + continue + else: + h.reopen() + +def _remove_from_reopenable(wr): + _reopenable_handlers.remove(wr) + + class FileHandler(StreamHandler): """File handler which supports reopening of logs. @@ -34,6 +53,8 @@ StreamHandler.__init__(self, open(filename, mode)) self.baseFilename = filename self.mode = mode + self._wr = weakref.ref(self, _remove_from_reopenable) + _reopenable_handlers.append(self._wr) def close(self): self.stream.close() Modified: ZConfig/trunk/ZConfig/components/logger/tests/test_logger.py =================================================================== --- ZConfig/trunk/ZConfig/components/logger/tests/test_logger.py 2007-06-20 18:45:27 UTC (rev 76854) +++ ZConfig/trunk/ZConfig/components/logger/tests/test_logger.py 2007-06-20 19:12:32 UTC (rev 76855) @@ -45,22 +45,41 @@ # XXX This tries to save and restore the state of logging around # the test. Somewhat surgical; there may be a better way. - name = None - def setUp(self): - self._old_logger = logging.getLogger(self.name) + self._created = [] + self._old_logger = logging.getLogger() self._old_level = self._old_logger.level self._old_handlers = self._old_logger.handlers[:] self._old_logger.handlers[:] = [] self._old_logger.setLevel(logging.WARN) + self._old_logger_dict = logging.root.manager.loggerDict.copy() + logging.root.manager.loggerDict.clear() + def tearDown(self): + logging.root.manager.loggerDict.clear() + logging.root.manager.loggerDict.update(self._old_logger_dict) + for h in self._old_logger.handlers: self._old_logger.removeHandler(h) for h in self._old_handlers: self._old_logger.addHandler(h) self._old_logger.setLevel(self._old_level) + while self._created: + os.unlink(self._created.pop()) + + def mktemp(self): + fd, fn = tempfile.mkstemp() + os.close(fd) + self._created.append(fn) + return fn + + def move(self, fn): + nfn = self.mktemp() + os.rename(fn, nfn) + return nfn + _schema = None def get_schema(self): @@ -129,8 +148,7 @@ def test_with_logfile(self): import os - fd, fn = tempfile.mkstemp() - os.close(fd) + fn = self.mktemp() logger = self.check_simple_logger("<eventlog>\n" " <logfile>\n" " path %s\n" @@ -140,21 +158,6 @@ logfile = logger.handlers[0] self.assertEqual(logfile.level, logging.DEBUG) self.assert_(isinstance(logfile, loghandler.FileHandler)) - # Test the move-and-reopen behavior: - logger.propagate = False - logger.error("message 1") - os.rename(fn, fn + "-") - logger.error("message 2") - logfile.reopen() - logger.error("message 3") - logfile.close() - text1 = open(fn + "-").read() - text2 = open(fn).read() - self.assert_("message 1" in text1) - self.assert_("message 2" in text1) - self.assert_("message 3" in text2) - os.remove(fn) - os.remove(fn + "-") def test_with_stderr(self): self.check_standard_stream("stderr") @@ -277,8 +280,93 @@ return logger +class TestReopeningLogfiles(LoggingTestBase): + + # These tests should not be run on Windows. + + _schematext = """ + <schema> + <import package='ZConfig.components.logger'/> + <multisection type='logger' name='*' attribute='loggers'/> + </schema> + """ + + _sampleconfig_template = """ + <logger> + name foo.bar + <logfile> + path %s + level debug + </logfile> + <logfile> + path %s + level info + </logfile> + </logger> + + <logger> + name bar.foo + <logfile> + path %s + level info + </logfile> + </logger> + """ + + def test_filehandler_reopen(self): + + def mkrecord(msg): + return logging.LogRecord( + "foo.bar", logging.ERROR, __file__, 42, msg, (), ()) + + fn = self.mktemp() + h = loghandler.FileHandler(fn) + h.handle(mkrecord("message 1")) + nfn = self.move(fn) + h.handle(mkrecord("message 2")) + h.reopen() + h.handle(mkrecord("message 3")) + h.close() + + # Check that the messages are in the right files:: + text1 = open(nfn).read() + text2 = open(fn).read() + self.assert_("message 1" in text1) + self.assert_("message 2" in text1) + self.assert_("message 3" in text2) + + def test_logfile_reopening(self): + paths = self.mktemp(), self.mktemp(), self.mktemp() + text = self._sampleconfig_template % paths + conf = self.get_config(text) + assert len(conf.loggers) == 2 + # Build the loggers from the configuration, and write to them: + conf.loggers[0]().info("message 1") + conf.loggers[1]().info("message 2") + npaths = [self.move(fn) for fn in paths] + # + # We expect this to re-open the original files, so we'll have + # six files instead of three. + # + loghandler.reopenFiles() + # + # Write to them again: + conf.loggers[0]().info("message 3") + conf.loggers[1]().info("message 4") + # + # We should not have all six files: + for fn in paths: + self.assert_(os.path.isfile(fn), "%r must exist" % fn) + for fn in npaths: + self.assert_(os.path.isfile(fn), "%r must exist" % fn) + + def test_suite(): - return unittest.makeSuite(TestConfig) + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestConfig)) + if os.name != "nt": + suite.addTest(unittest.makeSuite(TestReopeningLogfiles)) + return suite if __name__ == '__main__': unittest.main(defaultTest="test_suite") Modified: ZConfig/trunk/doc/zconfig.pdf =================================================================== (Binary files differ) Modified: ZConfig/trunk/doc/zconfig.tex =================================================================== --- ZConfig/trunk/doc/zconfig.tex 2007-06-20 18:45:27 UTC (rev 76854) +++ ZConfig/trunk/doc/zconfig.tex 2007-06-20 19:12:32 UTC (rev 76855) @@ -1178,8 +1178,17 @@ method which may be called to close any log files and re-open them. This is useful when using a \UNIX{} signal to effect log file rotation: the signal handler can call this method, and not have to -worry about what handlers have been registered for the logger. +worry about what handlers have been registered for the logger. There +is also a function in the +\module{ZConfig.components.logger.loghandler} module that re-opens all +open log files created using ZConfig configuraiton: +\begin{funcdesc}{reopenFiles}{} + Closes and re-opens all the log files held open by handlers created + by the factories for \code{logfile} sections. This is intended to + help support log rotation for applications. +\end{funcdesc} + Building an application that uses the logging components is fairly straightforward. The schema needs to import the relevant components and declare their use: _______________________________________________ ZConfig mailing list ZConfig@... http://mail.zope.org/mailman/listinfo/zconfig |
| Free Forum Powered by Nabble | Forum Help |