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 .history_monitor import HistoryMonitor, BashMonitor, GDBMonitor
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 tfw import EventHandlerBase
from tfw.mixins import SupervisorMixin
from tfw.mixins import SupervisorMixin, SupervisorLogMixin
from tfw.config.logs import logging
from .directory_monitor import with_monitor_paused
LOG = logging.getLogger(__name__)
class ProcessManager(SupervisorMixin):
class ProcessManager(SupervisorMixin, SupervisorLogMixin):
def __init__(self):
self.commands = {'start': self.start_process,
'stop': self.stop_process,
@ -34,22 +34,24 @@ class ProcessManagingEventHandler(EventHandlerBase):
Commands available: start, stop, restart, readlog
(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)
self.key = key
self.monitor = dirmonitor
self.processmanager = ProcessManager()
self.log_tail = log_tail
@with_monitor_paused
def handle_event(self, message):
try:
data = message['data']
self.processmanager(data['command'], data['process_name'])
message['data']['log'] = self.processmanager.read_log_stdout(message['data']['process_name'])
try:
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
except KeyError:
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.
# 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 .observer_mixin import ObserverMixin
from .monitor_manager_mixin import MonitorManagerMixin

View File

@ -7,10 +7,10 @@ LOG = logging.getLogger(__name__)
class MonitorManagerMixin:
def __init__(self, monitor_type, directories):
def __init__(self, monitor_type, *monitor_args):
self._monitor_type = monitor_type
self._monitor = None
self._monitored_directories = directories
self._monitor_args = monitor_args
self.reload_monitor()
@property
@ -23,5 +23,5 @@ class MonitorManagerMixin:
self._monitor.stop()
except KeyError:
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

View File

@ -9,9 +9,21 @@ from os import remove
from tfw.config import TFWENV
class SupervisorMixin:
supervisor = xmlrpc.client.ServerProxy(TFWENV.SUPERVISOR_HTTP_URI).supervisor
def get_supervisor_instance():
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):
with suppress(SupervisorFault):
self.supervisor.stopProcess(process_name)
@ -19,22 +31,20 @@ class SupervisorMixin:
def start_process(self, 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):
self.stop_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]
user=root
logfile = /tmp/supervisord.log
loglevel = debug
pidfile = /tmp/supervisord.pid