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'; | 5 import 'dart:collection'; |
6 import 'dart:math' as math; | 6 import 'dart:math' as math; |
7 | 7 |
8 import '../log/log.dart'; | 8 import '../log/log.dart'; |
9 import '../server.dart'; | 9 import '../server.dart'; |
10 import 'page_writer.dart'; | 10 import 'page_writer.dart'; |
11 | 11 |
12 /** | 12 /** |
13 * A page writer that will produce the page containing access to the full | 13 * A page writer that will produce the page containing access to the full |
14 * content of the log. | 14 * content of the log. |
15 */ | 15 */ |
16 class LogPage extends PageWriter { | 16 class LogPage extends PageWriter { |
17 /** | 17 /** |
18 * The instrumentation log to be written. | 18 * The instrumentation log to be written. |
19 */ | 19 */ |
20 InstrumentationLog log; | 20 InstrumentationLog log; |
21 | 21 |
22 /** | 22 /** |
| 23 * The id of the entry groups to be displayed. |
| 24 */ |
| 25 EntryGroup selectedGroup; |
| 26 |
| 27 /** |
| 28 * The entries in the selected group. |
| 29 */ |
| 30 List<LogEntry> entries; |
| 31 |
| 32 /** |
23 * The index of the first entry to be written. | 33 * The index of the first entry to be written. |
24 */ | 34 */ |
25 int pageStart = 0; | 35 int pageStart = 0; |
26 | 36 |
27 /** | 37 /** |
28 * The number of entries to be written, or `null` if all of the entries should | 38 * The number of entries to be written, or `null` if all of the entries should |
29 * be written. | 39 * be written. |
30 */ | 40 */ |
31 int pageLength = null; | 41 int pageLength = null; |
32 | 42 |
33 /** | 43 /** |
34 * The number of digits in the event stamps that are the same for every entry. | 44 * The number of digits in the event stamps that are the same for every entry. |
35 */ | 45 */ |
36 int prefixLength; | 46 int prefixLength; |
37 | 47 |
38 /** | 48 /** |
39 * The number of each kind of log entry. Currently used only for debugging and | |
40 * should be removed. | |
41 */ | |
42 Map<String, int> counts = new HashMap<String, int>(); | |
43 | |
44 /** | |
45 * Initialize a newly created writer to write the content of the given | 49 * Initialize a newly created writer to write the content of the given |
46 * [instrumentationLog]. | 50 * [instrumentationLog]. |
47 */ | 51 */ |
48 LogPage(this.log) { | 52 LogPage(this.log); |
49 List<LogEntry> entries = log.logEntries; | |
50 prefixLength = computePrefixLength(entries); | |
51 for (LogEntry entry in entries) { | |
52 int count = counts.putIfAbsent(entry.kind, () => 0); | |
53 counts[entry.kind] = count + 1; | |
54 } | |
55 } | |
56 | 53 |
57 @override | 54 @override |
58 void writeBody(StringSink sink) { | 55 void writeBody(StringSink sink) { |
| 56 entries = log.entriesInGroup(selectedGroup); |
| 57 prefixLength = computePrefixLength(entries); |
| 58 |
59 writeMenu(sink); | 59 writeMenu(sink); |
60 writeTwoColumns( | 60 writeTwoColumns( |
61 sink, 'leftColumn', _writeLeftColumn, 'rightColumn', _writeRightColumn); | 61 sink, 'leftColumn', _writeLeftColumn, 'rightColumn', _writeRightColumn); |
62 } | 62 } |
63 | 63 |
64 @override | 64 @override |
65 void writeScripts(StringSink sink) { | 65 void writeScripts(StringSink sink) { |
66 super.writeScripts(sink); | 66 super.writeScripts(sink); |
67 sink.writeln(r''' | 67 sink.writeln(r''' |
68 var highlightedRows = []; | 68 var highlightedRows = []; |
(...skipping 13 matching lines...) Expand all Loading... |
82 if (element != null) { | 82 if (element != null) { |
83 element.style.fontWeight = weight; | 83 element.style.fontWeight = weight; |
84 } | 84 } |
85 } | 85 } |
86 function setDetails(detailsContent) { | 86 function setDetails(detailsContent) { |
87 var element = document.getElementById("details"); | 87 var element = document.getElementById("details"); |
88 if (element != null) { | 88 if (element != null) { |
89 element.innerHTML = detailsContent; | 89 element.innerHTML = detailsContent; |
90 } | 90 } |
91 } | 91 } |
| 92 function selectEntryGroup(pageStart) { |
| 93 var element = document.getElementById("entryGroup"); |
| 94 var url = "/log?group=" + element.value; |
| 95 window.location.assign(url); |
| 96 } |
92 '''); | 97 '''); |
93 } | 98 } |
94 | 99 |
95 /** | 100 /** |
96 * Write the content of the style sheet (without the 'script' tag) for the | 101 * Write the content of the style sheet (without the 'script' tag) for the |
97 * page to the given [sink]. | 102 * page to the given [sink]. |
98 */ | 103 */ |
99 void writeStyleSheet(StringSink sink) { | 104 void writeStyleSheet(StringSink sink) { |
100 super.writeStyleSheet(sink); | 105 super.writeStyleSheet(sink); |
101 writeTwoColumnStyles(sink, 'leftColumn', 'rightColumn'); | 106 writeTwoColumnStyles(sink, 'leftColumn', 'rightColumn'); |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 '$description <span class="gray">(pub - $duration ms)</span>'; | 173 '$description <span class="gray">(pub - $duration ms)</span>'; |
169 } | 174 } |
170 } | 175 } |
171 } | 176 } |
172 } else if (entry is TaskEntry) { | 177 } else if (entry is TaskEntry) { |
173 description = entry.description; | 178 description = entry.description; |
174 } else if (entry is ErrorEntry) { | 179 } else if (entry is ErrorEntry) { |
175 description = '<span class="error">$description</span>'; | 180 description = '<span class="error">$description</span>'; |
176 } else if (entry is ExceptionEntry) { | 181 } else if (entry is ExceptionEntry) { |
177 description = '<span class="error">$description</span>'; | 182 description = '<span class="error">$description</span>'; |
| 183 } else if (entry is MalformedLogEntry) { |
| 184 description = '<span class="error">$description</span>'; |
178 } | 185 } |
179 id = id == null ? '' : 'id="$id" '; | 186 id = id == null ? '' : 'id="$id" '; |
180 clickHandler = '$clickHandler; setDetails(\'${escape(entry.details())}\')'; | 187 clickHandler = '$clickHandler; setDetails(\'${escape(entry.details())}\')'; |
181 String timeStamp = entry.timeStamp.toString(); | 188 String timeStamp = entry.timeStamp.toString(); |
182 if (prefixLength > 0) { | 189 if (prefixLength > 0) { |
183 timeStamp = timeStamp.substring(prefixLength); | 190 timeStamp = timeStamp.substring(prefixLength); |
184 } | 191 } |
185 | 192 |
186 sink.writeln('<tr ${id}onclick="$clickHandler">'); | 193 sink.writeln('<tr ${id}onclick="$clickHandler">'); |
187 sink.writeln('<td>$icon</td>'); | 194 sink.writeln('<td>$icon</td>'); |
188 sink.writeln('<td>'); | 195 sink.writeln('<td>'); |
189 sink.writeln(timeStamp); | 196 sink.writeln(timeStamp); |
190 sink.writeln('</td>'); | 197 sink.writeln('</td>'); |
191 sink.writeln('<td style="white-space:nowrap;">'); | 198 sink.writeln('<td style="white-space:nowrap;">'); |
192 sink.writeln(description); | 199 sink.writeln(description); |
193 sink.writeln('</td>'); | 200 sink.writeln('</td>'); |
194 sink.writeln('</tr>'); | 201 sink.writeln('</tr>'); |
195 } | 202 } |
196 | 203 |
197 /** | 204 /** |
198 * Write the entries in the instrumentation log to the given [sink]. | 205 * Write the entries in the instrumentation log to the given [sink]. |
199 */ | 206 */ |
200 void _writeLeftColumn(StringSink sink) { | 207 void _writeLeftColumn(StringSink sink) { |
201 List<LogEntry> entries = log.nonTaskEntries; | |
202 int length = entries.length; | 208 int length = entries.length; |
203 int pageEnd = | 209 int pageEnd = |
204 pageLength == null ? length : math.min(pageStart + pageLength, length); | 210 pageLength == null ? length : math.min(pageStart + pageLength, length); |
205 // | 211 // |
206 // Write the header of the column. | 212 // Write the header of the column. |
207 // | 213 // |
208 sink.writeln('<div class="columnHeader">'); | 214 sink.writeln('<div class="columnHeader">'); |
209 sink.writeln('<div style="float: left">'); | 215 sink.writeln('<div style="float: left">'); |
| 216 sink.writeln('<select id="entryGroup" onchange="selectEntryGroup()">'); |
| 217 for (EntryGroup group in EntryGroup.groups) { |
| 218 sink.write('<option value="'); |
| 219 sink.write(group.id); |
| 220 sink.write('"'); |
| 221 if (group == selectedGroup) { |
| 222 sink.write(' selected'); |
| 223 } |
| 224 sink.write('>'); |
| 225 sink.write(group.name); |
| 226 sink.writeln('</option>'); |
| 227 } |
| 228 sink.writeln('</select>'); |
210 sink.writeln('Events $pageStart - ${pageEnd - 1} of ${length - 1}'); | 229 sink.writeln('Events $pageStart - ${pageEnd - 1} of ${length - 1}'); |
211 sink.writeln('</div>'); | 230 sink.writeln('</div>'); |
212 | 231 |
213 sink.writeln('<div style="float: right">'); | 232 sink.writeln('<div style="float: right">'); |
214 if (pageStart == 0) { | 233 if (pageStart == 0) { |
215 sink.writeln('<button type="button" disabled><b><</b></button>'); | 234 sink.writeln('<button type="button" disabled><b><</b></button>'); |
216 } else { | 235 } else { |
217 sink.write('<button type="button">'); | 236 sink.write('<button type="button">'); |
218 sink.write( | 237 sink.write( |
219 '<a href="${WebServer.logPath}?start=${pageStart - pageLength}">'); | 238 '<a href="${WebServer.logPath}?group=${selectedGroup.id}&start=${pageS
tart - pageLength}">'); |
220 sink.write('<b><</b>'); | 239 sink.write('<b><</b>'); |
221 sink.writeln('</a></button>'); | 240 sink.writeln('</a></button>'); |
222 } | 241 } |
223 // TODO(brianwilkerson) Add a text field for selecting the start index. | 242 // TODO(brianwilkerson) Add a text field for selecting the start index. |
224 if (pageEnd == length) { | 243 if (pageEnd == length) { |
225 sink.writeln('<button type="button" disabled><b>></b></button>'); | 244 sink.writeln('<button type="button" disabled><b>></b></button>'); |
226 } else { | 245 } else { |
227 sink.write('<button type="button">'); | 246 sink.write('<button type="button">'); |
228 sink.write( | 247 sink.write( |
229 '<a href="${WebServer.logPath}?start=${pageStart + pageLength}">'); | 248 '<a href="${WebServer.logPath}?group=${selectedGroup.id}&start=${pageS
tart + pageLength}">'); |
230 sink.write('<b>></b>'); | 249 sink.write('<b>></b>'); |
231 sink.writeln('</a></button>'); | 250 sink.writeln('</a></button>'); |
232 } | 251 } |
233 sink.writeln('</div>'); | 252 sink.writeln('</div>'); |
234 sink.writeln('</div>'); | 253 sink.writeln('</div>'); |
235 // | 254 // |
236 // Write the main body of the column. | 255 // Write the main body of the column. |
237 // | 256 // |
238 sink.writeln('<table class="fullWidth">'); | 257 sink.writeln('<table class="fullWidth">'); |
239 sink.writeln('<tr>'); | 258 sink.writeln('<tr>'); |
(...skipping 15 matching lines...) Expand all Loading... |
255 void _writeRightColumn(StringSink sink) { | 274 void _writeRightColumn(StringSink sink) { |
256 // | 275 // |
257 // Write the header of the column. | 276 // Write the header of the column. |
258 // | 277 // |
259 sink.writeln('<div class="columnHeader">'); | 278 sink.writeln('<div class="columnHeader">'); |
260 sink.writeln('<p><b>Entry Details</b></p>'); | 279 sink.writeln('<p><b>Entry Details</b></p>'); |
261 sink.writeln('</div>'); | 280 sink.writeln('</div>'); |
262 sink.writeln('<div id="details"></div>'); | 281 sink.writeln('<div id="details"></div>'); |
263 } | 282 } |
264 } | 283 } |
OLD | NEW |