Skip to content

Commit ac52b23

Browse files
authored
Merge pull request #928 from yurtaev/feature/server-timing-api
Add Support "Server Timing"
2 parents 76c2c21 + d59d900 commit ac52b23

File tree

8 files changed

+113
-0
lines changed

8 files changed

+113
-0
lines changed

debug_toolbar/middleware.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,32 @@ def process_response(self, request, response):
124124
# When the toolbar will be inserted for sure, generate the stats.
125125
for panel in reversed(toolbar.enabled_panels):
126126
panel.generate_stats(request, response)
127+
panel.generate_server_timing(request, response)
128+
129+
response = self.generate_server_timing_header(response, toolbar.enabled_panels)
127130

128131
bits[-2] += toolbar.render_toolbar()
129132
response.content = insert_before.join(bits)
130133
if response.get('Content-Length', None):
131134
response['Content-Length'] = len(response.content)
132135
return response
136+
137+
@staticmethod
138+
def generate_server_timing_header(response, panels):
139+
data = []
140+
141+
for panel in panels:
142+
stats = panel.get_server_timing_stats()
143+
if not stats:
144+
continue
145+
146+
for key, record in stats.items():
147+
# example: `SQLPanel_sql_time=0; "SQL 0 queries"`
148+
data.append('{}_{}={}; "{}"'.format(panel.panel_id,
149+
key,
150+
record.get('value'),
151+
record.get('title')))
152+
153+
if data:
154+
response['Server-Timing'] = ', '.join(data)
155+
return response

debug_toolbar/panels/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,21 @@ def get_stats(self):
147147
"""
148148
return self.toolbar.stats.get(self.panel_id, {})
149149

150+
def record_server_timing(self, key, title, value):
151+
"""
152+
Store data gathered by the panel. ``stats`` is a :class:`dict`.
153+
154+
Each call to ``record_stats`` updates the statistics dictionary.
155+
"""
156+
data = {key: dict(title=title, value=value)}
157+
self.toolbar.server_timing_stats.setdefault(self.panel_id, {}).update(data)
158+
159+
def get_server_timing_stats(self):
160+
"""
161+
Access data stored by the panel. Returns a :class:`dict`.
162+
"""
163+
return self.toolbar.server_timing_stats.get(self.panel_id, {})
164+
150165
# Standard middleware methods
151166

152167
def process_request(self, request):
@@ -192,6 +207,16 @@ def generate_stats(self, request, response):
192207
Does not return a value.
193208
"""
194209

210+
def generate_server_timing(self, request, response):
211+
"""
212+
Similar to :meth:`generate_stats
213+
<debug_toolbar.panels.Panel.generate_stats>`,
214+
215+
Generate stats for Server Timing https://w3c.github.io/server-timing/
216+
217+
Does not return a value.
218+
"""
219+
195220

196221
# Backward-compatibility for 1.0, remove in 2.0.
197222
class DebugPanel(Panel):

debug_toolbar/panels/cache.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,9 @@ def generate_stats(self, request, response):
224224
'misses': self.misses,
225225
'counts': self.counts,
226226
})
227+
228+
def generate_server_timing(self, request, response):
229+
stats = self.get_stats()
230+
value = stats.get('total_time', 0)
231+
title = 'Cache {} Calls'.format(stats.get('total_calls', 0))
232+
self.record_server_timing('total_time', title, value)

debug_toolbar/panels/sql/panel.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,9 @@ def generate_stats(self, request, response):
240240
'queries': [q for a, q in self._queries],
241241
'sql_time': self._sql_time,
242242
})
243+
244+
def generate_server_timing(self, request, response):
245+
stats = self.get_stats()
246+
title = 'SQL {} queries'.format(len(stats.get('queries', [])))
247+
value = stats.get('sql_time', 0)
248+
self.record_server_timing('sql_time', title, value)

debug_toolbar/panels/timer.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,13 @@ def generate_stats(self, request, response):
8282

8383
self.record_stats(stats)
8484

85+
def generate_server_timing(self, request, response):
86+
stats = self.get_stats()
87+
88+
self.record_server_timing('utime', 'User CPU time', stats.get('utime', 0))
89+
self.record_server_timing('stime', 'System CPU time', stats.get('stime', 0))
90+
self.record_server_timing('total', 'Total CPU time', stats.get('total', 0))
91+
self.record_server_timing('total_time', 'Elapsed time', stats.get('total_time', 0))
92+
8593
def _elapsed_ru(self, name):
8694
return getattr(self._end_rusage, name) - getattr(self._start_rusage, name)

debug_toolbar/toolbar.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(self, request):
2727
panel_instance = panel_class(self)
2828
self._panels[panel_instance.panel_id] = panel_instance
2929
self.stats = {}
30+
self.server_timing_stats = {}
3031
self.store_id = None
3132

3233
# Manage panels

tests/panels/test_cache.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,25 @@ def test_insert_content(self):
4848
# ensure the panel renders correctly.
4949
self.assertIn('café', self.panel.content)
5050
self.assertValidHTML(self.panel.content)
51+
52+
def test_generate_server_timin(self):
53+
self.assertEqual(len(self.panel.calls), 0)
54+
cache.cache.set('foo', 'bar')
55+
cache.cache.get('foo')
56+
cache.cache.delete('foo')
57+
58+
self.assertEqual(len(self.panel.calls), 3)
59+
60+
self.panel.generate_stats(self.request, self.response)
61+
self.panel.generate_server_timing(self.request, self.response)
62+
63+
stats = self.panel.get_stats()
64+
65+
expected_data = {
66+
'total_time': {
67+
'title': 'Cache {} Calls'.format(stats['total_calls']),
68+
'value': stats['total_time']
69+
}
70+
}
71+
72+
self.assertEqual(self.panel.get_server_timing_stats(), expected_data)

tests/panels/test_sql.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,28 @@ def test_recording(self):
5050
# ensure the stacktrace is populated
5151
self.assertTrue(len(query[1]['stacktrace']) > 0)
5252

53+
def test_generate_server_timing(self):
54+
self.assertEqual(len(self.panel._queries), 0)
55+
56+
list(User.objects.all())
57+
58+
self.panel.process_response(self.request, self.response)
59+
self.panel.generate_stats(self.request, self.response)
60+
self.panel.generate_server_timing(self.request, self.response)
61+
62+
# ensure query was logged
63+
self.assertEqual(len(self.panel._queries), 1)
64+
query = self.panel._queries[0]
65+
66+
expected_data = {
67+
'sql_time': {
68+
'title': 'SQL 1 queries',
69+
'value': query[1]['duration']
70+
}
71+
}
72+
73+
self.assertEqual(self.panel.get_server_timing_stats(), expected_data)
74+
5375
def test_non_ascii_query(self):
5476
self.assertEqual(len(self.panel._queries), 0)
5577

0 commit comments

Comments
 (0)