From 190c35db9e3fc6ee4f59edfb3e31d4fcf22c4b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20G=C3=B6ransson?= Date: Wed, 4 May 2022 10:34:29 +0200 Subject: [PATCH 1/2] print_stats table width fix for #2084 --- locust/stats.py | 30 +++++++++++++++++------------ locust/test/test_stats.py | 40 +++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/locust/stats.py b/locust/stats.py index c6f1aa9680..5a5f675a3b 100644 --- a/locust/stats.py +++ b/locust/stats.py @@ -525,11 +525,11 @@ def to_string(self, current=True): rps = self.total_rps fail_per_sec = self.total_fail_per_sec return ( - " %-" + "%-" + str(STATS_TYPE_WIDTH) + "s %-" - + str(STATS_NAME_WIDTH) - + "s %7d %12s | %7d %7d %7d %7d | %7.2f %11.2f" + + str(STATS_NAME_WIDTH - STATS_TYPE_WIDTH) + + "s %7d %12s | %7d %7d %7d %7d | %7.2f %11.2f" ) % ( (self.method and self.method + " " or ""), self.name, @@ -602,7 +602,7 @@ def percentile(self): if not self.num_requests: raise ValueError("Can't calculate percentile on url with no successful requests") - tpl = f" %-{str(STATS_TYPE_WIDTH)}s %-{str(STATS_NAME_WIDTH)}s %8d {' '.join(['%6d'] * len(PERCENTILES_TO_REPORT))}" + tpl = f"%-{str(STATS_TYPE_WIDTH)}s %-{str(STATS_NAME_WIDTH)}s %8d {' '.join(['%6d'] * len(PERCENTILES_TO_REPORT))}" return tpl % ( (self.method or "", self.name) @@ -730,10 +730,16 @@ def on_worker_report(client_id, data): def print_stats(stats, current=True): console_logger.info( - (" %-" + str(STATS_TYPE_WIDTH) + "s %-" + str(STATS_NAME_WIDTH) + "s %7s %12s | %7s %7s %7s %7s | %7s %11s") + ( + "%-" + + str(STATS_TYPE_WIDTH) + + "s %-" + + str(STATS_NAME_WIDTH - STATS_TYPE_WIDTH) + + "s %7s %12s | %7s %7s %7s %7s | %7s %11s" + ) % ("Type", "Name", "# reqs", "# fails", "Avg", "Min", "Max", "Median", "req/s", "failures/s") ) - separator = f'{"-" * STATS_TYPE_WIDTH}|{"-" * STATS_NAME_WIDTH}|{"-" * 8}|{"-" * (13 + 1)}|{"-" * 8}|{"-" * 7}|{"-" * 7}|{"-" * (7 + 2)}|{"-" * 8}|{"-" * 12}|' + separator = f'{"-" * STATS_TYPE_WIDTH}|{"-" * (STATS_NAME_WIDTH - STATS_TYPE_WIDTH)}|{"-" * 7}|{"-" * 13}|{"-" * 8}|{"-" * 7}|{"-" * 7}|{"-" * 8}|{"-" * 8}|{"-" * 11}' console_logger.info(separator) for key in sorted(stats.entries.keys()): r = stats.entries[key] @@ -748,14 +754,14 @@ def print_percentile_stats(stats): headers = ("Type", "Name") + tuple(get_readable_percentiles(PERCENTILES_TO_REPORT)) + ("# reqs",) console_logger.info( ( - f" %-{str(STATS_TYPE_WIDTH)}s %-{str(STATS_NAME_WIDTH)}s %8s " + f"%-{str(STATS_TYPE_WIDTH)}s %-{str(STATS_NAME_WIDTH)}s %8s " f"{' '.join(['%6s'] * len(PERCENTILES_TO_REPORT))}" ) % headers ) separator = ( - f'{"-" * STATS_TYPE_WIDTH}|{"-" * STATS_NAME_WIDTH}|{"-" * 9}|{("-" * 6 + "|") * len(PERCENTILES_TO_REPORT)}' - ) + f'{"-" * STATS_TYPE_WIDTH}|{"-" * STATS_NAME_WIDTH}|{"-" * 8}|{("-" * 6 + "|") * len(PERCENTILES_TO_REPORT)}' + )[:-1] console_logger.info(separator) for key in sorted(stats.entries.keys()): r = stats.entries[key] @@ -772,11 +778,11 @@ def print_error_report(stats): if not len(stats.errors): return console_logger.info("Error report") - console_logger.info(" %-18s %-100s" % ("# occurrences", "Error")) - separator = f'{"-" * 18}|{"-" * ((80 + STATS_NAME_WIDTH) - 19)}|' + console_logger.info("%-18s %-100s" % ("# occurrences", "Error")) + separator = f'{"-" * 18}|{"-" * ((80 + STATS_NAME_WIDTH) - 19)}' console_logger.info(separator) for error in stats.errors.values(): - console_logger.info(" %-18i %-100s" % (error.occurrences, error.to_name())) + console_logger.info("%-18i %-100s" % (error.occurrences, error.to_name())) console_logger.info(separator) console_logger.info("") diff --git a/locust/test/test_stats.py b/locust/test/test_stats.py index ce55b1fa86..2b5f8a55e6 100644 --- a/locust/test/test_stats.py +++ b/locust/test/test_stats.py @@ -11,7 +11,15 @@ from locust import HttpUser, TaskSet, task, User, constant, __version__ from locust.env import Environment from locust.rpc.protocol import Message -from locust.stats import CachedResponseTimes, RequestStats, StatsEntry, diff_response_time_dicts, PERCENTILES_TO_REPORT +from locust.stats import ( + CachedResponseTimes, + RequestStats, + StatsEntry, + diff_response_time_dicts, + PERCENTILES_TO_REPORT, + STATS_NAME_WIDTH, + STATS_TYPE_WIDTH, +) from locust.stats import StatsCSVFileWriter from locust.stats import stats_history from locust.test.testcases import LocustTestCase @@ -309,14 +317,26 @@ def setUp(self): self.stats = RequestStats() for i in range(100): - self.stats.log_request("GET", "test_entry", i, 2000 + i) - if i % 5 == 0: - self.stats.log_error("GET", "test_entry", RuntimeError("error")) + for method, name, freq in [ + ( + "GET", + "test_entry", + 5, + ), + ( + "DELETE", + "test" * int((STATS_NAME_WIDTH - STATS_TYPE_WIDTH) / len("test")), + 3, + ), + ]: + self.stats.log_request(method, name, i, 2000 + i) + if i % freq == 0: + self.stats.log_error(method, name, RuntimeError(f"{method} error")) def test_print_percentile_stats(self): locust.stats.print_percentile_stats(self.stats) info = self.mocked_log.info - self.assertEqual(7, len(info)) + self.assertEqual(8, len(info)) self.assertEqual("Response time percentiles (approximated)", info[0]) # check that headline contains same number of column as the value rows headlines = info[1].replace("# reqs", "#reqs").split() @@ -327,12 +347,12 @@ def test_print_percentile_stats(self): def test_print_stats(self): locust.stats.print_stats(self.stats) info = self.mocked_log.info - self.assertEqual(6, len(info)) + self.assertEqual(7, len(info)) headlines = info[0].replace("# ", "#").split() - # check number of columns in separator, which will end with a pipe as well - self.assertEqual(len(headlines), len(info[1].split("|")) + 1) + # check number of columns in separator + self.assertEqual(len(headlines), len(info[1].split("|")) + 2) # check entry row self.assertEqual(len(headlines), len(info[2].split())) # check aggregated row, which is missing value in "type"-column @@ -343,12 +363,12 @@ def test_print_stats(self): def test_print_error_report(self): locust.stats.print_error_report(self.stats) info = self.mocked_log.info - self.assertEqual(6, len(info)) + self.assertEqual(7, len(info)) self.assertEqual("Error report", info[0]) headlines = info[1].replace("# ", "#").split() # check number of columns in headlines vs table ascii separator - self.assertEqual(len(headlines), len(info[2].split("|")) - 1) + self.assertEqual(len(headlines), len(info[2].split("|"))) # table ascii seprators self.assertEqual(info[2], info[-2]) From 411061acb086a5170bb5fa3cddc134ed728747c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20G=C3=B6ransson?= Date: Wed, 4 May 2022 11:23:39 +0200 Subject: [PATCH 2/2] increase request name column by an additional 4 characters by compacting other columns, and changing `Median` header to `Med`. --- locust/stats.py | 17 ++++++----------- locust/test/test_stats.py | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/locust/stats.py b/locust/stats.py index 5a5f675a3b..740de26638 100644 --- a/locust/stats.py +++ b/locust/stats.py @@ -528,8 +528,8 @@ def to_string(self, current=True): "%-" + str(STATS_TYPE_WIDTH) + "s %-" - + str(STATS_NAME_WIDTH - STATS_TYPE_WIDTH) - + "s %7d %12s | %7d %7d %7d %7d | %7.2f %11.2f" + + str((STATS_NAME_WIDTH - STATS_TYPE_WIDTH) + 4) + + "s %7d %12s |%7d %7d %7d%7d | %7.2f %11.2f" ) % ( (self.method and self.method + " " or ""), self.name, @@ -729,17 +729,12 @@ def on_worker_report(client_id, data): def print_stats(stats, current=True): + name_column_width = (STATS_NAME_WIDTH - STATS_TYPE_WIDTH) + 4 # saved characters by compacting other columns console_logger.info( - ( - "%-" - + str(STATS_TYPE_WIDTH) - + "s %-" - + str(STATS_NAME_WIDTH - STATS_TYPE_WIDTH) - + "s %7s %12s | %7s %7s %7s %7s | %7s %11s" - ) - % ("Type", "Name", "# reqs", "# fails", "Avg", "Min", "Max", "Median", "req/s", "failures/s") + ("%-" + str(STATS_TYPE_WIDTH) + "s %-" + str(name_column_width) + "s %7s %12s |%7s %7s %7s%7s | %7s %11s") + % ("Type", "Name", "# reqs", "# fails", "Avg", "Min", "Max", "Med", "req/s", "failures/s") ) - separator = f'{"-" * STATS_TYPE_WIDTH}|{"-" * (STATS_NAME_WIDTH - STATS_TYPE_WIDTH)}|{"-" * 7}|{"-" * 13}|{"-" * 8}|{"-" * 7}|{"-" * 7}|{"-" * 8}|{"-" * 8}|{"-" * 11}' + separator = f'{"-" * STATS_TYPE_WIDTH}|{"-" * (name_column_width)}|{"-" * 7}|{"-" * 13}|{"-" * 7}|{"-" * 7}|{"-" * 7}|{"-" * 7}|{"-" * 8}|{"-" * 11}' console_logger.info(separator) for key in sorted(stats.entries.keys()): r = stats.entries[key] diff --git a/locust/test/test_stats.py b/locust/test/test_stats.py index 2b5f8a55e6..5c72d4d75a 100644 --- a/locust/test/test_stats.py +++ b/locust/test/test_stats.py @@ -325,7 +325,7 @@ def setUp(self): ), ( "DELETE", - "test" * int((STATS_NAME_WIDTH - STATS_TYPE_WIDTH) / len("test")), + "test" * int((STATS_NAME_WIDTH - STATS_TYPE_WIDTH + 4) / len("test")), 3, ), ]: