Commit b625d776 authored by Hanno Schlichting's avatar Hanno Schlichting

Move Signals package into the ZServer project.

Moved to zopefoundation/ZServer@ee3cb3c7576df0de96d0fe96fbdc7912efed3049.
parent 8e149c4b
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Signal handling dispatcher."""
import os
import sys
import signal
from logging import getLogger
LOG = getLogger('SignalHandler')
class SignalHandler(object):
def __init__(self):
self.registry = {}
def registerHandler(self, signum, handler):
"""Register a handler function that will be called when the process
recieves the signal signum. The signum argument must be a signal
constant such as SIGTERM. The handler argument must be a function
or method that takes no arguments. Note that handlers will not
be called on non-posix platforms."""
if os.name != 'posix':
return
items = self.registry.get(signum)
if items is None:
items = self.registry[signum] = []
signal.signal(signum, self.signalHandler)
signame = get_signal_name(signum)
LOG.debug("Installed sighandler for %s" % signame)
items.insert(0, handler)
def getRegisteredSignals(self):
"""Return a list of the signals that have handlers registered. This
is used to pass the signals through to the ZDaemon code."""
return self.registry.keys()
def signalHandler(self, signum, frame):
"""Meta signal handler that dispatches to registered handlers."""
signame = get_signal_name(signum)
LOG.info("Caught signal %s" % signame)
for handler in self.registry.get(signum, []):
# Never let a bad handler prevent the standard signal
# handlers from running.
try:
handler()
except SystemExit:
# if we trap SystemExit, we can't restart
raise
except:
LOG.warn('A handler for %s failed!' % signame,
exc_info=sys.exc_info())
_signals = None
def get_signal_name(n):
"""Return the symbolic name for signal n.
Returns 'unknown' if there is no SIG name bound to n in the signal
module.
"""
global _signals
if _signals is None:
_signals = {}
for k, v in signal.__dict__.items():
startswith = getattr(k, 'startswith', None)
if startswith is None:
continue
if startswith('SIG') and not startswith('SIG_'):
_signals[v] = k
return _signals.get(n, 'signal %d' % n)
# The SignalHandler is actually a singleton.
SignalHandler = SignalHandler()
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Zope signal handlers for clean shutdown, restart and log rotation.
"""
import logging
import os
LIFETIME = True
try:
from Lifetime import (
shutdownFastHandler,
shutdownHandler,
restartHandler,
showStacks,
)
except ImportError:
LIFETIME = False
logger = logging.getLogger("Z2")
if os.name == 'nt':
try:
from WinSignalHandler import SignalHandler
except ImportError:
msg = ('Can not install signal handlers. Please install '
'(or upgrade) your pywin32 installation '
'(https://sf.net/projects/pywin32)')
logger.warning(msg)
SignalHandler = None
else:
from SignalHandler import SignalHandler
class LogfileReopenHandler(object):
"""Reopen log files on SIGUSR2.
This is registered first, so it should be called after all other
SIGUSR2 handlers.
"""
def __init__(self, loggers):
self.loggers = [log for log in loggers if log is not None]
def __call__(self):
for log in self.loggers:
log.reopen()
logger.info("Log files reopened successfully")
# On Windows, a 'reopen' is useless - the file can not be renamed
# while open, so we perform a trivial 'rotate'.
class LogfileRotateHandler(object):
"""
Rotate log files on SIGUSR2. Only called on Windows. This is
registered first, so it should be called after all other SIGUSR2
handlers.
"""
def __init__(self, loggers):
self.loggers = [log for log in loggers if log is not None]
def __call__(self):
logger.debug("Log files rotation starting...")
for log in self.loggers:
for f in log.handler_factories:
handler = f()
if hasattr(handler, 'rotate') and callable(handler.rotate):
handler.rotate()
logger.info("Log files rotation complete")
def registerZopeSignals(loggers):
from signal import SIGTERM, SIGINT
try:
from signal import SIGHUP, SIGUSR1, SIGUSR2
except ImportError:
# Windows doesn't have these (but also doesn't care what the exact
# numbers are)
SIGHUP = 1
SIGUSR1 = 10
SIGUSR2 = 12
if not SignalHandler:
return
mod_wsgi = True
try:
from mod_wsgi import version # NOQA
except ImportError:
mod_wsgi = False
if not mod_wsgi and LIFETIME:
SignalHandler.registerHandler(SIGTERM, shutdownFastHandler)
SignalHandler.registerHandler(SIGINT, shutdownHandler)
if os.name != 'nt':
if not mod_wsgi and LIFETIME:
SignalHandler.registerHandler(SIGHUP, restartHandler)
SignalHandler.registerHandler(SIGUSR1, showStacks)
SignalHandler.registerHandler(SIGUSR2, LogfileReopenHandler(loggers))
else:
# no restart handler on windows.
# Log files get 'rotated', not 'reopened'
SignalHandler.registerHandler(SIGUSR2, LogfileRotateHandler(loggers))
This diff is collapsed.
import sys
import thread
import traceback
import time
from cStringIO import StringIO
def dump_threads():
"""Dump running threads. Returns a string with the tracebacks."""
frames = sys._current_frames()
this_thread_id = thread.get_ident()
now = time.strftime("%Y-%m-%d %H:%M:%S")
res = ["Threads traceback dump at %s\n" % now]
for thread_id, frame in frames.iteritems():
if thread_id == this_thread_id:
continue
# Find request in frame
reqinfo = ''
f = frame
while f is not None:
co = f.f_code
if co.co_name == 'publish':
if co.co_filename.endswith('/ZPublisher/Publish.py'):
request = f.f_locals.get('request')
if request is not None:
reqinfo += (request.get('REQUEST_METHOD', '') + ' ' +
request.get('PATH_INFO', ''))
qs = request.get('QUERY_STRING')
if qs:
reqinfo += '?' + qs
break
f = f.f_back
if reqinfo:
reqinfo = " (%s)" % reqinfo
output = StringIO()
traceback.print_stack(frame, file=output)
res.append(
"Thread %s%s:\n%s" % (thread_id, reqinfo, output.getvalue()))
frames = None
res.append("End of dump")
return '\n'.join(res)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment