Source code for pyloggr.scripts

# encoding: utf-8

"""
The `script` subpackage contains launchers for the pyloggr's processes.
"""

__author__ = 'stef'

import logging
import logging.config
import time
import psutil
import sys
from signal import signal, SIGTERM, SIGINT, SIG_IGN

from tornado.ioloop import IOLoop
from tornado.process import fork_processes
from tornado.gen import coroutine
from future.utils import lmap

from pyloggr.config import Config
from pyloggr.cache import Cache
from pyloggr.event import Event

[docs]class PyloggrProcess(object): """ Boilerplate for starting the different pyloggr processes """ def __init__(self, fork=True, shared_cache=True): self.name = self.__class__.__name__ self.logger = None self.fork = fork self.task_id = -1 self.pyloggr_process = None self.shared_cache = shared_cache
[docs] def main(self): """ main method - Initialize Redis cache - set up signal handlers - fork if necessary - run the `launch` method """ self.logger = logging.getLogger('pyloggr') Event.set_hmac_key(Config.HMAC_KEY) if self.shared_cache: Cache.initialize() signal(SIGTERM, self._parent_sig_handler) signal(SIGINT, self._parent_sig_handler) if self.fork: try: self.task_id = fork_processes(0) except OSError as ex: if ex.errno == 10: return raise # fork_processes returns as a child process # set signals for tornado children signal(SIGTERM, self._child_sig_handler) signal(SIGINT, SIG_IGN) # child process IOLoop.instance().add_callback(self._launch) self.logger.info("Starting the IOLoop for {}".format(self.task_id)) IOLoop.instance().start() # cleanly exits the child sys.exit(0) # noinspection PyUnusedLocal
def _child_sig_handler(self, sig, frame): self.logger.info('Child caught signal: {}'.format(sig)) IOLoop.instance().add_callback_from_signal(self.shutdown) # noinspection PyUnusedLocal def _parent_sig_handler(self, sig, frame): self.logger.info('Parent caught signal: {}'.format(sig)) if self.fork: # ask the children to stop current_process = psutil.Process() current_name = current_process.name() children = [child for child in current_process.children() if child.name() == current_name] [child.send_signal(SIGTERM) for child in children] # wait until everyone is dead [child.wait() for child in children] from pyloggr.utils import remove_pid_file remove_pid_file(self.name) else: # single process tornado IOLoop.instance().add_callback_from_signal(self.shutdown) @coroutine
[docs] def _launch(self): """ launch() Abstract method Note ==== Tornado coroutine """ raise NotImplementedError
@coroutine
[docs] def shutdown(self): """ shutdown() Cleanly shutdown the process Note ==== Tornado coroutine """ self.logger.info("Shutting down '{}'...".format(self.task_id)) if self.pyloggr_process: if isinstance(self.pyloggr_process, list): futures = map(lambda p: p.shutdown(), self.pyloggr_process) yield futures else: yield self.pyloggr_process.shutdown() io_loop = IOLoop.instance() def _stop_sleepers(): # noinspection PyUnresolvedReferences,PyProtectedMember sleepers = [timeout for timeout in io_loop._timeouts if getattr(timeout.callback, 'sleeper', None)] lmap(io_loop.remove_timeout, sleepers) max_wait = getattr(Config, 'MAX_WAIT_SECONDS_BEFORE_SHUTDOWN', 10) deadline = time.time() + max_wait countdown = max_wait def _stop_loop(counter): self.logger.debug(counter) # get rid of sleepers _stop_sleepers() now = time.time() # noinspection PyProtectedMember if now < deadline and (io_loop._callbacks or io_loop._timeouts): io_loop.call_later(1, _stop_loop, counter - 1) else: io_loop.stop() self.logger.info("We stopped the IOLoop for '{}'".format(self.task_id)) _stop_loop(countdown) Cache.shutdown() if (self.task_id == -1) and (not self.fork): from pyloggr.utils import remove_pid_file remove_pid_file(self.name)