Merge pull request #32 from avatao-content/console

Console
This commit is contained in:
therealkrispet 2018-05-30 14:30:17 +02:00 committed by GitHub
commit 84ea46ae76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 111 additions and 32 deletions

View File

@ -7,3 +7,4 @@ from .terminal_event_handler import TerminalEventHandler
from .ide_event_handler import IdeEventHandler from .ide_event_handler import IdeEventHandler
from .history_monitor import HistoryMonitor, BashMonitor, GDBMonitor from .history_monitor import HistoryMonitor, BashMonitor, GDBMonitor
from .terminal_commands import TerminalCommands from .terminal_commands import TerminalCommands
from .log_monitoring_event_handler import LogMonitoringEventHandler

View File

@ -0,0 +1,52 @@
# Copyright (C) 2018 Avatao.com Innovative Learning Kft.
# All Rights Reserved. See LICENSE file for details.
import logging
from os.path import dirname
from watchdog.events import PatternMatchingEventHandler as PatternMatchingWatchdogEventHandler
from tfw.networking.event_handlers import ServerUplinkConnector
from tfw.decorators import RateLimiter
from tfw.mixins import ObserverMixin, SupervisorLogMixin
class LogMonitor(ObserverMixin):
def __init__(self, process_name, log_tail=0):
self.prevent_log_recursion()
ObserverMixin.__init__(self)
event_handler = SendLogWatchdogEventHandler(process_name, log_tail=log_tail)
self.observer.schedule(
event_handler,
event_handler.path
)
@staticmethod
def prevent_log_recursion():
# This is done to prevent inotify event logs triggering themselves (infinite log recursion)
logging.getLogger('watchdog.observers.inotify_buffer').propagate = False
class SendLogWatchdogEventHandler(PatternMatchingWatchdogEventHandler, SupervisorLogMixin):
def __init__(self, process_name, log_tail=0):
self.acquire_own_supervisor_instance() # This thread-localises the xmlrpc client
self.process_name = process_name
self.procinfo = self.supervisor.getProcessInfo(self.process_name)
super().__init__([self.procinfo['stdout_logfile'], self.procinfo['stderr_logfile']])
self.uplink = ServerUplinkConnector()
self.log_tail = log_tail
@property
def path(self):
return dirname(self.procinfo['stdout_logfile'])
@RateLimiter(rate_per_second=5)
def on_modified(self, event):
self.uplink.send({
'key': 'processlog',
'data': {
'command': 'new_log',
'stdout': self.read_stdout(self.process_name, tail=self.log_tail),
'stderr': self.read_stderr(self.process_name, tail=self.log_tail)
}
})

View File

@ -0,0 +1,15 @@
# Copyright (C) 2018 Avatao.com Innovative Learning Kft.
# All Rights Reserved. See LICENSE file for details.
from tfw import EventHandlerBase
from tfw.mixins import MonitorManagerMixin
from .log_monitor import LogMonitor
class LogMonitoringEventHandler(EventHandlerBase, MonitorManagerMixin):
def __init__(self, process_name, log_tail=0):
MonitorManagerMixin.__init__(self, LogMonitor, process_name, log_tail)
def handle_event(self, message):
pass

View File

