浏览代码

Fix "tail -f" hang if log rotation occurs while tailing

Conflicts:
	supervisor/http.py
	supervisor/tests/test_http.py
Mike Naberezny 11 年之前
父节点
当前提交
f51f0579db
共有 3 个文件被更改,包括 51 次插入8 次删除
  1. 3 0
      CHANGES.txt
  2. 28 6
      supervisor/http.py
  3. 20 2
      supervisor/tests/test_http.py

+ 3 - 0
CHANGES.txt

@@ -3,6 +3,9 @@
 
 - Added docs/conf.py and docs/Makefile.py to the package.
 
+- Fixed a bug where ``supervisorctl tail -f name`` output would stop if log
+  rotation occurred while tailing.
+
 3.1.0 (2014-07-29)
 ------------------
 

+ 28 - 6
supervisor/http.py

@@ -647,18 +647,22 @@ class supervisor_af_unix_http_server(supervisor_http_server):
 
 class tail_f_producer:
     def __init__(self, request, filename, head):
-        self.file = open(filename, 'rb')
         self.request = request
+        self.filename = filename
         self.delay = 0.1
-        sz = self.fsize()
+
+        self._open()
+        sz = self._fsize()
         if sz >= head:
             self.sz = sz - head
-        else:
-            self.sz = 0
+
+    def __del__(self):
+        self._close()
 
     def more(self):
+        self._follow()
         try:
-            newsz = self.fsize()
+            newsz = self._fsize()
         except OSError:
             # file descriptor was closed
             return ''
@@ -673,7 +677,25 @@ class tail_f_producer:
             return bytes
         return NOT_DONE_YET
 
-    def fsize(self):
+    def _open(self):
+        self.file = open(self.filename, 'rb')
+        self.ino = os.fstat(self.file.fileno())[stat.ST_INO]
+        self.sz = 0
+
+    def _close(self):
+        self.file.close()
+
+    def _follow(self):
+        try:
+            ino = os.stat(self.filename)[stat.ST_INO]
+        except OSError:
+            return
+
+        if self.ino != ino: # log rotation occurred
+            self._close()
+            self._open()
+
+    def _fsize(self):
         return os.fstat(self.file.fileno())[stat.ST_SIZE]
 
 class logtail_handler:

+ 20 - 2
supervisor/tests/test_http.py

@@ -124,8 +124,7 @@ class TailFProducerTests(unittest.TestCase):
         f = tempfile.NamedTemporaryFile()
         f.write('a' * 80)
         f.flush()
-        t = f.name
-        producer = self._makeOne(request, t, 80)
+        producer = self._makeOne(request, f.name, 80)
         result = producer.more()
         self.assertEqual(result, 'a' * 80)
         f.write('w' * 100)
@@ -139,6 +138,25 @@ class TailFProducerTests(unittest.TestCase):
         result = producer.more()
         self.assertEqual(result, '==> File truncated <==\n')
 
+    def test_handle_more_follow(self):
+        request = DummyRequest('/logtail/foo', None, None, None)
+        from supervisor import http
+        f = tempfile.NamedTemporaryFile()
+        f.write('a' * 80)
+        f.flush()
+        producer = self._makeOne(request, f.name, 80)
+        result = producer.more()
+        self.assertEqual(result, 'a' * 80)
+        f.close()
+        f2 = open(f.name, 'w')
+        try:
+            f2.write('b' * 80)
+            f2.close()
+            result = producer.more()
+        finally:
+            os.unlink(f2.name)
+        self.assertEqual(result, 'b' * 80)
+
 class DeferringChunkedProducerTests(unittest.TestCase):
     def _getTargetClass(self):
         from supervisor.http import deferring_chunked_producer