ConsolePlugin.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import re
  2. import logging
  3. from Plugin import PluginManager
  4. from Config import config
  5. from Debug import Debug
  6. from util import SafeRe
  7. from util.Flag import flag
  8. class WsLogStreamer(logging.StreamHandler):
  9. def __init__(self, stream_id, ui_websocket, filter):
  10. self.stream_id = stream_id
  11. self.ui_websocket = ui_websocket
  12. if filter:
  13. if not SafeRe.isSafePattern(filter):
  14. raise Exception("Not a safe prex pattern")
  15. self.filter_re = re.compile(".*" + filter)
  16. else:
  17. self.filter_re = None
  18. return super(WsLogStreamer, self).__init__()
  19. def emit(self, record):
  20. if self.ui_websocket.ws.closed:
  21. self.stop()
  22. return
  23. line = self.format(record)
  24. if self.filter_re and not self.filter_re.match(line):
  25. return False
  26. self.ui_websocket.cmd("logLineAdd", {"stream_id": self.stream_id, "lines": [line]})
  27. def stop(self):
  28. logging.getLogger('').removeHandler(self)
  29. @PluginManager.registerTo("UiWebsocket")
  30. class UiWebsocketPlugin(object):
  31. def __init__(self, *args, **kwargs):
  32. self.log_streamers = {}
  33. return super(UiWebsocketPlugin, self).__init__(*args, **kwargs)
  34. @flag.no_multiuser
  35. @flag.admin
  36. def actionConsoleLogRead(self, to, filter=None, read_size=32 * 1024, limit=500):
  37. log_file_path = "%s/debug.log" % config.log_dir
  38. log_file = open(log_file_path, encoding="utf-8")
  39. log_file.seek(0, 2)
  40. end_pos = log_file.tell()
  41. log_file.seek(max(0, end_pos - read_size))
  42. if log_file.tell() != 0:
  43. log_file.readline() # Partial line junk
  44. pos_start = log_file.tell()
  45. lines = []
  46. if filter:
  47. assert SafeRe.isSafePattern(filter)
  48. filter_re = re.compile(".*" + filter)
  49. last_match = False
  50. for line in log_file:
  51. if not line.startswith("[") and last_match: # Multi-line log entry
  52. lines.append(line.replace(" ", " "))
  53. continue
  54. if filter and not filter_re.match(line):
  55. last_match = False
  56. continue
  57. last_match = True
  58. lines.append(line)
  59. num_found = len(lines)
  60. lines = lines[-limit:]
  61. return {"lines": lines, "pos_end": log_file.tell(), "pos_start": pos_start, "num_found": num_found}
  62. def addLogStreamer(self, stream_id, filter=None):
  63. logger = WsLogStreamer(stream_id, self, filter)
  64. logger.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)-8s %(name)s %(message)s'))
  65. logger.setLevel(logging.getLevelName("DEBUG"))
  66. logging.getLogger('').addHandler(logger)
  67. return logger
  68. @flag.no_multiuser
  69. @flag.admin
  70. def actionConsoleLogStream(self, to, filter=None):
  71. stream_id = to
  72. self.log_streamers[stream_id] = self.addLogStreamer(stream_id, filter)
  73. self.response(to, {"stream_id": stream_id})
  74. @flag.no_multiuser
  75. @flag.admin
  76. def actionConsoleLogStreamRemove(self, to, stream_id):
  77. try:
  78. self.log_streamers[stream_id].stop()
  79. del self.log_streamers[stream_id]
  80. return "ok"
  81. except Exception as err:
  82. return {"error": Debug.formatException(err)}