@ -4,14 +4,14 @@
from xmlrpc.client import Fault as SupervisorFault from xmlrpc.client import Fault as SupervisorFault
from tfw import EventHandlerBase from tfw import EventHandlerBase
from tfw.mixins import SupervisorMixin from tfw.mixins import SupervisorMixin, SupervisorLogMixin
from tfw.config.logs import logging from tfw.config.logs import logging
from .directory_monitor import with_monitor_paused from .directory_monitor import with_monitor_paused
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class ProcessManager(SupervisorMixin): class ProcessManager(SupervisorMixin, SupervisorLogMixin):
def __init__(self): def __init__(self):
self.commands = {'start': self.start_process, self.commands = {'start': self.start_process,
'stop': self.stop_process, 'stop': self.stop_process,
@ -34,22 +34,24 @@ class ProcessManagingEventHandler(EventHandlerBase):
Commands available: start, stop, restart, readlog Commands available: start, stop, restart, readlog
(the names are as self-documenting as it gets) (the names are as self-documenting as it gets)
""" """
def __init__(self, key, dirmonitor=None): def __init__(self, key, dirmonitor=None, log_tail=0):
super().__init__(key) super().__init__(key)
self.key = key self.key = key
self.monitor = dirmonitor self.monitor = dirmonitor
self.processmanager = ProcessManager() self.processmanager = ProcessManager()
self.log_tail = log_tail
@with_monitor_paused @with_monitor_paused
def handle_event(self, message): def handle_event(self, message):
try: try:
data = message['data'] data = message['data']
self.processmanager(data['command'], data['process_name']) try:
message['data']['log'] = self.processmanager.read_log_stdout(message['data']['process_name']) self.processmanager(data['command'], data['process_name'])
except SupervisorFault as fault:
message['data']['error'] = fault.faultString
finally:
message['data']['stdout'] = self.processmanager.read_stdout(data['process_name'], self.log_tail)
message['data']['stderr'] = self.processmanager.read_stderr(data['process_name'], self.log_tail)
return message return message
except KeyError: except KeyError:
LOG.error('IGNORING MESSAGE: Invalid message received: %s', message) LOG.error('IGNORING MESSAGE: Invalid message received: %s', message)
except SupervisorFault as fault:
message['data']['error'] = fault.faultString
message['data']['log'] = self.processmanager.read_log_stderr(message['data']['process_name'])
return message

View File

@ -1,7 +1,7 @@
# Copyright (C) 2018 Avatao.com Innovative Learning Kft. # Copyright (C) 2018 Avatao.com Innovative Learning Kft.
# All Rights Reserved. See LICENSE file for details. # All Rights Reserved. See LICENSE file for details.
from .supervisor_mixin import SupervisorMixin from .supervisor_mixin import SupervisorMixin, SupervisorLogMixin
from .callback_mixin import CallbackMixin from .callback_mixin import CallbackMixin
from .observer_mixin import ObserverMixin from .observer_mixin import ObserverMixin
from .monitor_manager_mixin import MonitorManagerMixin from .monitor_manager_mixin import MonitorManagerMixin

View File

@ -7,10 +7,10 @@ LOG = logging.getLogger(__name__)
class MonitorManagerMixin: class MonitorManagerMixin:
def __init__(self, monitor_type, directories): def __init__(self, monitor_type, *monitor_args):
self._monitor_type = monitor_type self._monitor_type = monitor_type
self._monitor = None self._monitor = None
self._monitored_directories = directories self._monitor_args = monitor_args
self.reload_monitor() self.reload_monitor()
@property @property
@ -23,5 +23,5 @@ class MonitorManagerMixin:
self._monitor.stop() self._monitor.stop()
except KeyError: except KeyError:
LOG.debug('Working directory was removed ignoring...') LOG.debug('Working directory was removed ignoring...')
self._monitor = self._monitor_type(self._monitored_directories) self._monitor = self._monitor_type(*self._monitor_args)
self._monitor.watch() # This runs on a separate thread self._monitor.watch() # This runs on a separate thread

View File

@ -9,9 +9,21 @@ from os import remove
from tfw.config import TFWENV from tfw.config import TFWENV
class SupervisorMixin: def get_supervisor_instance():
supervisor = xmlrpc.client.ServerProxy(TFWENV.SUPERVISOR_HTTP_URI).supervisor return xmlrpc.client.ServerProxy(TFWENV.SUPERVISOR_HTTP_URI).supervisor
class SupervisorBaseMixin:
supervisor = get_supervisor_instance()
def acquire_own_supervisor_instance(self):
"""
Give this instance non-static, local xmlrpc client
"""
self.supervisor = get_supervisor_instance()
class SupervisorMixin(SupervisorBaseMixin):
def stop_process(self, process_name): def stop_process(self, process_name):
with suppress(SupervisorFault): with suppress(SupervisorFault):
self.supervisor.stopProcess(process_name) self.supervisor.stopProcess(process_name)
@ -19,22 +31,20 @@ class SupervisorMixin:
def start_process(self, process_name): def start_process(self, process_name):
self.supervisor.startProcess(process_name) self.supervisor.startProcess(process_name)
def read_log_stdout(self, process_name):
return self._read_log_internal(self.supervisor.readProcessStdoutLog, process_name)
def read_log_stderr(self, process_name):
return self._read_log_internal(self.supervisor.readProcessStderrLog, process_name)
def _read_log_internal(self, read_method, process_name):
log = read_method(process_name, 0, 0)
self.clear_logs(process_name)
return log
def clear_logs(self, process_name):
for logfile in ('stdout_logfile', 'stderr_logfile'):
remove(self.supervisor.getProcessInfo(process_name)[logfile])
self.supervisor.clearProcessLogs(process_name)
def restart_process(self, process_name): def restart_process(self, process_name):
self.stop_process(process_name) self.stop_process(process_name)
self.start_process(process_name) self.start_process(process_name)
class SupervisorLogMixin(SupervisorBaseMixin):
def read_stdout(self, process_name, tail=0):
return self.supervisor.readProcessStdoutLog(process_name, -tail, 0)
def read_stderr(self, process_name, tail=0):
return self.supervisor.readProcessStderrLog(process_name, -tail, 0)
def clear_logs(self, process_name):
for logfile in ('stdout_logfile', 'stderr_logfile'):
with suppress(FileNotFoundError):
remove(self.supervisor.getProcessInfo(process_name)[logfile])
self.supervisor.clearProcessLogs(process_name)

View File

@ -1,6 +1,5 @@
[supervisord] [supervisord]
user=root user=root
logfile = /tmp/supervisord.log
loglevel = debug loglevel = debug
pidfile = /tmp/supervisord.pid pidfile = /tmp/supervisord.pid