mirror of
https://github.com/avatao-content/baseimage-tutorial-framework
synced 2024-11-05 14:41:20 +00:00
commit
84ea46ae76
@ -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
|
||||
|
52
lib/tfw/components/log_monitor.py
Normal file
52
lib/tfw/components/log_monitor.py
Normal 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)
|
||||
}
|
||||
})
|
15
lib/tfw/components/log_monitoring_event_handler.py
Normal file
15
lib/tfw/components/log_monitoring_event_handler.py
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -1,6 +1,5 @@
|
||||
[supervisord]
|
||||
user=root
|
||||
logfile = /tmp/supervisord.log
|
||||
loglevel = debug
|
||||
pidfile = /tmp/supervisord.pid
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user