[Checkins] SVN: z3c.recipe.winservice/trunk/ - Added debug option to log exceptions for totally misbehaving scripts
Adam Groszer
agroszer at gmail.com
Mon Mar 23 06:07:51 EDT 2009
Log message for revision 98304:
- Added debug option to log exceptions for totally misbehaving scripts
- Slight changes to service manager
Changed:
U z3c.recipe.winservice/trunk/CHANGES.txt
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in
-=-
Modified: z3c.recipe.winservice/trunk/CHANGES.txt
===================================================================
--- z3c.recipe.winservice/trunk/CHANGES.txt 2009-03-23 09:19:29 UTC (rev 98303)
+++ z3c.recipe.winservice/trunk/CHANGES.txt 2009-03-23 10:07:51 UTC (rev 98304)
@@ -7,6 +7,10 @@
- ...
+- Added debug option to log exceptions for totally misbehaving scripts
+
+- Slight changes to service manager
+
Version 0.5.0 (2008-04-12)
--------------------------
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt 2009-03-23 09:19:29 UTC (rev 98303)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt 2009-03-23 10:07:51 UTC (rev 98304)
@@ -7,7 +7,7 @@
This Zope 3 recipes offers windows service installation support.
-The 'service' recipe installes the required scripts and files whihc can be
+The 'service' recipe installes the required scripts and files whihc can be
used for install a windwos service.
@@ -23,8 +23,8 @@
The windows service description option.
runzope
- The script name which get used by the winservice. This script must exist in
- the bin folder and before we run this recipe exist. This script can get setup
+ The script name which get used by the winservice. This script must exist in
+ the bin folder and before we run this recipe exist. This script can get setup
with the z3c.recipe.dev.app recipe.
@@ -96,7 +96,7 @@
- buildout.exe
- winservice.py
-The winservice-scrip.py contains the service setup for our zope windows service:
+The winservice.py contains the service setup for our zope windows service:
>>> cat('bin', 'winservice.py')
##############################################################################
@@ -186,10 +186,11 @@
import win32process
<BLANKLINE>
# these are replacements from winservice recipe
- PYTHON = r'C:\Python24\python.exe'
+ PYTHON = r'U:\Python24\python.exe'
PYTHONDIR = os.path.split(PYTHON)[0]
PYTHONW = os.path.join(PYTHONDIR, 'pythonw.exe')
PYTHONSERVICE_EXE = r'%s\Lib\site-packages\win32\pythonservice.exe' % PYTHONDIR
+ TOSTART = r'/sample-buildout/bin/app-script.py'
<BLANKLINE>
<BLANKLINE>
# the max seconds we're allowed to spend backing off
@@ -241,14 +242,20 @@
# each instance. The below-defined start_cmd (and _svc_display_name_
# and _svc_name_) are just examples.
<BLANKLINE>
- _svc_name_ = '870810267'
+ _svc_name_ = '..._bin_app_script_py'
_svc_display_name_ = r'Zope 3 Windows Service'
_svc_description_ = r'Zope 3 Windows Service description'
<BLANKLINE>
_exe_name_ = PYTHONSERVICE_EXE
- start_cmd = '"%s" "%s"' % (PYTHONW, r'/sample-buildout/bin/app-script.py')
+ start_cmd = '"%s" "%s"' % (PYTHONW, TOSTART)
<BLANKLINE>
def __init__(self, args):
+ if not os.path.exists(PYTHONW):
+ raise OSError("%s does not exist" % PYTHON)
+ <BLANKLINE>
+ if not os.path.exists(TOSTART):
+ raise OSError("%s does not exist" % TOSTART)
+ <BLANKLINE>
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
@@ -398,3 +405,77 @@
# This ensures that pythonservice.exe is registered...
os.system('"%s" -register' % PYTHONSERVICE_EXE)
win32serviceutil.HandleCommandLine(Zope3Service)
+
+
+Debug
+-----
+
+This option is for service scripts having fundamental problems.
+The problem with those scripts is that they are starting and stopping at once.
+Seemingly there is no output, no sing that the script did something.
+And because they run in a subprocess until those scripts have logging established
+they won't have any chance to report the error.
+For this we'll setup a bare catch-all around the whole script and log any
+exceptions to the windows event log.
+CAUTION: this takes a copy of the app-script.py but does not update the patched
+result when it changes!
+
+We can enable the ``debug`` option:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runzope = app
+ ... debug = true
+ ...
+ ... ''' % globals())
+
+Now, Let's run the buildout and see what we get:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Uninstalling winservice.
+ Installing winservice.
+
+
+The bin folder contains the windows service installer script:
+
+ >>> ls('bin')
+ - app-script.py
+ - app-servicedebug.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
+
+The winservice.py file gets changed according to the new script name:
+
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ ...TOSTART = r'/sample-buildout/bin/app-servicedebug.py'...
+ ..._svc_name_ = '...bin_app_script_py'...
+
+The debug script contains a bare catch-all and a logger:
+
+ >>> cat('bin', 'app-servicedebug.py')
+ <BLANKLINE>
+ def exceptionlogger():
+ import servicemanager
+ import traceback
+ servicemanager.LogErrorMsg("Script %s had an exception: %s" % (
+ __file__, traceback.format_exc()
+ ))
+ <BLANKLINE>
+ try:
+ <BLANKLINE>
+ dummy start script
+ <BLANKLINE>
+ except Exception, e:
+ exceptionlogger()
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py 2009-03-23 09:19:29 UTC (rev 98303)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py 2009-03-23 10:07:51 UTC (rev 98304)
@@ -17,42 +17,82 @@
"""
import os
+import string
import shutil
import zc.buildout
def installScript(srcFile, script, replacements):
generated = []
-
+
# generate the winservice script
text = open(srcFile, "rU").read()
# perform replacements
for var, string in replacements:
text = text.replace(var, string)
- # If the file exists, keep the old file. This is a
- # hopefully temporary hack to get around distutils
- # stripping the permissions on the server skeletin files.
- # We reuse the original default files, which have the
- # right permissions.
- old = os.path.exists(script)
- if old:
- f = open(script, "r+")
- f.truncate(0)
- else:
- f = open(script, "w")
+ changed = not (os.path.exists(script) and open(script).read() == text)
- f.write(text)
- f.close()
+ if changed:
+ # If the file exists, keep the old file. This is a
+ # hopefully temporary hack to get around distutils
+ # stripping the permissions on the server skeleton files.
+ # We reuse the original default files, which have the
+ # right permissions.
+ old = os.path.exists(script)
+ if old:
+ f = open(script, "r+")
+ f.truncate(0)
+ else:
+ f = open(script, "w")
- if not old:
- shutil.copymode(srcFile, script)
- shutil.copystat(srcFile, script)
+ f.write(text)
+ f.close()
+
+ if not old:
+ shutil.copymode(srcFile, script)
+ shutil.copystat(srcFile, script)
+
generated.append(script)
return generated
+def patchScript(srcFile, dstFile):
+ generated = []
+ src = open(srcFile, 'rU').read()
+
+ src = '\n'.join([' '+line for line in src.split('\n')])
+
+ dest = PATCH_TEMPLATE % dict(script=src,
+ srcFile=srcFile)
+
+ changed = not (os.path.exists(dstFile) and open(dstFile).read() == dest)
+
+ if changed:
+ open(dstFile, 'w').write(dest)
+ shutil.copymode(srcFile, dstFile)
+ shutil.copystat(srcFile, dstFile)
+
+ generated.append(dstFile)
+
+ return generated
+
+
+PATCH_TEMPLATE = '''
+def exceptionlogger():
+ import servicemanager
+ import traceback
+ servicemanager.LogErrorMsg("Script %%s had an exception: %%s" %% (
+ __file__, traceback.format_exc()
+ ))
+
+try:
+%(script)s
+except Exception, e:
+ exceptionlogger()
+'''
+
class ServiceSetup:
def __init__(self, buildout, name, options):
@@ -77,38 +117,65 @@
runzope = '%s-script.py' % runzope
self.runScript = os.path.join(binDir, runzope)
+ options['serviceName'] = self.getServiceName()
+ def getServiceName(self):
+ try:
+ serviceName = self.options['serviceName']
+ except KeyError:
+ if len(self.runScript) < 128:
+ #make a meaningful name in case it fits
+ serviceName = ''
+ for c in self.runScript:
+ if c in string.letters + string.digits:
+ serviceName += c
+ else:
+ serviceName += '_'
+ else:
+ #otherwise a dumb hash
+ serviceName = str(hash(self.runScript))
+ return serviceName
+
+ def install(self):
+ options = self.options
+
# setup service name
defaultName = 'Zope3 %s' % self.name
- defaultDescription = 'Zope3 windows service for %s' % self.name
+ defaultDescription = 'Zope3 windows service for %s, using %s' % (
+ self.name, self.runScript)
displayName = options.get('name', defaultName)
- serviceName = str(hash(displayName))
+ serviceName = options['serviceName']
description = options.get('description', defaultDescription)
+
+ generated = []
+
+ if options.get('debug'):
+ serviceScript = self.runScript.replace(
+ '-script.py', '-servicedebug.py')
+ generated += patchScript(self.runScript, serviceScript)
+ else:
+ serviceScript = self.runScript
+
self.winServiceVars = [
("<<PYTHON>>", self.executable),
- ("<<RUNZOPE>>", self.runScript),
+ ("<<RUNZOPE>>", serviceScript),
("<<SERVICE_NAME>>", serviceName),
("<<SERVICE_DISPLAY_NAME>>", displayName),
("<<SERVICE_DESCRIPTION>>", description),
]
- self.runZopeVars = []
- def install(self):
- options = self.options
- generated = []
-
# raise exeption if the app script is not here now
- if not os.path.exists(self.runScript):
+ if not os.path.exists(serviceScript):
raise zc.buildout.UserError(
- 'App start script %s does not exist in bin folder.' %
+ 'App start script %s does not exist in bin folder.' %
self.runScript)
# get templates
- binDir = self.buildout['buildout']['bin-directory']
winServiceTemplate = os.path.join(os.path.dirname(__file__),
'winservice.in')
# setup winservice file paths
+ binDir = self.buildout['buildout']['bin-directory']
script = os.path.join(binDir, 'winservice.py')
generated += installScript(winServiceTemplate, script,
@@ -117,4 +184,4 @@
# return list of generated files
return generated
- update = install
+ update = install
\ No newline at end of file
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in 2009-03-23 09:19:29 UTC (rev 98303)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in 2009-03-23 10:07:51 UTC (rev 98304)
@@ -37,7 +37,7 @@
install : Installs the service
update : Updates the service, use this when you change
- the service class implementation
+ the service class implementation
remove : Removes the service
@@ -89,6 +89,7 @@
PYTHONDIR = os.path.split(PYTHON)[0]
PYTHONW = os.path.join(PYTHONDIR, 'pythonw.exe')
PYTHONSERVICE_EXE = r'%s\Lib\site-packages\win32\pythonservice.exe' % PYTHONDIR
+TOSTART = r'<<RUNZOPE>>'
# the max seconds we're allowed to spend backing off
@@ -145,9 +146,15 @@
_svc_description_ = r'<<SERVICE_DESCRIPTION>>'
_exe_name_ = PYTHONSERVICE_EXE
- start_cmd = '"%s" "%s"' % (PYTHONW, r'<<RUNZOPE>>')
+ start_cmd = '"%s" "%s"' % (PYTHONW, TOSTART)
def __init__(self, args):
+ if not os.path.exists(PYTHONW):
+ raise OSError("%s does not exist" % PYTHON)
+
+ if not os.path.exists(TOSTART):
+ raise OSError("%s does not exist" % TOSTART)
+
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
More information about the Checkins
mailing list