diff --git a/locust/stats.py b/locust/stats.py index e74d0dca9a..c3ac5d2bf0 100644 --- a/locust/stats.py +++ b/locust/stats.py @@ -31,6 +31,21 @@ CachedResponseTimes = namedtuple("CachedResponseTimes", ["response_times", "num_requests"]) +PERCENTILES_TO_REPORT = [ + 0.50, + 0.66, + 0.75, + 0.80, + 0.90, + 0.95, + 0.98, + 0.99, + 0.999, + 0.9999, + 0.99999, + 1.0 +] + class RequestStatsAdditionError(Exception): pass @@ -754,9 +769,6 @@ def write_stat_csvs(base_filepath, stats_history_enabled=False): with open(base_filepath + '_stats.csv', 'w') as f: f.write(requests_csv()) - with open(base_filepath + '_response_times.csv', 'w') as f: - f.write(distribution_csv()) - with open(base_filepath + '_stats_history.csv', 'a') as f: f.write(stats_history_csv(stats_history_enabled) + "\n") @@ -768,7 +780,7 @@ def sort_stats(stats): def requests_csv(): from . import runners - """Returns the contents of the 'requests' tab as CSV.""" + """Returns the contents of the 'requests' & 'distribution' tab as CSV.""" rows = [ ",".join([ '"Type"', @@ -781,12 +793,30 @@ def requests_csv(): '"Max response time"', '"Average Content Size"', '"Requests/s"', - '"Requests Failed/s' + '"Requests Failed/s', + '"50%"', + '"66%"', + '"75%"', + '"80%"', + '"90%"', + '"95%"', + '"98%"', + '"99%"', + '"99.9%"', + '"99.99%"', + '"99.999"', + '"100%"' ]) ] for s in chain(sort_stats(runners.locust_runner.request_stats), [runners.locust_runner.stats.total]): - rows.append('"%s","%s",%i,%i,%i,%i,%i,%i,%i,%.2f,%.2f' % ( + if s.num_requests: + percentile_str = ','.join([ + str(int(s.get_current_response_time_percentile(x) or 0)) for x in PERCENTILES_TO_REPORT]) + else: + percentile_str = ','.join(['"N/A"'] * len(PERCENTILES_TO_REPORT)) + + rows.append('"%s","%s",%i,%i,%i,%i,%i,%i,%i,%.2f,%.2f,%s' % ( s.method, s.name, s.num_requests, @@ -797,38 +827,11 @@ def requests_csv(): s.max_response_time, s.avg_content_length, s.total_rps, - s.total_fail_per_sec + s.total_fail_per_sec, + percentile_str )) return "\n".join(rows) -def distribution_csv(): - """Returns the contents of the 'distribution' tab as CSV.""" - from . import runners - - rows = [",".join(( - '"Type"' - '"Name"', - '"# requests"', - '"50%"', - '"66%"', - '"75%"', - '"80%"', - '"90%"', - '"95%"', - '"98%"', - '"99%"', - '"99.9%"', - '"99.99%"', - '"100%"', - ))] - for s in chain(sort_stats(runners.locust_runner.request_stats), [runners.locust_runner.stats.total]): - if s.num_requests: - rows.append(s.percentile(tpl='"%s","%s",%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i')) - else: - rows.append('"%s","%s",0,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A"' % (s.method, s.name)) - - return "\n".join(rows) - def stats_history_csv_header(): """Headers for the stats history CSV""" @@ -865,22 +868,8 @@ def stats_history_csv(stats_history_enabled=False): rows = [] timestamp = int(time.time()) - PERCENTILES_TO_REPORT = [ - 0.50, - 0.66, - 0.75, - 0.80, - 0.90, - 0.95, - 0.98, - 0.99, - 0.999, - 0.9999, - 0.99999, - 1.0 - ] - stats_entries_per_iteration = [] + if stats_history_enabled: stats_entries_per_iteration = sort_stats(runners.locust_runner.request_stats) diff --git a/locust/test/test_web.py b/locust/test/test_web.py index e14c7f11e0..0037bff3b7 100644 --- a/locust/test/test_web.py +++ b/locust/test/test_web.py @@ -96,25 +96,6 @@ def test_request_stats_csv(self): response = requests.get("http://127.0.0.1:%i/stats/requests/csv" % self.web_port) self.assertEqual(200, response.status_code) - def test_distribution_stats_csv(self): - for i in range(19): - stats.global_stats.log_request("GET", "/test2", 400, 5612) - stats.global_stats.log_request("GET", "/test2", 1200, 5612) - response = requests.get("http://127.0.0.1:%i/stats/distribution/csv" % self.web_port) - self.assertEqual(200, response.status_code) - rows = response.text.split("\n") - # check that /test2 is present in stats - row = rows[len(rows)-2].split(",") - self.assertEqual('"GET"', row[0]) - self.assertEqual('"/test2"', row[1]) - # check total row - total_cols = rows[len(rows)-1].split(",") - self.assertEqual('"None"', total_cols[0]) - self.assertEqual('"Aggregated"', total_cols[1]) - # verify that the 95%, 98%, 99% and 100% percentiles are 1200 - for value in total_cols[-4:]: - self.assertEqual('1200', value) - def test_failure_stats_csv(self): stats.global_stats.log_error("GET", "/", Exception("Error1337")) response = requests.get("http://127.0.0.1:%i/stats/failures/csv" % self.web_port) diff --git a/locust/web.py b/locust/web.py index 00888ffc2a..ec7034b5ce 100644 --- a/locust/web.py +++ b/locust/web.py @@ -24,7 +24,7 @@ from . import runners from .runners import MasterLocustRunner -from .stats import distribution_csv, failures_csv, median_from_dict, requests_csv, sort_stats +from .stats import failures_csv, median_from_dict, requests_csv, sort_stats from .util.cache import memoize from .util.rounding import proper_round @@ -100,15 +100,6 @@ def request_stats_csv(): response.headers["Content-disposition"] = disposition return response -@app.route("/stats/distribution/csv") -def distribution_stats_csv(): - response = make_response(distribution_csv()) - file_name = "distribution_{0}.csv".format(time()) - disposition = "attachment;filename={0}".format(file_name) - response.headers["Content-type"] = "text/csv" - response.headers["Content-disposition"] = disposition - return response - @app.route("/stats/failures/csv") def failures_stats_csv(): response = make_response(failures_csv())