-
Notifications
You must be signed in to change notification settings - Fork 3k
/
extend_web_ui.py
163 lines (133 loc) Β· 5.14 KB
/
extend_web_ui.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
"""
This is an example of a locustfile that uses Locust's built in event and web
UI extension hooks to track the sum of the content-length header in all
successful HTTP responses and display them in the web UI.
"""
from locust import HttpUser, TaskSet, between, events, task, web
import json
import os
from html import escape
from time import time
from flask import Blueprint, jsonify, make_response, render_template, request
class MyTaskSet(TaskSet):
@task(2)
def index(l):
l.client.get("/")
@task(1)
def stats(l):
l.client.get("/stats/requests")
class WebsiteUser(HttpUser):
host = "http://127.0.0.1:8089"
wait_time = between(2, 5)
tasks = [MyTaskSet]
stats = {}
path = os.path.dirname(os.path.abspath(__file__))
extend = Blueprint(
"extend",
"extend_web_ui",
static_folder=f"{path}/static/",
static_url_path="/extend/static/",
template_folder=f"{path}/templates/",
)
@events.init.add_listener
def locust_init(environment, **kwargs):
"""
We need somewhere to store the stats.
On the master node stats will contain the aggregated sum of all content-lengths,
while on the worker nodes this will be the sum of the content-lengths since the
last stats report was sent to the master
"""
if environment.web_ui:
def get_content_length_stats():
"""
This is used by the Content Length tab in the
extended web UI to show the stats.
"""
if stats:
stats_tmp = []
for name, inner_stats in stats.items():
content_length = inner_stats["content-length"]
stats_tmp.append(
{"name": name, "safe_name": escape(name, quote=False), "content_length": content_length}
)
# Truncate the total number of stats and errors displayed since a large number of rows will cause the app
# to render extremely slowly.
return stats_tmp[:500]
return stats
@environment.web_ui.app.after_request
def extend_stats_response(response):
if request.path != "/stats/requests":
return response
response.set_data(
json.dumps(
{**response.json, "extended_stats": [{"key": "content-length", "data": get_content_length_stats()}]}
)
)
return response
@extend.route("/extend")
def extend_web_ui():
"""
Add route to access the extended web UI with our new tab.
"""
# ensure the template_args are up to date before using them
environment.web_ui.update_template_args()
return render_template(
"index.html",
template_args={
**environment.web_ui.template_args,
"extended_tabs": [{"title": "Content Length", "key": "content-length"}],
"extended_tables": [
{
"key": "content-length",
"structure": [
{"key": "name", "title": "Name"},
{"key": "content_length", "title": "Total content length"},
],
}
],
"extended_csv_files": [
{"href": "/content-length/csv", "title": "Download content length statistics CSV"}
],
},
)
@extend.route("/content-length/csv")
def request_content_length_csv():
"""
Add route to enable downloading of content-length stats as CSV
"""
response = make_response(content_length_csv())
file_name = f"content_length{time()}.csv"
disposition = f"attachment;filename={file_name}"
response.headers["Content-type"] = "text/csv"
response.headers["Content-disposition"] = disposition
return response
def content_length_csv():
"""Returns the content-length stats as CSV."""
rows = [
",".join(
[
'"Name"',
'"Total content-length"',
]
)
]
if stats:
for url, inner_stats in stats.items():
rows.append(f"\"{url}\",{inner_stats['content-length']:.2f}")
return "\n".join(rows)
# register our new routes and extended UI with the Locust web UI
environment.web_ui.app.register_blueprint(extend)
@events.request.add_listener
def on_request(request_type, name, response_time, response_length, exception, context, **kwargs):
"""
Event handler that get triggered on every request
"""
stats.setdefault(name, {"content-length": 0})
stats[name]["content-length"] += response_length
@events.reset_stats.add_listener
def on_reset_stats():
"""
Event handler that get triggered on click of web UI Reset Stats button
"""
global stats
stats = {}