Explorar o código

Test clearing of autochildlogs. Warn if someone has set up AUTO child logging but hasn't specified a maxbytes.

Chris McDonough %!s(int64=18) %!d(string=hai) anos
pai
achega
0b9fbc6213

+ 25 - 21
src/supervisor/options.py

@@ -780,6 +780,16 @@ class ServerOptions(Options):
                     '%(process_num) must be present within process_name when '
                     'numprocs > 1')
 
+        for n in ('stdout_logfile', 'stderr_logfile'):
+            lf_name = logfile_name(get(section, n, None))
+            mb_key = '%s_maxbytes' % n
+            maxbytes = byte_size(get(section, mb_key, '50MB'))
+            if not maxbytes and lf_name is Automatic:
+                self.logger.warn(
+                    'For [%s], AUTO logging used for %s without '
+                    'rollover, set maxbytes > 0 to avoid filling up '
+                    'filesystem unintentionally' % (section, n))
+                
         for process_num in range(0, numprocs):
 
             expansions = {'process_num':process_num,
@@ -972,6 +982,14 @@ class ServerOptions(Options):
         except ValueError, why:
             self.usage(why[0])
 
+    def get_autochildlog_name(self, name, identifier, channel):
+        prefix='%s-%s---%s-' % (name, channel, identifier)
+        logfile = self.mktempfile(
+            suffix='.log',
+            prefix=prefix,
+            dir=self.childlogdir)
+        return logfile
+
     def clear_autochildlogdir(self):
         # must be called after realize()
         childlogdir = self.childlogdir
@@ -1450,32 +1468,18 @@ class ProcessConfig(Config):
 
     def create_autochildlogs(self):
         # temporary logfiles which are erased at start time
+        get_autoname = self.options.get_autochildlog_name
+        sid = self.options.identifier
+        name = self.name
         if self.stdout_logfile is Automatic:
-            self.stdout_logfile = self._getautolog(self.name,
-                                                  self.options.identifier,
-                                                  'stdout')
+            self.stdout_logfile = get_autoname(name, sid, 'stdout')
         if self.stderr_logfile is Automatic:
-            self.stderr_logfile = self._getautolog(self.name,
-                                                   self.options.identifier,
-                                                   'stderr')
+            self.stderr_logfile = get_autoname(name, sid, 'stderr')
         if self.stdout_capturefile is Automatic:
-            self.stdout_capturefile = self._getautolog(self.name,
-                                                       self.options.identifier,
-                                                       'stdout_capture')
+            self.stdout_capturefile = get_autoname(name, sid, 'stdout_capture')
         if self.stderr_capturefile is Automatic:
-            self.stderr_capturefile = self._getautolog(self.name,
-                                                       self.options.identifier,
-                                                       'stderr_capture')
+            self.stderr_capturefile = get_autoname(name, sid, 'stderr_capture')
             
-
-    def _getautolog(self, name, identifier, channel):
-        prefix='%s-%s---%s-' % (name, channel, identifier)
-        logfile = self.options.mktempfile(
-            suffix='.log',
-            prefix=prefix,
-            dir=self.options.childlogdir)
-        return logfile
-
     def make_stderr_recorder(self):
         from supervisor.recorders import LoggingRecorder
         if self.stderr_logfile and not self.redirect_stderr:

+ 1 - 1
src/supervisor/supervisord.py

@@ -31,7 +31,7 @@ Options:
 -i/--identifier STR -- identifier used for this instance of supervisord
 -q/--childlogdir DIRECTORY -- the log directory for child process logs
 -k/--nocleanup --  prevent the process from performing cleanup (removal of
-                   orphaned child log files, etc.) at startup.
+                   old automatic child log files) at startup.
 -w/--http_port SOCKET -- the host/port that the HTTP server should listen on
 -g/--http_username STR -- the username for HTTP auth
 -r/--http_password STR -- the password for HTTP auth

