Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9abd5b72f4 | ||
|
|
2e953ea098 | ||
|
|
211ae1e2d9 | ||
|
|
84ea46ae76 | ||
|
|
73f6c6635b | ||
|
|
babb14288c | ||
|
|
3bb97a6dcc | ||
|
|
9e6cd57532 | ||
|
|
28c16e1127 | ||
|
|
4815b97200 | ||
|
|
9f6418bf60 | ||
|
|
780d6a9b3c | ||
|
|
32e3c2860d | ||
|
|
029f4a9eb2 | ||
|
|
a6d9b50b93 | ||
|
|
9723f98950 | ||
|
|
1a93dad562 | ||
|
|
44df95a434 |
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
# 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 tfw.config.logs import logging
|
||||
from .log_monitor import LogMonitor
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LogMonitoringEventHandler(EventHandlerBase, MonitorManagerMixin):
|
||||
def __init__(self, key, process_name, log_tail=0):
|
||||
super().__init__(key)
|
||||
self.process_name = process_name
|
||||
self.log_tail = log_tail
|
||||
MonitorManagerMixin.__init__(self, LogMonitor, self.process_name, self.log_tail)
|
||||
|
||||
self.command_handlers = {
|
||||
'process_name': self.handle_process_name,
|
||||
'log_tail': self.handle_log_tail
|
||||
}
|
||||
|
||||
def handle_event(self, message):
|
||||
try:
|
||||
data = message['data']
|
||||
self.command_handlers[data['command']](data)
|
||||
self.reload_monitor()
|
||||
except KeyError:
|
||||
LOG.error('IGNORING MESSAGE: Invalid message received: %s', message)
|
||||
|
||||
def handle_process_name(self, data):
|
||||
self.set_monitor_args(data['process_name'], self.log_tail)
|
||||
|
||||
def handle_log_tail(self, data):
|
||||
self.set_monitor_args(self.process_name, data['log_tail'])
|
||||
@@ -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,21 +7,24 @@ 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
|
||||
def monitor(self):
|
||||
return self._monitor
|
||||
|
||||
def set_monitor_args(self, *monitor_args):
|
||||
self.monitor_args = monitor_args
|
||||
|
||||
def reload_monitor(self):
|
||||
if self._monitor:
|
||||
try:
|
||||
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,7 @@
|
||||
[supervisord]
|
||||
user=root
|
||||
logfile = /tmp/supervisord.log
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
loglevel = debug
|
||||
pidfile = /tmp/supervisord.pid
|
||||
|
||||
|
||||
Reference in New Issue
Block a user