Skip to content

Commit

Permalink
Add Windows service
Browse files Browse the repository at this point in the history
  • Loading branch information
Yann Diorcet committed Apr 16, 2018
1 parent 377eb78 commit 1e90dab
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 14 deletions.
6 changes: 1 addition & 5 deletions circus/circusd.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def main():
sys.exit(0)

parser = argparse.ArgumentParser(description='Run some watchers.')
parser.add_argument('config', help='configuration file', nargs='?')
parser.add_argument('config', help='configuration file')

# XXX we should be able to add all these options in the config file as well
parser.add_argument('--log-level', dest='loglevel',
Expand Down Expand Up @@ -114,10 +114,6 @@ def main():
print(__version__)
sys.exit(0)

if args.config is None:
parser.print_usage()
sys.exit(0)

if args.daemonize:
daemonize()

Expand Down
98 changes: 98 additions & 0 deletions circus/circusrv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import win32serviceutil
import servicemanager
import os
import logging
import traceback

from circus.arbiter import Arbiter
from circus.util import check_future_exception_and_log, LOG_LEVELS


class ServiceManagerHandler(logging.Handler):
_map_ = {
logging.CRITICAL: servicemanager.EVENTLOG_ERROR_TYPE,
logging.ERROR: servicemanager.EVENTLOG_ERROR_TYPE,
logging.WARNING: servicemanager.EVENTLOG_WARNING_TYPE,
logging.INFO: servicemanager.EVENTLOG_INFORMATION_TYPE,
logging.DEBUG: servicemanager.EVENTLOG_INFORMATION_TYPE
}

def emit(self, record):
level = self._map_.get(record.levelno)
details = ""
if record.exc_info is not None:
formated_exc = traceback.format_exception(*record.exc_info)
details = os.linesep.join(formated_exc)
servicemanager.LogMsg(level, 0xF000, (record.getMessage(), details))


class CircusSrv(win32serviceutil.ServiceFramework):
_svc_name_ = 'circus'
_svc_display_name_ = 'Circus'
_svc_description_ = 'Run some watchers.'

_parameter_config = 'Config'
_parameter_loglevel = 'LogLevel'

def __init__(self, args):
self._svc_name_ = args[0]
super(CircusSrv, self).__init__(args)

config = win32serviceutil.GetServiceCustomOption(self._svc_name_, self._parameter_config)
loglevel = logging.INFO
try:
lls = win32serviceutil.GetServiceCustomOption(self._svc_name_, self._parameter_loglevel)
if lls is not None:
loglevel = LOG_LEVELS.get(lls.lower(), logging.INFO)
except:
pass

root_logger = logging.getLogger()
root_logger.setLevel(loglevel)
root_logger.handlers = [ServiceManagerHandler()]

# From here it can also come from the arbiter configuration
# load the arbiter from config
self.arbiter = Arbiter.load_from_config(config)

def SvcStop(self):
self.arbiter.loop.run_sync(self.arbiter._emergency_stop)

def SvcDoRun(self):
arbiter = self.arbiter
try:
future = arbiter.start()
check_future_exception_and_log(future)
except Exception as e:
# emergency stop
arbiter.loop.run_sync(arbiter._emergency_stop)
raise (e)
except KeyboardInterrupt:
pass

@classmethod
def OptionsHandler(cls, opts):
for opt, val in opts:
if opt == '-c':
win32serviceutil.SetServiceCustomOption(cls._svc_name_,
cls._parameter_config,
val)
if opt == '-l':
win32serviceutil.SetServiceCustomOption(cls._svc_name_,
cls._parameter_loglevel,
val)

# Register now the source (rights of service's user may be different)
servicemanager.SetEventSourceName(cls._svc_name_, True)


def main():
kwargs = {}
kwargs['customInstallOptions'] = 'c:l:'
kwargs['customOptionHandler'] = CircusSrv.OptionsHandler
ret = win32serviceutil.HandleCommandLine(CircusSrv, **kwargs)
sys.exit(ret)


if __name__ == '__main__':
main()
5 changes: 4 additions & 1 deletion circus/sighandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ def __init__(self, controller):
# init signals
logger.info('Registering signals...')
self._old = {}
self._register()
try:
self._register()
except ValueError as e:
logger.warning("Can't register signals: %s" % e)

def stop(self):
for sig, callback in self._old.items():
Expand Down
14 changes: 6 additions & 8 deletions circus/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import socket
import sys
import time
import traceback
import json
import struct
try:
Expand Down Expand Up @@ -1084,10 +1083,9 @@ def exception(self, timeout=None):

def check_future_exception_and_log(future):
if isinstance(future, concurrent.Future):
exception = future.exception()
if exception is not None:
logger.error("exception %s caught" % exception)
if hasattr(future, "exc_info"):
exc_info = future.exc_info()
traceback.print_tb(exc_info[2])
return exception
try:
future.result()
return None
except Exception as e:
logger.exception("exception %s caught" % e)
return e
5 changes: 5 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sys
import os
from setuptools import setup, find_packages
from circus import __version__

Expand All @@ -13,6 +14,9 @@
except ImportError:
install_requires.append('argparse')

if os.name == 'nt':
install_requires.append('pypiwin32')

with open("README.rst") as f:
README = f.read()

Expand Down Expand Up @@ -40,6 +44,7 @@
entry_points="""
[console_scripts]
circusd = circus.circusd:main
circusrv = circus.circusrv:main
circusd-stats = circus.stats:main
circusctl = circus.circusctl:main
circus-top = circus.stats.client:main
Expand Down

0 comments on commit 1e90dab

Please sign in to comment.