[Zope-Checkins] CVS: Zope - z2.py:1.68

Chris McDonough chrism@zope.com
Tue, 11 Jun 2002 18:01:25 -0400


Update of /cvs-repository/Zope
In directory cvs.zope.org:/tmp/cvs-serv23837

Modified Files:
	z2.py 
Log Message:
These patches provide clean signal handling and logfile rotation to Zope.

All Zope process will respond to signals in the specified manner:

  SIGHUP -  close open database connections and sockets, then restart the
            process

  SIGTERM - close open database connections and sockets, then shut down.

  SIGINT  - same as SIGTERM

  SIGUSR2 - rotate all Zope log files (z2.log, event log, detailed log)

The common idiom for doing automated logfile rotation will become:

kill -USR2 `cat /path/to/var/z2.pid`

The common idiom for doing "prophylactic" restarts will become:

kill -HUP `cat /path/to/var/z2.pid`

When a process is interrupted via ctrl-C or via a TERM signal (INT, TERM),
all open database connections and sockets will be closed before
the process dies.  This will speed up restart time for sites that
use a FileStorage as its index will be written to the filesystem before
shutdown. 

Unspecified signals kill the process without doing cleanup.



=== Zope/z2.py 1.67 => 1.68 ===
 Zpid=''
 
+
+#   Install signal handlers.
+#   SIGTERM - cleanly shuts down.
+#   SIGHUP - cleanly restarts.
+#   SIGUSR2 - open and close all log files (for log rotation)
+
+if os.name == 'posix':  # signal.signal() not reliable on Windows
+    import signal
+    def closeall():
+        zLOG.LOG('Z2', zLOG.INFO, "Closing all open network connections")
+        for socket in asyncore.socket_map.values():
+            try:
+                socket.close()
+            except:
+                pass
+
+        zLOG.LOG('Z2', zLOG.INFO, "Closing all open ZODB databases")
+        import Globals
+        for db in Globals.opened:
+            try:
+                db.close()
+            finally:
+                pass
+
+    def sighandler(signum, frame):
+        signame = zdaemon.Daemon.get_signal_name(signum)
+        zLOG.LOG('Z2', zLOG.INFO , "Caught signal %s" % signame)
+
+        if signum in [signal.SIGTERM, signal.SIGINT]:
+            closeall()
+            zLOG.LOG('Z2', zLOG.INFO , "Shutting down")
+            sys.exit(0)
+
+        if signum == signal.SIGHUP:
+            closeall()
+            zLOG.LOG('Z2', zLOG.INFO , "Restarting")
+            sys.exit(1)
+
+        if signum == signal.SIGUSR2:
+            zLOG.LOG('Z2', zLOG.INFO , "Reopening log files")
+            if hasattr(sys, '__lg') and hasattr(sys.__lg, 'reopen'):
+                sys.__lg.reopen()
+                zLOG.LOG('Z2', zLOG.BLATHER, "Reopened Z2.log")
+            if (hasattr(sys, '__detailedlog') and
+                hasattr(sys.__detailedlog, 'reopen')):
+                zLOG.LOG('Z2', zLOG.BLATHER,"Reopened detailed request log")
+                sys.__detailedlog.reopen()
+            if hasattr(zLOG, '_set_stupid_dest'):
+                zLOG._set_stupid_dest(None)
+            else:
+                zLOG._stupid_dest = None
+            ZLogger.stupidFileLogger._stupid_dest = None
+            zLOG.LOG('Z2', zLOG.BLATHER, "Reopened event log")
+            zLOG.LOG('Z2', zLOG.INFO, "Log files reopened successfully")
+        
+    INTERESTING_SIGNALS = {
+        signal.SIGTERM: sighandler,
+        signal.SIGHUP: sighandler,
+        signal.SIGUSR2: sighandler,
+        signal.SIGINT: sighandler,
+        }
+
+    def installsighandlers():
+        # normal kill, restart, and open&close logs respectively
+        for signum, sighandler in INTERESTING_SIGNALS.items():
+            signal.signal(signum, sighandler)
+            signame = zdaemon.Daemon.get_signal_name(signum)
+            zLOG.LOG('Z2',zLOG.BLATHER,"Installed sighandler for %s" % signame)
+
 ########################################################################
 # Configuration section
 
@@ -510,7 +579,8 @@
     import zdaemon, App.FindHomes, posix
     sys.ZMANAGED=1
     
-    zdaemon.run(sys.argv, os.path.join(CLIENT_HOME, Zpid))
+    zdaemon.run(sys.argv, os.path.join(CLIENT_HOME, Zpid),
+                INTERESTING_SIGNALS.keys())
 
 os.chdir(CLIENT_HOME)
 
@@ -535,7 +605,12 @@
     if DETAILED_LOG_FILE:
         from ZServer import DebugLogger
         logfile=os.path.join(CLIENT_HOME, DETAILED_LOG_FILE)
-        DebugLogger.log=DebugLogger.DebugLogger(logfile).log
+        zLOG.LOG('z2', zLOG.BLATHER,
+                 'Using detailed request log file %s' % logfile)
+        DL=DebugLogger.DebugLogger(logfile)
+        DebugLogger.log=DL.log
+        DebugLogger.reopen=DL.reopen
+        sys.__detailedlog=DL
 
     # Import Zope (or Main)
     exec "import "+MODULE in {}
@@ -579,16 +654,21 @@
 
     if READ_ONLY:
         lg = logger.file_logger('-') # log to stdout
+        zLOG.LOG('z2', zLOG.BLATHER, 'Logging access log to stdout')
     elif os.environ.has_key('ZSYSLOG_ACCESS'):
         if os.environ.has_key("ZSYSLOG_ACCESS_FACILITY"):
             lg = logger.syslog_logger(os.environ['ZSYSLOG_ACCESS'],facility=os.environ['ZSYSLOG_ACCESS_FACILITY'])
         else:
             lg = logger.syslog_logger(os.environ['ZSYSLOG_ACCESS'])
+        zLOG.LOG('z2', zLOG.BLATHER, 'Using local syslog access log')
     elif os.environ.has_key('ZSYSLOG_ACCESS_SERVER'):
         (addr, port) = os.environ['ZSYSLOG_ACCESS_SERVER'].split( ':')
         lg = logger.syslog_logger((addr, int(port)))
+        zLOG.LOG('z2', zLOG.BLATHER, 'Using remote syslog access log')
     else:
         lg = logger.file_logger(LOG_PATH)
+        zLOG.LOG('z2', zLOG.BLATHER, 'Using access log file %s' % LOG_PATH)
+    sys.__lg = lg
 
     # HTTP Server
     if HTTP_PORT:
@@ -786,7 +866,7 @@
     except:
         raise
 
-    # Check umask sanity.
+    # Check umask sanity and install signal handlers if we're on posix.
     if os.name == 'posix':
         # umask is silly, blame POSIX.  We have to set it to get its value.
         current_umask = os.umask(0)
@@ -796,6 +876,8 @@
             zLOG.LOG("z2", zLOG.INFO, 'Your umask of ' + current_umask + \
                      ' may be too permissive; for the security of your ' + \
                      'Zope data, it is recommended you use 077')
+        # we've deferred til now to actuall install signal handlers
+        installsighandlers()
 
 except:
     # Log startup exception and tell zdaemon not to restart us.