OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:collection'; | |
6 | |
7 import '../log/log.dart'; | 5 import '../log/log.dart'; |
8 import 'page_writer.dart'; | 6 import 'page_writer.dart'; |
9 | 7 |
10 /** | 8 /** |
11 * A page writer that will produce the page containing statistics about an | 9 * A page writer that will produce the page containing statistics about an |
12 * instrumentation log. | 10 * instrumentation log. |
13 */ | 11 */ |
14 class StatsPage extends PageWriter { | 12 class StatsPage extends PageWriter { |
15 /** | 13 /** |
16 * The instrumentation log to be written. | 14 * The instrumentation log to be written. |
17 */ | 15 */ |
18 final InstrumentationLog log; | 16 final InstrumentationLog log; |
19 | 17 |
20 /** | 18 /** |
21 * A table mapping the kinds of entries in the log to the number of each kind. | 19 * A table mapping the kinds of entries in the log to the number of each kind. |
22 */ | 20 */ |
23 final Map<String, int> entryCounts = new HashMap<String, int>(); | 21 final Map<String, int> entryCounts = <String, int>{}; |
24 | 22 |
25 /** | 23 /** |
26 * The number of responses that returned an error. | 24 * The number of responses that returned an error. |
27 */ | 25 */ |
28 int errorCount = 0; | 26 int errorCount = 0; |
29 | 27 |
30 /** | 28 /** |
| 29 * The number of responses from each plugin that returned an error. |
| 30 */ |
| 31 Map<String, int> pluginErrorCount = <String, int>{}; |
| 32 |
| 33 /** |
31 * A table mapping request method names to a list of the latencies associated | 34 * A table mapping request method names to a list of the latencies associated |
32 * with those requests, where the latency is defined to be the time between | 35 * with those requests, where the latency is defined to be the time between |
33 * when the request was sent by the client and when the server started | 36 * when the request was sent by the client and when the server started |
34 * processing the request. | 37 * processing the request. |
35 */ | 38 */ |
36 final Map<String, List<int>> latencyData = new HashMap<String, List<int>>(); | 39 final Map<String, List<int>> latencyData = <String, List<int>>{}; |
| 40 |
| 41 /** |
| 42 * A table mapping request method names to a list of the latencies associated |
| 43 * with those requests, where the latency is defined to be the time between |
| 44 * when the request was sent by the server and when the plugin sent a response
. |
| 45 */ |
| 46 final Map<String, Map<String, List<int>>> pluginResponseData = |
| 47 <String, Map<String, List<int>>>{}; |
37 | 48 |
38 /** | 49 /** |
39 * A list of the number of milliseconds between a completion request and the | 50 * A list of the number of milliseconds between a completion request and the |
40 * first event for that request. | 51 * first event for that request. |
41 */ | 52 */ |
42 final List<int> completionResponseTimes = <int>[]; | 53 final List<int> completionResponseTimes = <int>[]; |
43 | 54 |
44 /** | 55 /** |
45 * Initialize a newly created page writer to write information about the given | 56 * Initialize a newly created page writer to write information about the given |
46 * instrumentation [log]. | 57 * instrumentation [log]. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 map[key] = (map[key] ?? 0) + 1; | 93 map[key] = (map[key] ?? 0) + 1; |
83 } | 94 } |
84 | 95 |
85 for (LogEntry entry in entries) { | 96 for (LogEntry entry in entries) { |
86 String kind = entry.kind; | 97 String kind = entry.kind; |
87 increment(entryCounts, kind); | 98 increment(entryCounts, kind); |
88 if (entry is ResponseEntry) { | 99 if (entry is ResponseEntry) { |
89 if (entry.result('error') != null) { | 100 if (entry.result('error') != null) { |
90 errorCount++; | 101 errorCount++; |
91 } | 102 } |
92 } | 103 } else if (entry is RequestEntry) { |
93 | |
94 if (entry is RequestEntry) { | |
95 String method = entry.method; | 104 String method = entry.method; |
96 int latency = entry.timeStamp - entry.clientRequestTime; | 105 int latency = entry.timeStamp - entry.clientRequestTime; |
97 latencyData.putIfAbsent(method, () => new List<int>()).add(latency); | 106 latencyData.putIfAbsent(method, () => new List<int>()).add(latency); |
98 if (method == 'completion.getSuggestions') { | 107 if (method == 'completion.getSuggestions') { |
99 ResponseEntry response = log.responseFor(entry); | 108 ResponseEntry response = log.responseFor(entry); |
100 if (response != null) { | 109 if (response != null) { |
101 String id = response.result('id'); | 110 String id = response.result('id'); |
102 if (id != null) { | 111 if (id != null) { |
103 List<NotificationEntry> events = log.completionEventsWithId(id); | 112 List<NotificationEntry> events = log.completionEventsWithId(id); |
104 if (events != null && events.length > 0) { | 113 if (events != null && events.length > 0) { |
105 completionResponseTimes | 114 completionResponseTimes |
106 .add(events[0].timeStamp - entry.timeStamp); | 115 .add(events[0].timeStamp - entry.timeStamp); |
107 } | 116 } |
108 } | 117 } |
109 } | 118 } |
110 } | 119 } |
| 120 } else if (entry is PluginResponseEntry) { |
| 121 if (entry.result('error') != null) { |
| 122 int count = pluginErrorCount[entry.pluginId] ?? 0; |
| 123 pluginErrorCount[entry.pluginId] = count + 1; |
| 124 } |
| 125 } else if (entry is PluginRequestEntry) { |
| 126 PluginResponseEntry response = log.pluginResponseFor(entry); |
| 127 int responseTime = response.timeStamp - entry.timeStamp; |
| 128 var pluginData = pluginResponseData.putIfAbsent( |
| 129 entry.pluginId, () => <String, List<int>>{}); |
| 130 pluginData |
| 131 .putIfAbsent(entry.method, () => new List<int>()) |
| 132 .add(responseTime); |
111 } | 133 } |
112 } | 134 } |
113 } | 135 } |
114 | 136 |
115 void _writeLeftColumn(StringSink sink) { | 137 void _writeLeftColumn(StringSink sink) { |
116 List<String> filePaths = log.logFilePaths; | 138 List<String> filePaths = log.logFilePaths; |
117 List<LogEntry> entries = log.logEntries; | 139 List<LogEntry> entries = log.logEntries; |
118 DateTime startDate = entries[0].toTime; | 140 DateTime startDate = entries[0].toTime; |
119 DateTime endDate = entries[entries.length - 1].toTime; | 141 DateTime endDate = entries[entries.length - 1].toTime; |
120 Duration duration = endDate.difference(startDate); | 142 Duration duration = endDate.difference(startDate); |
(...skipping 25 matching lines...) Expand all Loading... |
146 sink.writeln('<br>'); | 168 sink.writeln('<br>'); |
147 sink.write('<span class="label">Duration:</span> '); | 169 sink.write('<span class="label">Duration:</span> '); |
148 sink.write(duration.toString()); | 170 sink.write(duration.toString()); |
149 sink.writeln('</p>'); | 171 sink.writeln('</p>'); |
150 | 172 |
151 sink.writeln('<h3>Entries</h3>'); | 173 sink.writeln('<h3>Entries</h3>'); |
152 sink.write('<p>'); | 174 sink.write('<p>'); |
153 sink.write('<span class="label">Number of entries:</span> '); | 175 sink.write('<span class="label">Number of entries:</span> '); |
154 sink.write(entries.length); | 176 sink.write(entries.length); |
155 sink.writeln('</p>'); | 177 sink.writeln('</p>'); |
| 178 sink.write('<p>'); |
| 179 sink.write('<span class="label">Error count:</span> '); |
| 180 sink.write(errorCount); |
| 181 sink.writeln('</p>'); |
| 182 pluginErrorCount.forEach((String pluginId, int count) { |
| 183 sink.write('<p>'); |
| 184 sink.write('<span class="label">Errors from $pluginId:</span> '); |
| 185 sink.write(count); |
| 186 sink.writeln('</p>'); |
| 187 }); |
156 sink.writeln('<table>'); | 188 sink.writeln('<table>'); |
157 sink.writeln('<tr><th>count</th><th>kind</th></tr>'); | 189 sink.writeln('<tr><th>count</th><th>kind</th></tr>'); |
158 for (String kind in entryKinds) { | 190 for (String kind in entryKinds) { |
159 sink.write('<tr><td class="int">'); | 191 sink.write('<tr><td class="int">'); |
160 sink.write(entryCounts[kind]); | 192 sink.write(entryCounts[kind]); |
161 sink.write('</td><td>'); | 193 sink.write('</td><td>'); |
162 sink.write(kind); | 194 sink.write(kind); |
163 sink.writeln('</td></tr>'); | 195 sink.writeln('</td></tr>'); |
164 } | 196 } |
165 sink.write('<tr><td class="int">'); | 197 sink.write('<tr><td class="int">'); |
166 sink.write(entries.length); | 198 sink.write(entries.length); |
167 sink.writeln('</td><td>Total</td></tr>'); | 199 sink.writeln('</td><td>Total</td></tr>'); |
168 sink.writeln('</table>'); | 200 sink.writeln('</table>'); |
169 } | 201 } |
170 | 202 |
171 void _writeRightColumn(StringSink sink) { | 203 void _writeRightColumn(StringSink sink) { |
172 List<String> methodNames = latencyData.keys.toList()..sort(); | |
173 completionResponseTimes.sort(); | 204 completionResponseTimes.sort(); |
174 | 205 |
175 sink.writeln('<h3>Latency</h3>'); | 206 sink.writeln('<h3>Latency</h3>'); |
176 sink.write('<p>'); | 207 sink.write('<p>'); |
177 sink.write('<span class="label">Latency by method</span>'); | 208 sink.write('<span class="label">Latency by method</span>'); |
178 sink.writeln('</p>'); | 209 sink.writeln('</p>'); |
179 sink.writeln('<table>'); | 210 sink.writeln('<table>'); |
180 sink.writeln( | 211 sink.writeln( |
181 '<tr><th>min</th><th>mean</th><th>max</th><th>method</th></tr>'); | 212 '<tr><th>min</th><th>mean</th><th>max</th><th>method</th></tr>'); |
| 213 List<String> methodNames = latencyData.keys.toList()..sort(); |
182 for (String method in methodNames) { | 214 for (String method in methodNames) { |
183 List<int> latencies = latencyData[method]..sort(); | 215 List<int> latencies = latencyData[method]..sort(); |
184 // TODO(brianwilkerson) Add a spark-line distribution graph. | 216 // TODO(brianwilkerson) Add a spark-line distribution graph. |
185 sink.write('<tr><td class="int">'); | 217 sink.write('<tr><td class="int">'); |
186 sink.write(latencies[0]); | 218 sink.write(latencies[0]); |
187 sink.write('</td><td class="int">'); | 219 sink.write('</td><td class="int">'); |
188 sink.write(_mean(latencies)); | 220 sink.write(_mean(latencies)); |
189 sink.write('</td><td class="int">'); | 221 sink.write('</td><td class="int">'); |
190 sink.write(latencies[latencies.length - 1]); | 222 sink.write(latencies[latencies.length - 1]); |
191 sink.write('</td><td>'); | 223 sink.write('</td><td>'); |
192 sink.write(method); | 224 sink.write(method); |
193 sink.writeln('</td></tr>'); | 225 sink.writeln('</td></tr>'); |
194 } | 226 } |
195 sink.writeln('</table>'); | 227 sink.writeln('</table>'); |
196 | 228 |
197 sink.writeln('<h3>Completion</h3>'); | 229 sink.writeln('<h3>Completion</h3>'); |
198 sink.write('<p>'); | 230 sink.write('<p>'); |
199 sink.write('<span class="label">Time to first notification:</span> '); | 231 sink.write('<span class="label">Time to first notification:</span> '); |
200 sink.write(completionResponseTimes[0]); | 232 sink.write(completionResponseTimes[0]); |
201 sink.write(', '); | 233 sink.write(', '); |
202 sink.write(_mean(completionResponseTimes)); | 234 sink.write(_mean(completionResponseTimes)); |
203 sink.write(', '); | 235 sink.write(', '); |
204 sink.write(completionResponseTimes[completionResponseTimes.length - 1]); | 236 sink.write(completionResponseTimes[completionResponseTimes.length - 1]); |
205 sink.writeln('</p>'); | 237 sink.writeln('</p>'); |
| 238 |
| 239 if (pluginResponseData.isNotEmpty) { |
| 240 sink.writeln('<h3>Plugin response times</h3>'); |
| 241 pluginResponseData |
| 242 .forEach((String pluginId, Map<String, List<int>> responseData) { |
| 243 sink.write('<p>'); |
| 244 sink.write(pluginId); |
| 245 sink.writeln('</p>'); |
| 246 sink.writeln('<table>'); |
| 247 List<String> methodNames = responseData.keys.toList()..sort(); |
| 248 for (String method in methodNames) { |
| 249 List<int> responseTimes = responseData[method]..sort(); |
| 250 // TODO(brianwilkerson) Add a spark-line distribution graph. |
| 251 sink.write('<tr><td class="int">'); |
| 252 sink.write(responseTimes[0]); |
| 253 sink.write('</td><td class="int">'); |
| 254 sink.write(_mean(responseTimes)); |
| 255 sink.write('</td><td class="int">'); |
| 256 sink.write(responseTimes[responseTimes.length - 1]); |
| 257 sink.write('</td><td>'); |
| 258 sink.write(method); |
| 259 sink.writeln('</td></tr>'); |
| 260 } |
| 261 ; |
| 262 sink.writeln('</table>'); |
| 263 }); |
| 264 } |
206 } | 265 } |
207 } | 266 } |
OLD | NEW |