diff foodtruck/pubutil.py @ 615:cbb170d9c894

admin: Improve publish logs showing as alerts in the admin panel. * Don't show stuff that happened before the page was opened. * Skinning tweaks.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 04 Feb 2016 21:50:18 -0800
parents e2e955a3bb25
children 65706804e1de
line wrap: on
line diff
--- a/foodtruck/pubutil.py	Thu Feb 04 21:49:15 2016 -0800
+++ b/foodtruck/pubutil.py	Thu Feb 04 21:50:18 2016 -0800
@@ -28,7 +28,20 @@
                   lambda *args: _shutdown_server_and_raise_sigint())
 
 
+def _read_pid_file(pid_file):
+    logger.debug("Reading PID file: %s" % pid_file)
+    try:
+        with open(pid_file, 'r') as fp:
+            pid_str = fp.read()
+
+        return int(pid_str.strip())
+    except Exception:
+        logger.error("Error reading PID file.")
+        raise
+
+
 def _pid_exists(pid):
+    logger.debug("Checking if process ID %d is running" % pid)
     try:
         os.kill(pid, 0)
     except OSError as ex:
@@ -44,61 +57,52 @@
         return True
 
 
-def _read_pid_file(pid_file):
-    logger.debug("Reading PID file: %s" % pid_file)
-    try:
-        with open(pid_file, 'r') as fp:
-            pid_str = fp.read()
-
-        return int(pid_str.strip())
-    except Exception:
-        logger.error("Error reading PID file.")
-        raise
-
-
 class PublishLogReader(object):
-    _pub_max_time = 10 * 60   # Don't bother about pubs older than 10mins.
     _poll_interval = 1        # Check the process every 1 seconds.
     _ping_interval = 30       # Send a ping message every 30 seconds.
 
     def __init__(self, pid_path, log_path):
         self.pid_path = pid_path
         self.log_path = log_path
-        self._pub_pid_mtime = 0
-        self._last_seek = -1
-        self._last_ping_time = 0
 
     def run(self):
         logger.debug("Opening publish log...")
         pid = None
+        pid_mtime = 0
         is_running = False
+        last_seek = -1
+        last_ping_time = 0
         try:
             while not server_shutdown:
                 # PING!
-                interval = time.time() - self._last_ping_time
+                interval = time.time() - last_ping_time
                 if interval > self._ping_interval:
                     logger.debug("Sending ping...")
-                    self._last_ping_time = time.time()
+                    last_ping_time = time.time()
                     yield bytes("event: ping\ndata: 1\n\n", 'utf8')
 
-                # Check if the PID file has changed.
+                # Check the PID file timestamp.
                 try:
                     new_mtime = os.path.getmtime(self.pid_path)
                 except OSError:
                     new_mtime = 0
 
-                if (new_mtime > 0 and
-                        time.time() - new_mtime > self._pub_max_time):
-                    new_mtime = 0
+                # If there's a valid PID file and we either just started
+                # streaming (pid_mtime == 0) or we remember an older version
+                # of that PID file (pid_mtime != new_mtime), let's read the
+                # PID from the file.
+                is_pid_file_prehistoric = False
+                if new_mtime > 0 and new_mtime != pid_mtime:
+                    is_pid_file_prehistoric = (pid_mtime == 0)
+                    pid_mtime = new_mtime
+                    pid = _read_pid_file(self.pid_path)
 
-                # Re-read the PID file.
-                prev_mtime = self._pub_pid_mtime
-                if new_mtime > 0 and new_mtime != prev_mtime:
-                    self._pub_pid_mtime = new_mtime
-                    pid = _read_pid_file(self.pid_path)
-                    if pid:
-                        logger.debug("Monitoring new process, PID: %d" % pid)
+                if is_pid_file_prehistoric:
+                    logger.debug("PID file is pre-historic, we will skip the "
+                                 "first parts of the log.")
 
+                # If we have a valid PID, let's check if the process is
+                # currently running.
                 was_running = is_running
                 if pid:
                     is_running = _pid_exists(pid)
@@ -106,29 +110,41 @@
                             "Process %d is %s" %
                             (pid, 'running' if is_running else 'not running'))
                     if not is_running:
+                        # Let's forget this PID file until it changes.
                         pid = None
                 else:
                     is_running = False
 
-                # Send data.
+                # Read new data from the log file.
                 new_data = None
                 if is_running or was_running:
-                    if self._last_seek < 0:
-                        outstr = 'event: message\ndata: Publish started.\n\n'
-                        yield bytes(outstr, 'utf8')
-                        self._last_seek = 0
+                    if last_seek < 0:
+                        # Only send the "publish started" message if we
+                        # actually caught the process as it was starting, not
+                        # if we started streaming after it started.
+                        # This means we saw the PID file get changed.
+                        if not is_pid_file_prehistoric:
+                            outstr = (
+                                    'event: message\n'
+                                    'data: Publish started.\n\n')
+                            yield bytes(outstr, 'utf8')
+                        last_seek = 0
 
                     try:
                         with open(self.log_path, 'r', encoding='utf8') as fp:
-                            fp.seek(self._last_seek)
+                            fp.seek(last_seek)
                             new_data = fp.read()
-                            self._last_seek = fp.tell()
+                            last_seek = fp.tell()
                     except OSError:
                         pass
                 if not is_running:
-                    self._last_seek = 0
+                    # Process is not running anymore, let's reset our seek
+                    # marker back to the beginning.
+                    last_seek = -1
 
-                if new_data:
+                # Stream the new data to the client, but don't send old stuff
+                # that happened before we started this stream.
+                if new_data and not is_pid_file_prehistoric:
                     logger.debug("SSE: %s" % new_data)
                     for line in new_data.split('\n'):
                         outstr = 'event: message\ndata: %s\n\n' % line