import re import logging from Plugin import PluginManager from Config import config from Debug import Debug from util import SafeRe from util.Flag import flag class WsLogStreamer(logging.StreamHandler): def __init__(self, stream_id, ui_websocket, filter): self.stream_id = stream_id self.ui_websocket = ui_websocket if filter: if not SafeRe.isSafePattern(filter): raise Exception("Not a safe prex pattern") self.filter_re = re.compile(".*" + filter) else: self.filter_re = None return super(WsLogStreamer, self).__init__() def emit(self, record): if self.ui_websocket.ws.closed: self.stop() return line = self.format(record) if self.filter_re and not self.filter_re.match(line): return False self.ui_websocket.cmd("logLineAdd", {"stream_id": self.stream_id, "lines": [line]}) def stop(self): logging.getLogger('').removeHandler(self) @PluginManager.registerTo("UiWebsocket") class UiWebsocketPlugin(object): def __init__(self, *args, **kwargs): self.log_streamers = {} return super(UiWebsocketPlugin, self).__init__(*args, **kwargs) @flag.no_multiuser @flag.admin def actionConsoleLogRead(self, to, filter=None, read_size=32 * 1024, limit=500): log_file_path = "%s/debug.log" % config.log_dir log_file = open(log_file_path, encoding="utf-8") log_file.seek(0, 2) end_pos = log_file.tell() log_file.seek(max(0, end_pos - read_size)) if log_file.tell() != 0: log_file.readline() # Partial line junk pos_start = log_file.tell() lines = [] if filter: assert SafeRe.isSafePattern(filter) filter_re = re.compile(".*" + filter) last_match = False for line in log_file: if not line.startswith("[") and last_match: # Multi-line log entry lines.append(line.replace(" ", " ")) continue if filter and not filter_re.match(line): last_match = False continue last_match = True lines.append(line) num_found = len(lines) lines = lines[-limit:] return {"lines": lines, "pos_end": log_file.tell(), "pos_start": pos_start, "num_found": num_found} def addLogStreamer(self, stream_id, filter=None): logger = WsLogStreamer(stream_id, self, filter) logger.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)-8s %(name)s %(message)s')) logger.setLevel(logging.getLevelName("DEBUG")) logging.getLogger('').addHandler(logger) return logger @flag.no_multiuser @flag.admin def actionConsoleLogStream(self, to, filter=None): stream_id = to self.log_streamers[stream_id] = self.addLogStreamer(stream_id, filter) self.response(to, {"stream_id": stream_id}) @flag.no_multiuser @flag.admin def actionConsoleLogStreamRemove(self, to, stream_id): try: self.log_streamers[stream_id].stop() del self.log_streamers[stream_id] return "ok" except Exception as err: return {"error": Debug.formatException(err)}