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