| 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 |