OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import 'dart:collection'; | |
6 | |
7 import '../log/log.dart'; | |
8 import 'page_writer.dart'; | |
9 | |
10 /** | |
11 * A page writer that will produce the page containing statistics about an | |
12 * instrumentation log. | |
13 */ | |
14 class StatsPage extends PageWriter { | |
15 /** | |
16 * The instrumentation log to be written. | |
17 */ | |
18 final InstrumentationLog log; | |
19 | |
20 /** | |
21 * A table mapping the kinds of entries in the log to the number of each kind. | |
22 */ | |
23 final Map<String, int> entryCounts = new HashMap<String, int>(); | |
24 | |
25 /** | |
26 * The number of responses that returned an error. | |
27 */ | |
28 int errorCount = 0; | |
29 | |
30 /** | |
31 * 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 | |
33 * when the request was sent by the client and when the server started | |
34 * processing the request. | |
35 */ | |
36 final Map<String, List<int>> latencyData = new HashMap<String, List<int>>(); | |
37 | |
38 /** | |
39 * A list of the number of milliseconds between a completion request and the | |
40 * first event for that request. | |
41 */ | |
42 final List<int> completionResponseTimes = <int>[]; | |
43 | |
44 /** | |
45 * Initialize a newly created page writer to write information about the given | |
46 * instrumentation [log]. | |
47 */ | |
48 StatsPage(this.log) { | |
49 _processEntries(log.logEntries); | |
50 } | |
51 | |
52 @override | |
53 void writeBody(StringSink sink) { | |
54 writeMenu(sink); | |
55 sink.writeln('<div id="container">'); | |
56 sink.writeln(' <div id="content">'); | |
57 sink.writeln(' <div id="leftColumn">'); | |
58 sink.writeln(' <div class="inset">'); | |
59 _writeLeftColumn(sink); | |
60 sink.writeln(' </div>'); | |
61 sink.writeln(' </div>'); | |
62 sink.writeln(' <div id="rightColumn">'); | |
63 sink.writeln(' <div class="inset">'); | |
64 _writeRightColumn(sink); | |
65 sink.writeln(' </div>'); | |
66 sink.writeln(' </div>'); | |
67 sink.writeln(' </div>'); | |
68 sink.writeln('</div>'); | |
69 } | |
70 | |
71 /** | |
72 * Write the content of the style sheet (without the 'script' tag) for the | |
73 * page to the given [sink]. | |
74 */ | |
75 void writeStyleSheet(StringSink sink) { | |
76 super.writeStyleSheet(sink); | |
77 sink.writeln('#leftColumn {'); | |
78 sink.writeln(' float: left;'); | |
79 sink.writeln(' height: 100%;'); | |
80 sink.writeln(' overflow: auto;'); | |
81 sink.writeln(' width: 50%;'); | |
82 sink.writeln('}'); | |
83 sink.writeln('#rightColumn {'); | |
84 sink.writeln(' float: right;'); | |
85 sink.writeln(' height: 100%;'); | |
86 sink.writeln(' overflow: auto;'); | |
87 sink.writeln(' width: 50%;'); | |
88 sink.writeln('}'); | |
89 } | |
90 | |
91 /** | |
92 * Return the mean of the values in the given list of [latencies]. | |
93 */ | |
94 int _mean(List<int> latencies) { | |
scheglov
2016/09/14 16:17:17
The parameter could have a more generic name.
Brian Wilkerson
2016/09/14 16:39:27
Done
| |
95 int sum = latencies.fold(0, (int sum, int latency) => sum + latency); | |
96 return sum ~/ latencies.length; | |
97 } | |
98 | |
99 /** | |
100 * Return a table mapping the kinds of the given [entries] to the number of | |
101 * each kind. | |
102 */ | |
103 void _processEntries(List<LogEntry> entries) { | |
104 void increment/*<K>*/(Map<dynamic/*=K*/, int> map, dynamic/*=K*/ key) { | |
105 map[key] = (map[key] ?? 0) + 1; | |
106 } | |
107 | |
108 for (LogEntry entry in entries) { | |
109 String kind = entry.kind; | |
110 increment(entryCounts, kind); | |
111 if (entry is ResponseEntry) { | |
112 if (entry.result('error') != null) { | |
113 errorCount++; | |
114 } | |
115 } | |
116 | |
117 if (entry is RequestEntry) { | |
118 String method = entry.method; | |
119 int latency = entry.timeStamp - entry.clientRequestTime; | |
120 latencyData.putIfAbsent(method, () => new List<int>()).add(latency); | |
121 if (method == 'completion.getSuggestions') { | |
122 ResponseEntry response = log.responseFor(entry); | |
123 if (response != null) { | |
124 String id = response.result('id'); | |
125 if (id != null) { | |
126 List<NotificationEntry> events = log.completionEventsWithId(id); | |
127 if (events != null && events.length > 0) { | |
128 completionResponseTimes | |
129 .add(events[0].timeStamp - entry.timeStamp); | |
130 } | |
131 } | |
132 } | |
133 } | |
134 } | |
135 } | |
136 } | |
137 | |
138 void _writeLeftColumn(StringSink sink) { | |
139 List<String> filePaths = log.logFilePaths; | |
140 List<LogEntry> entries = log.logEntries; | |
141 DateTime startDate = entries[0].toTime; | |
142 DateTime endDate = entries[entries.length - 1].toTime; | |
143 Duration duration = endDate.difference(startDate); | |
144 List<String> entryKinds = entryCounts.keys.toList()..sort(); | |
145 | |
146 sink.writeln('<h3>General</h3>'); | |
147 sink.writeln('<p>'); | |
148 if (filePaths.length == 1) { | |
149 sink.write('<span class="label">Log file:</span> '); | |
150 sink.write(filePaths[0]); | |
151 } else { | |
152 sink.write('<span class="label">Log files:</span> '); | |
153 bool needsSeparator = false; | |
154 for (String path in filePaths) { | |
155 if (needsSeparator) { | |
156 sink.write(', '); | |
157 } else { | |
158 needsSeparator = true; | |
159 } | |
160 sink.write(path); | |
161 } | |
162 } | |
163 sink.writeln('<br>'); | |
164 sink.write('<span class="label">Start time:</span> '); | |
165 writeDate(sink, startDate); | |
166 sink.writeln('<br>'); | |
167 sink.write('<span class="label">End time:</span> '); | |
168 writeDate(sink, endDate); | |
169 sink.writeln('<br>'); | |
170 sink.write('<span class="label">Duration:</span> '); | |
171 sink.write(duration.toString()); | |
172 sink.writeln('</p>'); | |
173 | |
174 sink.writeln('<h3>Entries</h3>'); | |
175 sink.write('<p>'); | |
176 sink.write('<span class="label">Number of entries:</span> '); | |
177 sink.write(entries.length); | |
178 sink.writeln('</p>'); | |
179 sink.writeln('<table>'); | |
180 sink.writeln('<tr><th>count</th><th>kind</th></tr>'); | |
181 for (String kind in entryKinds) { | |
182 sink.write('<tr><td class="int">'); | |
183 sink.write(entryCounts[kind]); | |
184 sink.write('</td><td>'); | |
185 sink.write(kind); | |
186 sink.writeln('</td></tr>'); | |
187 } | |
188 sink.write('<tr><td class="int">'); | |
189 sink.write(entries.length); | |
190 sink.writeln('</td><td>Total</td></tr>'); | |
191 sink.writeln('</table>'); | |
192 } | |
193 | |
194 void _writeRightColumn(StringSink sink) { | |
195 List<String> methodNames = latencyData.keys.toList()..sort(); | |
196 completionResponseTimes.sort(); | |
197 | |
198 sink.writeln('<h3>Latency</h3>'); | |
199 sink.write('<p>'); | |
200 sink.write('<span class="label">Latency by method</span>'); | |
201 sink.writeln('</p>'); | |
202 sink.writeln('<table>'); | |
203 sink.writeln( | |
204 '<tr><th>min</th><th>mean</th><th>max</th><th>method</th></tr>'); | |
205 for (String method in methodNames) { | |
206 List<int> latencies = latencyData[method]..sort(); | |
207 // TODO(brianwilkerson) Add a spark-line distribution graph. | |
208 sink.write('<tr><td class="int">'); | |
209 sink.write(latencies[0]); | |
210 sink.write('</td><td class="int">'); | |
211 sink.write(_mean(latencies)); | |
212 sink.write('</td><td class="int">'); | |
213 sink.write(latencies[latencies.length - 1]); | |
214 sink.write('</td><td>'); | |
215 sink.write(method); | |
216 sink.writeln('</td></tr>'); | |
217 } | |
218 sink.writeln('</table>'); | |
219 | |
220 sink.writeln('<h3>Completion</h3>'); | |
221 sink.write('<p>'); | |
222 sink.write('<span class="label">Time to first notification:</span> '); | |
223 sink.write(completionResponseTimes[0]); | |
224 sink.write(', '); | |
225 sink.write(_mean(completionResponseTimes)); | |
226 sink.write(', '); | |
227 sink.write(completionResponseTimes[completionResponseTimes.length - 1]); | |
228 sink.writeln('</p>'); | |
229 } | |
230 } | |
OLD | NEW |