This is my first Wiki contribution, so if you note any screaming formatting issues, please feel free to doctor them up. Thanks, JS.
I've only added part of this information. I'm leaving the part about actually setting up a Win32 Service for someone who's actually done it [Done]. For the moment at least, I intend to record some of the potential problems (and solutions) that may be encountered AFTER that has been done.
Problem
You want to run a web app as a service on Windows and you'd like to use Quixote and Medusa (this could apply to Twisted, or anything else, as well) to provide the functionality.
There are a few problems one can encounter while trying to run this type of setup as a Win32 service. We'll go through them one at a time.
Problem: How do I make it run as a service?
- === Solution ===
Here is one solution, inspired by the Zope2 service runner and other sources. Run a service process that spawns a second process; and run the Quixote/Medusa server in the second process. Stopping the service causes the second process to be killed. A key drawback of this code is that the service brings the server down hard, which could lead to corruption of logs and possibly other data sources (e.g. connected databases). A cleaner solution would signal the server, let it shut down properly, and then stop the service. Note that this is a generic solution; any Win32 process could be run as a service using this approach.
Problem: No STDOUT
A Win32 Service does not have a STDOUT. Any attempt to write to STDOUT will throw an exception:
exceptions.IOError, [Errno 9] Bad file descriptor
- === Solution === This can cause problems in two ways with Quixote/Medusa.
The first problem is that print statements in Quixote will actually go to STDOUT if you don't set up a debug log. When Quixote sets up the debug log, it replaces sys.stdout with the file object that corresponds to the debug log file (see DEBUG_LOG in your quixote.conf file). This way, prints (and other writes to sys.stdout) don't go to the real STDOUT.
The second problem is that Medusa, by default, logs accesses to STDOUT. When the http_server is instantiated, it grabs a reference to sys.stdout, and stores in it the logger object that it creates for internal use. Typically, the publisher.setup_logs() call hasn't happened yet when this is done, so that reference refers to the real STDOUT, not the quixote debog log file. As a result, any logging that Medusa does will go to STDOUT, raising exceptions.IOError, [Errno 9] Bad file descriptor
- There are three ways of dealing with this:
- Move the publisher.setup_logs() call ahead of the http_server() instantiation. This will conflate the Medusa log with your debug log, so it's not too desirable, but it will work, and it's quick and easy.
- Pass an explicit logger_object pointing to a real file to the http_server() call, telling Medusa where to log, so that it doesn't just grab sys.stdout (it's default behavior).
logger = medusa.logger.file_logger('c:\\temp\\medusa.log') server = http_server.http_server(INTERFACE, PORT, logger_object=logger)
- Pass an explicit null logger to the http_server() call, so that all of Medusa's logs get dumped in the bit bucket. This is really just a variation on the above. You'll probably want to have an ACCESS_LOG configured in your quixote.conf at this point. You'll also lose any of Medusa's own non-access-related logging efforts.
class null_logger(medusa.logger.file_logger): """ Logger object that will not log anything """ def __init__ (self): pass def __repr__ (self): return '<null logger>' % self.file def write (self, data): pass def writeline (self, line): pass def writelines (self, lines): pass def maybe_flush (self): pass def flush (self): pass def softspace (self, *args): pass def log (self, message): pass logger = null_logger() server = http_server.http_server(INTERFACE, PORT, logger_object=logger)
Problem: Running past a user logout
I've read about this problem on the web, but have not personally verified it.
Apparently, it is sometimes a problem to get the service to continue running after the user who started it logs out. I would suppose that it may also be a problem to get it running when the machine boots in this case.
- === Solution ===
The solution (that I've read on the web in a python-win32 archive http://mail.python.org/pipermail/python-win32/2002-March/000274.html) is that not only must the service be installed (as a service) by Administrator, the actual Python install must be done by Administrator.