def __init__(self): super(ConsoleStatusReporter, self).__init__() self._last_datapoint = None self.__streams_redirected = False self.logger_handlers = [] self.orig_streams = {} self.temp_stream = StringIONotifying(self.log_updated) self.screen_size = (140, 35) self.disabled = False self.console = None self.executor_widgets = [] self.screen = DummyScreen(self.screen_size[0], self.screen_size[1])
def _get_screen(self): screen_type = self._get_screen_type() if screen_type == "console": return ConsoleScreen() elif screen_type == "gui": return GUIScreen() else: cols = self.settings.get('dummy-cols', self.screen_size[0]) rows = self.settings.get('dummy-rows', self.screen_size[1]) return DummyScreen(cols, rows)
class ConsoleStatusReporter(Reporter, AggregatorListener, Singletone): """ Class to show process status on the console :type logger_handlers: list[StreamHandler] """ # NOTE: maybe should use separate thread for screen re-painting def __init__(self): super(ConsoleStatusReporter, self).__init__() self._last_datapoint = None self.__streams_redirected = False self.logger_handlers = [] self.orig_streams = {} self.temp_stream = StringIONotifying(self.log_updated) self.screen_size = (140, 35) self.disabled = False self.console = None self.executor_widgets = [] self.screen = DummyScreen(self.screen_size[0], self.screen_size[1]) def _get_screen(self): screen_type = self._get_screen_type() if screen_type == "console": return ConsoleScreen() elif screen_type == "gui": return GUIScreen() else: cols = self.settings.get('dummy-cols', self.screen_size[0]) rows = self.settings.get('dummy-rows', self.screen_size[1]) return DummyScreen(cols, rows) def _get_screen_type(self): screen_type = self.settings.get("screen", "console") if screen_type not in ("console", "gui", "dummy"): self.log.info("Invalid screen type %r, trying 'console'", screen_type) screen_type = "console" if not sys.stdout.isatty(): self.log.debug("Not in terminal, using dummy screen") screen_type = "dummy" if screen_type == "console": if ConsoleScreen is DummyScreen or is_windows(): self.log.debug("Can't use 'console' screen, trying 'gui'") screen_type = "gui" if screen_type == "gui" and GUIScreen is DummyScreen: self.log.debug("Can't use 'gui' screen, trying 'dummy'") screen_type = "dummy" return screen_type def prepare(self): """ Prepare console screen objects, logger, ask for widgets """ super(ConsoleStatusReporter, self).prepare() if isinstance(self.engine.aggregator, ResultsProvider): self.engine.aggregator.add_listener(self) disable = self.settings.get('disable', 'auto') explicit_disable = isinstance(disable, (bool, int)) and disable auto_disable = str( disable).lower() == 'auto' and not sys.stdout.isatty() if explicit_disable or auto_disable or self.engine.is_functional_mode( ): self.disabled = True return self.screen = self._get_screen() widgets = [] modules = [self.engine.provisioning ] # must create new list to not alter existing modules += self.engine.reporters modules += self.engine.services if isinstance(self.engine.provisioning, Local): modules += self.engine.provisioning.executors for module in modules: if isinstance(module, WidgetProvider): widget = module.get_widget() widgets.append(widget) if isinstance(widget, ExecutorWidget): self.executor_widgets.append(widget) self.console = TaurusConsole(widgets) self.screen.register_palette(self.console.palette) def check(self): """ Repaint the screen """ if self.disabled: if self._last_datapoint: self.__print_one_line_stats() self._last_datapoint = None return False self.__start_screen() for widget in self.executor_widgets: widget.update() self.__update_screen() return False def __print_one_line_stats(self): cur = self._last_datapoint[DataPoint.CURRENT][''] line = "Current: %s vu\t%s succ\t%s fail\t%.3f avg rt" stats = (cur[KPISet.CONCURRENCY], cur[KPISet.SUCCESSES], cur[KPISet.FAILURES], cur[KPISet.AVG_RESP_TIME]) cumul = self._last_datapoint[DataPoint.CUMULATIVE][''] line += "\t/\t" # separator line += "Cumulative: %.3f avg rt, %d%% failures" stats += (cumul[KPISet.AVG_RESP_TIME], 100 * (cumul[KPISet.FAILURES] / cumul[KPISet.SAMPLE_COUNT])) self.log.info(line % stats) def __start_screen(self): """ Start GUIScreen on windows or urwid.curses_display on *nix :return: """ if not self.screen.started: self.__redirect_streams() self.screen.start() self.log.info("Waiting for finish...") def __update_screen(self): """ update screen size, update log entries call screen.__repaint() :return: """ if self.screen.started: self.console.tick() self.screen_size = self.screen.get_cols_rows() self.console.update_log(self.temp_stream) try: self.__repaint() except KeyboardInterrupt: raise except BaseException as exc: self.log.error("Console screen failure: %s", exc) self.log.debug("%s", traceback.format_exc()) self.shutdown() def aggregated_second(self, data): """ Consume aggregate data and feed it to console screen :type data: bzt.modules.aggregator.DataPoint :return: """ self._last_datapoint = data if self.disabled: return try: self.console.add_data(data) except BaseException as exc: self.log.warning("Failed to add datapoint to display: %s", exc) self.log.debug("%s", traceback.format_exc()) def startup(self): super(ConsoleStatusReporter, self).startup() self.log.info("Waiting for results...") def shutdown(self): """ Stop showing the screen """ super(ConsoleStatusReporter, self).shutdown() if self.disabled: return self.screen.stop() self.__dump_saved_log() def post_process(self): super(ConsoleStatusReporter, self).post_process() self.__dump_saved_log() def __detect_console_logger(self): logger = self.log while logger: for handler in logger.handlers[:]: if isinstance(handler, StreamHandler): if handler.stream in (sys.stdout, sys.stderr): self.logger_handlers.append(handler) if logger.root == logger: break else: logger = logger.root def __redirect_streams(self): if self.__streams_redirected: return if isinstance(self.screen, DummyScreen): return if sys.stdout.isatty(): if not is_windows(): self.__detect_console_logger() if self.orig_streams: raise TaurusInternalException( "Console: original streams already set") elif self.logger_handlers and not self.orig_streams: self.log.debug("Overriding logging streams") for handler in self.logger_handlers: self.orig_streams[handler] = handler.stream handler.stream = self.temp_stream self.log.debug("Redirected logging streams, %s/%s", self.logger_handlers, self.orig_streams) self.__streams_redirected = True else: self.log.info("Did not mute console logging") def __dump_saved_log(self): """ Dump data from background logging buffer to orig_stream """ if self.logger_handlers and self.orig_streams: # dump what we have in our background logging stream self.log.debug("Restoring logging streams, %s/%s", self.logger_handlers, self.orig_streams) for handler in self.logger_handlers[:]: handler.stream = self.orig_streams[handler] self.temp_stream.seek(0) handler.stream.write(self.temp_stream.getvalue()) self.logger_handlers.remove(handler) self.orig_streams.pop(handler) self.temp_stream.truncate(0) else: self.log.debug("No logger_handler or orig_stream was detected") def __repaint(self): if self.screen.started: canvas = self.console.render(self.screen_size, focus=False) self.screen.draw_screen(self.screen_size, canvas) def log_updated(self): """ Notification for log changes, to repaint log widget """ self.console.update_log(self.temp_stream) # we need to repaint, otherwise graceful shutdown messages not visible self.__repaint()
class ConsoleStatusReporter(Reporter, AggregatorListener, Singletone): """ Class to show process status on the console :type logger_handlers: list[StreamHandler] """ # NOTE: maybe should use separate thread for screen re-painting def __init__(self): super(ConsoleStatusReporter, self).__init__() self._last_datapoint = None self.__streams_redirected = False self.logger_handlers = [] self.orig_streams = {} self.temp_stream = StringIONotifying(self.log_updated) self.screen_size = (140, 35) self.disabled = False self.console = None self.executor_widgets = [] self.screen = DummyScreen(self.screen_size[0], self.screen_size[1]) def _get_screen(self): screen_type = self._get_screen_type() if screen_type == "console": return ConsoleScreen() elif screen_type == "gui": return GUIScreen() else: cols = self.settings.get('dummy-cols', self.screen_size[0]) rows = self.settings.get('dummy-rows', self.screen_size[1]) return DummyScreen(cols, rows) def _get_screen_type(self): screen_type = self.settings.get("screen", "console") if screen_type not in ("console", "gui", "dummy"): self.log.info("Invalid screen type %r, trying 'console'", screen_type) screen_type = "console" if not sys.stdout.isatty(): self.log.debug("Not in terminal, using dummy screen") screen_type = "dummy" if screen_type == "console": if ConsoleScreen is DummyScreen or is_windows(): self.log.debug("Can't use 'console' screen, trying 'gui'") screen_type = "gui" if screen_type == "gui" and GUIScreen is DummyScreen: self.log.debug("Can't use 'gui' screen, trying 'dummy'") screen_type = "dummy" return screen_type def prepare(self): """ Prepare console screen objects, logger, ask for widgets """ super(ConsoleStatusReporter, self).prepare() if isinstance(self.engine.aggregator, ResultsProvider): self.engine.aggregator.add_listener(self) disable = self.settings.get('disable', 'auto') explicit_disable = isinstance(disable, (bool, int)) and disable auto_disable = str(disable).lower() == 'auto' and not sys.stdout.isatty() if explicit_disable or auto_disable or self.engine.is_functional_mode(): self.disabled = True return self.screen = self._get_screen() widgets = [] modules = [self.engine.provisioning] # must create new list to not alter existing modules += self.engine.reporters modules += self.engine.services if isinstance(self.engine.provisioning, Local): modules += self.engine.provisioning.executors for module in modules: if isinstance(module, WidgetProvider): widget = module.get_widget() widgets.append(widget) if isinstance(widget, ExecutorWidget): self.executor_widgets.append(widget) self.console = TaurusConsole(widgets) self.screen.register_palette(self.console.palette) def check(self): """ Repaint the screen """ if self.disabled: if self._last_datapoint: self.__print_one_line_stats() self._last_datapoint = None return False self.__start_screen() for widget in self.executor_widgets: widget.update() self.__update_screen() return False def __print_one_line_stats(self): cur = self._last_datapoint[DataPoint.CURRENT][''] line = "Current: %s vu\t%s succ\t%s fail\t%.3f avg rt" stats = (cur[KPISet.CONCURRENCY], cur[KPISet.SUCCESSES], cur[KPISet.FAILURES], cur[KPISet.AVG_RESP_TIME]) cumul = self._last_datapoint[DataPoint.CUMULATIVE][''] line += "\t/\t" # separator line += "Cumulative: %.3f avg rt, %d%% failures" stats += (cumul[KPISet.AVG_RESP_TIME], 100 * (cumul[KPISet.FAILURES] / cumul[KPISet.SAMPLE_COUNT])) self.log.info(line % stats) def __start_screen(self): """ Start GUIScreen on windows or urwid.curses_display on *nix :return: """ if not self.screen.started: self.__redirect_streams() self.screen.start() self.log.info("Waiting for finish...") def __update_screen(self): """ update screen size, update log entries call screen.__repaint() :return: """ if self.screen.started: self.console.tick() self.screen_size = self.screen.get_cols_rows() self.console.update_log(self.temp_stream) try: self.__repaint() except KeyboardInterrupt: raise except BaseException as exc: self.log.error("Console screen failure: %s", exc) self.log.debug("%s", traceback.format_exc()) self.shutdown() def aggregated_second(self, data): """ Consume aggregate data and feed it to console screen :type data: bzt.modules.aggregator.DataPoint :return: """ self._last_datapoint = data if self.disabled: return try: self.console.add_data(data) except BaseException as exc: self.log.warning("Failed to add datapoint to display: %s", exc) self.log.debug("%s", traceback.format_exc()) def startup(self): super(ConsoleStatusReporter, self).startup() self.log.info("Waiting for results...") def shutdown(self): """ Stop showing the screen """ super(ConsoleStatusReporter, self).shutdown() if self.disabled: return self.screen.stop() self.__dump_saved_log() def post_process(self): super(ConsoleStatusReporter, self).post_process() self.__dump_saved_log() def __detect_console_logger(self): logger = self.log while logger: for handler in logger.handlers[:]: if isinstance(handler, StreamHandler): if handler.stream in (sys.stdout, sys.stderr): self.logger_handlers.append(handler) if logger.root == logger: break else: logger = logger.root def __redirect_streams(self): if self.__streams_redirected: return if isinstance(self.screen, DummyScreen): return if sys.stdout.isatty(): if not is_windows(): self.__detect_console_logger() if self.orig_streams: raise TaurusInternalException("Console: original streams already set") elif self.logger_handlers and not self.orig_streams: self.log.debug("Overriding logging streams") for handler in self.logger_handlers: self.orig_streams[handler] = handler.stream handler.stream = self.temp_stream self.log.debug("Redirected logging streams, %s/%s", self.logger_handlers, self.orig_streams) self.__streams_redirected = True else: self.log.info("Did not mute console logging") def __dump_saved_log(self): """ Dump data from background logging buffer to orig_stream """ if self.logger_handlers and self.orig_streams: # dump what we have in our background logging stream self.log.debug("Restoring logging streams, %s/%s", self.logger_handlers, self.orig_streams) for handler in self.logger_handlers[:]: handler.stream = self.orig_streams[handler] self.temp_stream.seek(0) handler.stream.write(self.temp_stream.getvalue()) self.logger_handlers.remove(handler) self.orig_streams.pop(handler) self.temp_stream.truncate(0) else: self.log.debug("No logger_handler or orig_stream was detected") def __repaint(self): if self.screen.started: canvas = self.console.render(self.screen_size, focus=False) self.screen.draw_screen(self.screen_size, canvas) def log_updated(self): """ Notification for log changes, to repaint log widget """ self.console.update_log(self.temp_stream) # we need to repaint, otherwise graceful shutdown messages not visible self.__repaint()
class ConsoleStatusReporter(Reporter, AggregatorListener): """ Class to show process status on the console :type logger_handlers: list[logging.StreamHandler] """ # NOTE: maybe should use separate thread for screen re-painting def __init__(self): super(ConsoleStatusReporter, self).__init__() self.__report_wait_logged = False self.__streams_redirected = False self.logger_handlers = [] self.orig_streams = {} self.temp_stream = StringIONotifying(self.log_updated) self.screen_size = (140, 35) self.data_started = False self.disabled = False self.console = None self.screen = DummyScreen(self.screen_size[0], self.screen_size[1]) def _get_screen(self): screen_type = self._get_screen_type() if screen_type == "console": return ConsoleScreen() elif screen_type == "gui": return GUIScreen() else: cols = self.settings.get('dummy-cols', self.screen_size[0]) rows = self.settings.get('dummy-rows', self.screen_size[1]) return DummyScreen(cols, rows) def _get_screen_type(self): screen_type = self.settings.get("screen", "console") if screen_type not in ("console", "gui", "dummy"): self.log.info("Invalid screen type %r, trying 'console'", screen_type) screen_type = "console" if not sys.stdout.isatty(): self.log.debug("Not in terminal, using dummy screen") screen_type = "dummy" if screen_type == "console": if ConsoleScreen is None or is_windows(): self.log.debug("Can't use console' screen, trying 'gui'") screen_type = "gui" if screen_type == "gui" and GUIScreen is None: self.log.debug("Can't use 'gui' screen, trying 'dummy'") screen_type = "dummy" return screen_type def prepare(self): """ Prepare console screen objects, logger, ask for widgets """ super(ConsoleStatusReporter, self).prepare() self.disabled = self.settings.get("disable", False) if self.disabled: return self.screen = self._get_screen() widgets = [] modules = [self.engine.provisioning] # must create new list to not alter existing modules += self.engine.reporters modules += self.engine.services if isinstance(self.engine.provisioning, Local): modules += self.engine.provisioning.executors for module in modules: if isinstance(module, WidgetProvider): widgets.append(module.get_widget()) self.console = TaurusConsole(widgets) self.screen.register_palette(self.console.palette) if isinstance(self.engine.aggregator, ResultsProvider): self.engine.aggregator.add_listener(self) def check(self): """ Repaint the screen """ if self.disabled: self.log.info("Test is running...") return False if not self.data_started: if not self.__report_wait_logged: self.log.info("Waiting to get first results...") self.__report_wait_logged = True return False self.__start_screen() self.__update_screen() return False def __start_screen(self): """ Start GUIScreen on windows or urwid.curses_display on *nix :return: """ if self.data_started and not self.screen.started: self.__redirect_streams() self.screen.start() self.log.info("Waiting for finish...") def __update_screen(self): """ update screen size, update log entries call screen.__repaint() :return: """ if self.screen.started: self.console.tick() self.screen_size = self.screen.get_cols_rows() self.console.update_log(self.temp_stream) try: self.__repaint() except KeyboardInterrupt: raise except BaseException: self.log.error("Console screen failure: %s", traceback.format_exc()) self.shutdown() def aggregated_second(self, data): """ Consume aggregate data and feed it to console screen :type data: bzt.modules.aggregator.DataPoint :return: """ if self.disabled: return try: self.console.add_data(data) except BaseException: self.log.warn("Failed to add datapoint to display: %s", traceback.format_exc()) self.data_started = True def shutdown(self): """ Stop showing the screen """ super(ConsoleStatusReporter, self).shutdown() if self.disabled: return self.screen.stop() self.__dump_saved_log() def post_process(self): super(ConsoleStatusReporter, self).post_process() self.__dump_saved_log() def __detect_console_logger(self): logger = self.log while logger: for handler in logger.handlers[:]: if isinstance(handler, StreamHandler): if handler.stream in (sys.stdout, sys.stderr): self.logger_handlers.append(handler) if logger.root == logger: break else: logger = logger.root def __redirect_streams(self): if self.__streams_redirected: return if sys.stdout.isatty(): if not is_windows(): self.__detect_console_logger() if self.data_started and not self.screen.started: if self.orig_streams: raise RuntimeError("Orig streams already set") elif self.logger_handlers and not self.orig_streams: self.log.debug("Overriding logging streams") for handler in self.logger_handlers: self.orig_streams[handler] = handler.stream handler.stream = self.temp_stream self.log.debug("Redirected logging streams, %s/%s", self.logger_handlers, self.orig_streams) self.__streams_redirected = True else: self.log.info("Did not mute console logging") def __dump_saved_log(self): """ Dump data from background logging buffer to orig_stream """ if self.logger_handlers and self.orig_streams: # dump what we have in our background logging stream self.log.debug("Restoring logging streams, %s/%s", self.logger_handlers, self.orig_streams) for handler in self.logger_handlers[:]: handler.stream = self.orig_streams[handler] self.temp_stream.seek(0) handler.stream.write(self.temp_stream.getvalue()) self.logger_handlers.remove(handler) self.orig_streams.pop(handler) self.temp_stream.truncate(0) else: self.log.debug("No logger_handler or orig_stream was detected") def __repaint(self): if self.screen.started: canvas = self.console.render(self.screen_size, focus=False) self.screen.draw_screen(self.screen_size, canvas) def log_updated(self): """ Notification for log changes, to repaint log widget """ self.console.update_log(self.temp_stream) # we need to repaint, otherwise graceful shutdown messages not visible self.__repaint()