diff options
-rw-r--r-- | mpm/python/usrp_mpm/mpmlog.py | 42 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/rpc_server.py | 23 |
2 files changed, 64 insertions, 1 deletions
diff --git a/mpm/python/usrp_mpm/mpmlog.py b/mpm/python/usrp_mpm/mpmlog.py index b36ddf42c..cca202c9a 100644 --- a/mpm/python/usrp_mpm/mpmlog.py +++ b/mpm/python/usrp_mpm/mpmlog.py @@ -11,6 +11,8 @@ from __future__ import print_function import copy import logging from logging import CRITICAL, ERROR, WARNING, INFO, DEBUG +from logging import handlers +import collections from builtins import str # Colors @@ -26,6 +28,7 @@ RESET = str('\x1b[0m') TRACE = 1 DEFAULT_LOG_LEVEL = DEBUG +DEFAULT_LOG_BUF_SIZE = 100 class ColorStreamHandler(logging.StreamHandler): """ @@ -55,6 +58,17 @@ class ColorStreamHandler(logging.StreamHandler): record_.msg = BOLD + color + str(record_.msg) + RESET logging.StreamHandler.emit(self, record_) +class LossyQueueHandler(handlers.QueueHandler): + """ + Like QueueHandler, except it'll try and keep the youngest, not oldest, + entries. + """ + def enqueue(self, record): + """ + Replaces logging.handlers.QueueHandler.enqueue() + """ + self.queue.appendleft(record) + class MPMLogger(logging.getLoggerClass()): """ Extends the regular Python logging with level 'trace' (like UHD) @@ -67,16 +81,41 @@ class MPMLogger(logging.getLoggerClass()): self.cpp_log_buf = lib.types.log_buf.make_singleton() except ImportError: pass + self.py_log_buf = collections.deque( + maxlen=kwargs.get('log_buf_size', DEFAULT_LOG_BUF_SIZE) + ) def trace(self, *args, **kwargs): """ Extends logging for super-high verbosity """ self.log(TRACE, *args, **kwargs) + def get_log_buf(self): + """ + Return the contents of the logging queue, formatted as a list of + dictionaries. + """ + records = [] + # Note: This loop does not guarantee that all log items will be + # returned. The while loop is set up to be bounded, and to return as + # soon as is sensible. + while len(records) < self.py_log_buf.maxlen: + try: + records.append(self.py_log_buf.pop()) + except IndexError: + break + return [{ + 'name': record.name, + 'message': record.message, + 'levelname': record.levelname, + 'msecs': int(record.msecs), + } for record in records] + LOGGER = None # Logger singleton def get_main_logger( use_console=True, use_journal=False, + use_logbuf=True, console_color=True, log_default_delta=0 ): @@ -101,6 +140,9 @@ def get_main_logger( journal_formatter = logging.Formatter('[%(levelname)s] [%(module)s] %(message)s') journal_handler.setFormatter(journal_formatter) LOGGER.addHandler(journal_handler) + if use_logbuf: + queue_handler = LossyQueueHandler(LOGGER.py_log_buf) + LOGGER.addHandler(queue_handler) # Set default level: default_log_level = int(min( DEFAULT_LOG_LEVEL - log_default_delta * 10, diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py index cf3f4a21b..076103aa9 100644 --- a/mpm/python/usrp_mpm/rpc_server.py +++ b/mpm/python/usrp_mpm/rpc_server.py @@ -21,6 +21,7 @@ from gevent import monkey monkey.patch_all() from builtins import str, bytes from builtins import range +from six import iteritems from mprpc import RPCServer from usrp_mpm.mpmlog import get_main_logger from usrp_mpm.mpmutils import to_binary_str @@ -46,7 +47,7 @@ class MPMServer(RPCServer): # This is a list of methods in this class which require a claim default_claimed_methods = ['init', 'update_component', 'reclaim', 'unclaim'] # Compatibility number for MPM - MPM_COMPAT_NUM = (1, 0) + MPM_COMPAT_NUM = (1, 1) def __init__(self, state, mgr, mgr_generator=None, *args, **kwargs): self.log = get_main_logger().getChild('RPCServer') @@ -411,6 +412,26 @@ class MPMServer(RPCServer): """ return self._last_error + def get_log_buf(self, token): + """ + Return the contents of the log buffer as a list of str -> str + dictionaries. + """ + if not self._check_token_valid(token): + self.log.warning( + "Attempt to read logs without valid claim from {}".format( + self.client_host + ) + ) + err_msg = "get_log_buf() called without valid claim." + self._last_error = err_msg + raise RuntimeError(err_msg) + log_records = get_main_logger().get_log_buf() + self.log.trace("Returning %d log records.", len(log_records)) + return [ + {k: str(v) for k, v in iteritems(record)} + for record in log_records + ] def _rpc_server_process(shared_state, port, mgr, mgr_generator): |