+ 3 - 0
src/supervisor/tests/base.py

@@ -102,6 +102,9 @@ class DummyOptions:
     def clear_autochildlogdir(self):
         self.autochildlogdir_cleared = True
 
+    def get_autochildlog_name(self, *ignored):
+        return self.tempfile_name
+
     def cleanup(self):
         self.cleaned_up = True
 

+ 54 - 3
src/supervisor/tests/test_options.py

@@ -6,6 +6,7 @@ import tempfile
 import socket
 import unittest
 import signal
+import shutil
 
 from supervisor.tests.base import DummyLogger
 from supervisor.tests.base import DummyOptions
@@ -396,7 +397,7 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertRaises(ValueError, instance.processes_from_section,
                           config, 'program:foo', None)
 
-    def test_missing_replacement_in_process_name(self):
+    def test_processes_from_section_missing_replacement_in_process_name(self):
         instance = self._makeOne()
         text = lstrip("""\
         [program:foo]
@@ -409,7 +410,7 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertRaises(ValueError, instance.processes_from_section,
                           config, 'program:foo', None)
 
-    def test_bad_expression_in_process_name(self):
+    def test_processes_from_section_bad_expression_in_process_name(self):
         instance = self._makeOne()
         text = lstrip("""\
         [program:foo]
@@ -422,6 +423,30 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertRaises(ValueError, instance.processes_from_section,
                           config, 'program:foo', None)
 
+    def test_processes_from_autolog_without_rollover(self):
+        instance = self._makeOne()
+        text = lstrip("""\
+        [program:foo]
+        command = /bin/foo
+        stdout_logfile = AUTO
+        stdout_logfile_maxbytes = 0
+        stderr_logfile = AUTO
+        stderr_logfile_maxbytes = 0
+        """)
+        from supervisor.options import UnhosedConfigParser
+        config = UnhosedConfigParser()
+        instance.logger = DummyLogger()
+        config.read_string(text)
+        processes = instance.processes_from_section(config, 'program:foo', None)
+        self.assertEqual(instance.logger.data[0],
+             'For [program:foo], AUTO logging used for stdout_logfile '
+             'without rollover, set maxbytes > 0 to avoid filling up '
+              'filesystem unintentionally')
+        self.assertEqual(instance.logger.data[1],
+             'For [program:foo], AUTO logging used for stderr_logfile '
+             'without rollover, set maxbytes > 0 to avoid filling up '
+              'filesystem unintentionally')
+        
     def test_homogeneous_process_groups_from_parser(self):
         text = lstrip("""\
         [program:many]
@@ -622,7 +647,33 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertEqual(factory[0], 'dummy')
         self.assertEqual(factory[1], sys.modules[__name__])
         self.assertEqual(factory[2], {'foo':'bar'})
-        
+
+    def test_clear_autochildlogdir(self):
+        dn = tempfile.mkdtemp()
+        try:
+            instance = self._makeOne()
+            instance.childlogdir = dn
+            sid = 'supervisor'
+            instance.identifier = sid
+            logfn = instance.get_autochildlog_name('foo', sid,'stdout')
+            first = logfn + '.1'
+            second = logfn + '.2'
+            open(first, 'w')
+            open(second, 'w')
+            instance.clear_autochildlogdir()
+            self.failIf(os.path.exists(logfn))
+            self.failIf(os.path.exists(first))
+            self.failIf(os.path.exists(second))
+        finally:
+            shutil.rmtree(dn)
+
+    def test_clear_autochildlog_oserror(self):
+        instance = self._makeOne()
+        instance.childlogdir = '/tmp/this/cant/possibly/existjjjj'
+        instance.logger = DummyLogger()
+        instance.clear_autochildlogdir()
+        self.assertEqual(instance.logger.data, ['Could not clear childlog dir'])
+
 class TestProcessConfig(unittest.TestCase):
     def _getTargetClass(self):
         from supervisor.options import ProcessConfig