| // Copyright 2015 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview Class to handle file actions and trigger log processing. After |
| * logs are processed, summary information is displayed in the document. |
| */ |
| 'use strict'; |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| var docNode = document.getElementById('file_name'); |
| if (docNode) { |
| docNode.addEventListener('change', parseFile); |
| } else { |
| console.log('Cannot add event listener, must be testing.'); |
| } |
| }); |
| |
| /** |
| * Method to get the file name and trigger the file read. |
| * |
| * @private |
| */ |
| function parseFile() { |
| var logFile = document.getElementById('file_name').files; |
| if (logFile[0]) { |
| console.log('got the file!: ' + logFile[0].name); |
| readFile(logFile[0]); |
| } else { |
| console.log('file selection cancelled.'); |
| } |
| } |
| |
| /** |
| * Method to open the selected file and break into a String[] for processing. |
| * |
| * @param {String} logFile Name of the file to open. |
| * @private |
| */ |
| function readFile(logFile) { |
| var logSummary = new LogSummary(); |
| // clean up old log output |
| cleanupForNewFile(); |
| var fileReader = new FileReader(); |
| fileReader.onloadend = function(processEvent) { |
| if (processEvent.target.readyState == FileReader.DONE) { |
| var logLines = this.result.split('\n'); |
| var logHolder = logHelper.detectFileType(logLines); |
| console.log('detected file type: ' + logHolder.fileType); |
| var logText; |
| if (logHolder.fileType == 'unknown') { |
| displayError(); |
| logLines = null; |
| return; |
| } |
| |
| if (logHolder.androidlog) { |
| logHolder.androidlog = androidlogSummary.processLogLines( |
| logHolder.androidlog, logSummary); |
| } |
| |
| if (logHolder.brillolog) { |
| logHolder.brillolog = netlogSummary.processLogLines(logHolder.brillolog, |
| logSummary); |
| } |
| |
| if (logHolder.netlog) { |
| console.log('processing netlog'); |
| logHolder.netlog = netlogSummary.processLogLines(logHolder.netlog, |
| logSummary); |
| } |
| if (logHolder.syslog) { |
| console.log('processing syslog'); |
| logHolder.syslog = syslogSummary.processLogLines(logHolder.syslog, |
| logSummary); |
| } |
| |
| if (logHolder.netlog || logHolder.brillolog) { |
| displayLogSummary(logSummary); |
| displayServiceSummary(logSummary); |
| } |
| if (logHolder.syslog || logHolder.netlog || |
| logHolder.brillolog || logHolder.androidlog) { |
| displayLog(logHolder); |
| } else { |
| displayError(); |
| } |
| |
| logLines = null; |
| logText = null; |
| } |
| }; |
| fileReader.readAsText(logFile); |
| } |
| |
| /** |
| * Method to clean up state between selected files. |
| * |
| * @private |
| */ |
| function cleanupForNewFile() { |
| var oldManager = document.getElementsByClassName('managers'); |
| while (oldManager[0].hasChildNodes()) { |
| oldManager[0].removeChild(oldManager[0].lastChild); |
| } |
| } |
| |
| /** |
| * Method to display log-level summary information after processing the log. |
| * |
| * @param {LogSummary} logSummary Object to hold state read from log. |
| * @private |
| */ |
| function displayLogSummary(logSummary) { |
| console.log('inside log summary!'); |
| var manager = document.getElementsByClassName('managers'); |
| var logHeader = document.createElement('h2'); |
| logHeader.innerText = 'Log Summary'; |
| manager[0].appendChild(logHeader); |
| var timeInfo = document.createElement('p'); |
| timeInfo.innerText = 'Total Log Time: ' + |
| logHelper.formatElapsedMS( |
| logSummary.logEndTime - logSummary.logStartTime); |
| manager[0].appendChild(timeInfo); |
| var activeServiceLabel = 'Active Service '; |
| var managers = logSummary.managers; |
| for (var i = 0; i < managers.length; i++) { |
| var mInfo = document.createElement('p'); |
| mInfo.innerText = 'Manager ' + managers[i].id + ' ' + |
| logHelper.formatElapsedMS(managers[i].endTime - managers[i].startTime); |
| |
| if (managers[i].connFromSuspend.length > 0) { |
| var suspendConnInfo = document.createElement('p'); |
| var suspendConnTimes = managers[i].connFromSuspend; |
| suspendConnInfo.innerText = 'suspendDone to Online:'; |
| for (var w = 0; w < suspendConnTimes.length; w++) { |
| if (suspendConnTimes[w].suspendDone && suspendConnTimes[w].conn) { |
| var t = logHelper.formatElapsedMS( |
| suspendConnTimes[w].conn - suspendConnTimes[w].suspendDone); |
| suspendConnInfo.innerText += ' [' + t + '] '; |
| } |
| else if (suspendConnTimes[w].suspendDone) { |
| suspendConnInfo.innerText += ' [NA_] '; |
| } else { |
| suspendConnInfo.innerText += ' [_NA] '; |
| } |
| } |
| mInfo.appendChild(suspendConnInfo); |
| } |
| |
| var activeService = ''; |
| var failures; |
| for (var j = 0; j < managers[i].services.length; j++) { |
| failures = ''; |
| if (managers[i].services[j].isActive) { |
| for (var z = 0; z < managers[i].services[j].states.length; z++) { |
| if (z > 0) { |
| activeService += '--> '; |
| } |
| activeService += managers[i].services[j].states[z].state + ' '; |
| if (managers[i].services[j].states[z].state == 'Failure') { |
| var temp = managers[i].services[j].states[z]; |
| failures += ' [' + temp.failure + ']'; |
| } |
| } |
| var stateInfo = document.createElement('p'); |
| stateInfo.innerText = activeServiceLabel + |
| managers[i].services[j].serviceId + ': ' + activeService; |
| |
| var connCountInfo = document.createElement('p'); |
| connCountInfo.innerText = 'Connections: ' + |
| managers[i].services[j].connections; |
| stateInfo.appendChild(connCountInfo); |
| activeService = ''; |
| |
| if (managers[i].services[j].connections > 0) { |
| var connTimeInfo = document.createElement('p'); |
| var connTimes = managers[i].services[j].connectionTimes; |
| connTimeInfo.innerText = 'Time to Connect [assoc to ' + |
| 'online or failure (O|F)]:'; |
| for (var w = 0; w < connTimes.length; w++) { |
| if (connTimes[w].start && connTimes[w].end) { |
| var t = logHelper.formatElapsedMS( |
| connTimes[w].end - connTimes[w].start); |
| connTimeInfo.innerText += |
| ' [' + t + ' (' + connTimes[w].type + ')] '; |
| } |
| else if (connTimes[w].start) { |
| connTimeInfo.innerText += |
| ' [NA_ (' + connTimes[w].type + ')] '; |
| } else { |
| connTimeInfo.innerText += ' [_NA] '; |
| } |
| } |
| stateInfo.appendChild(connTimeInfo); |
| } |
| |
| if (failures != '') { |
| var failureInfo = document.createElement('p'); |
| failureInfo.innerText = 'Failures: ' + failures; |
| stateInfo.appendChild(failureInfo); |
| } |
| |
| console.log('graphData check: ', managers[i].services[j].graphData); |
| var xTime; |
| var graphData = managers[i].services[j].graphData; |
| for (var z = 0; z < graphData.length; z++) { |
| if (graphData[z].x != 0) { |
| xTime = Date.parse(graphData[z].x) - managers[i].startTime; |
| } else { |
| xTime = 0; |
| } |
| graphData[z].x = xTime; |
| if (z + 1 == graphData.length) { |
| graphData.push({'x': managers[i].endTime - managers[i].startTime, |
| 'y': graphData[z].y}); |
| z = z + 2; |
| } |
| } |
| |
| mInfo.appendChild(stateInfo); |
| } |
| } |
| console.log('scanDetail check: ', managers[i].scanDetails); |
| |
| console.log('suspendDetail check: ', managers[i].suspendDetails); |
| var suspendCount = managers[i].suspendDetails.length; |
| if (suspendCount > 0) { |
| // double check first and last suspend entries |
| var suspendEntry = managers[i].suspendDetails[0]; |
| if (suspendEntry.start == null) { |
| suspendEntry.start = 0; |
| } |
| suspendEntry = managers[i].suspendDetails[suspendCount - 1]; |
| if (suspendEntry.end == null) { |
| suspendEntry.end = managers[i].endTime - managers[i].startTime; |
| } |
| } |
| |
| for (var x = 0; x < managers[i].suspendDetails.length; x++) { |
| var checkDarkSuspends = managers[i].suspendDetails[x].darkSuspends; |
| if (checkDarkSuspends) { |
| console.log('darkSuspendCheck: ', checkDarkSuspends); |
| for (var y = 0; y < checkDarkSuspends.length; y++) { |
| if (checkDarkSuspends[y].start == null) { |
| checkDarkSuspends[y].start = managers[i].suspendDetails[x].start; |
| } |
| if (checkDarkSuspends[y].end == null) { |
| checkDarkSuspends[y].end = managers[i].suspendDetails[x].end; |
| } |
| } |
| console.log('fixed darkSuspendCheck: ', checkDarkSuspends); |
| } |
| } |
| var suppStates = managers[i].supplicantStates; |
| if (suppStates.length > 0) { |
| var lastState = suppStates[suppStates.length - 1]; |
| suppStates.push({'x': managers[i].endTime - managers[i].startTime, |
| 'y': lastState.y}); |
| } |
| console.log('supplicantData check: ', managers[i].supplicantStates); |
| |
| console.log('manager notes check: ', managers[i].notes); |
| |
| stateGraph.createGraph(manager[0], managers[i]); |
| |
| manager[0].appendChild(mInfo); |
| } |
| } |
| |
| /** |
| * Method to display an error message for a file that could not be processed. |
| * |
| * @private |
| */ |
| function displayError() { |
| var manager = document.getElementsByClassName('managers'); |
| var errorHeader = document.createElement('h2'); |
| errorHeader.innerText = 'File format not recognized.'; |
| manager[0].appendChild(errorHeader); |
| } |
| |
| /** |
| * Method to display individual service summaries with active states after |
| * processing a netlog. |
| * |
| * @param {LogSummary} logSummary Object holding state from processing the log. |
| * @private |
| */ |
| function displayServiceSummary(logSummary) { |
| var managers = logSummary.managers; |
| var manager = document.getElementsByClassName('managers'); |
| var managerHeader = document.createElement('h2'); |
| managerHeader.innerText = 'Managers'; |
| var managerOL = document.createElement('ol'); |
| manager[0].appendChild(managerHeader); |
| var li; |
| for (var i = 0; i < managers.length; i++) { |
| li = document.createElement('li'); |
| li.id = 'manager_' + managers[i].id; |
| li.innerText = 'Manager ' + managers[i].id + |
| ' logged time: ' + |
| logHelper.formatElapsedMS( |
| managers[i].endTime - managers[i].startTime) + |
| 'ms' + |
| ' total scans: ' + managers[i].scans + |
| ' seen services: ' + managers[i].services.length; |
| li.className = 'manager'; |
| if (managers[i].notes && managers[i].notes.length > 0) { |
| li.appendChild(displayNotes(logSummary, managers[i].notes)); |
| } |
| li.appendChild(displayServices(logSummary, managers[i].services)); |
| managerOL.appendChild(li); |
| } |
| manager[0].appendChild(managerOL); |
| } |
| |
| /** |
| * Method to display the services observed in the netlog. |
| * |
| * @param {LogSummary} logSummary Object holding state from processing the log. |
| * @param {Service[]} serviceList List of services to display. |
| * @return {node} Document node element containing the service list. |
| * @private |
| */ |
| function displayServices(logSummary, serviceList) { |
| var serviceOL = document.createElement('ol'); |
| for (var j = 0; j < serviceList.length; j++) { |
| var sId = serviceList[j].serviceId; |
| var serviceLI = document.createElement('li'); |
| serviceLI.innerText = 'Service ' + sId; |
| serviceLI.id = 'service_' + sId; |
| serviceLI.className = 'service'; |
| if (serviceList[j].notes && serviceList[j].notes.length > 0) { |
| serviceLI.appendChild(displayNotes(logSummary, serviceList[j].notes)); |
| } |
| serviceLI.appendChild(displayServiceStates(logSummary, |
| serviceList[j].states)); |
| serviceOL.appendChild(serviceLI); |
| } |
| return serviceOL; |
| } |
| |
| /** |
| * Method to display individual service states and failure reasons (if any). |
| * |
| * @param {LogSummary} logSummary Object holding state from processing the log. |
| * @param {Update[]} stateList Observed updates for a service. |
| * @return {node} Document node element containing the list of service states. |
| * @private |
| */ |
| function displayServiceStates(logSummary, stateList) { |
| var stateOL = document.createElement('ol'); |
| for (var z = 0; z < stateList.length; z++) { |
| var stateLI = document.createElement('li'); |
| var anchor = document.createElement('a'); |
| var text = document.createElement('span'); |
| anchor.href = '#' + stateList[z].time; |
| anchor.innerText = '[' + stateList[z].time + ']'; |
| text.innerText = ' [' + |
| logHelper.formatElapsedMS( |
| Date.parse(stateList[z].time) - logSummary.logStartTime) + '] ' + |
| stateList[z].state; |
| if (stateList[z].failure) { |
| text.innerText += ' [' + stateList[z].failure + ']'; |
| } |
| stateLI.appendChild(anchor); |
| stateLI.appendChild(text); |
| if (stateList[z].notes && stateList[z].notes.length > 0) { |
| stateLI.appendChild(displayNotes(logSummary, stateList[z].notes)); |
| } |
| stateOL.appendChild(stateLI); |
| } |
| return stateOL; |
| } |
| |
| /** |
| * Method to display individual notes for a service or manager. |
| * |
| * @param {LogSummary} logSummary Object holding state from processing the log. |
| * @param {Update[]} noteList Observed notes. |
| * @return {node} Document node containing the notes for a service or manager. |
| * @private |
| */ |
| function displayNotes(logSummary, noteList) { |
| if (noteList == null) { |
| return; |
| } |
| var noteOL = document.createElement('ol'); |
| noteOL.className = 'notes'; |
| for (var n = 0; n < noteList.length; n++) { |
| var noteLI = document.createElement('li'); |
| var anchor = document.createElement('a'); |
| var text = document.createElement('span'); |
| anchor.href = '#' + noteList[n].time; |
| anchor.innerText = '[' + noteList[n].time + ']'; |
| text.innerText = ' [' + |
| logHelper.formatElapsedMS( |
| Date.parse(noteList[n].time) - logSummary.logStartTime) + '] ' + |
| noteList[n].message; |
| noteLI.appendChild(anchor); |
| noteLI.appendChild(text); |
| noteOL.appendChild(noteLI); |
| } |
| return noteOL; |
| } |
| |
| /** |
| * Method to display the log lines after the summary by appending a preformatted |
| * (<pre>) element to the document. |
| * |
| * @param {Object[]} logHolder Object holding log subsections. |
| * @private |
| */ |
| function displayLog(logHolder) { |
| var mergedLog = []; |
| // merge all log types together... |
| var netLength = 0; |
| if (logHolder.netlog) |
| netLength = logHolder.netlog.length; |
| if (logHolder.brillolog) { |
| if (logHolder.netlog) { |
| console.error('processor holds netlog and brillo log - error!'); |
| return; |
| } |
| netLength = logHolder.brillolog.length; |
| logHolder.netlog = logHolder.brillolog; |
| } |
| var sysLength = 0; |
| if (logHolder.syslog) |
| sysLength = logHolder.syslog.length; |
| |
| if (logHolder.androidlog) { |
| console.log('attempting to display an android log'); |
| netLength = logHolder.androidlog.length; |
| logHolder.netlog = logHolder.androidlog; |
| } |
| |
| while (netLength > 0 || sysLength > 0) { |
| if (netLength == 0) { |
| mergedLog.appendLog(logHolder.syslog); |
| sysLength = 0; |
| } else if (sysLength == 0) { |
| mergedLog.appendLog(logHolder.netlog); |
| netLength = 0; |
| } else { |
| if (logHolder.netlog[0].ts < logHolder.syslog[0].ts) { |
| mergedLog.push(logHolder.netlog.shift()); |
| netLength -= 1; |
| } else if (logHolder.netlog[0].ts > logHolder.syslog[0].ts) { |
| mergedLog.push(logHolder.syslog.shift()); |
| sysLength -= 1; |
| } else { |
| //the log times are equal - grab them both for proper interleaving |
| mergedLog.push(logHolder.syslog.shift()); |
| sysLength -= 1; |
| mergedLog.push(logHolder.netlog.shift()); |
| netLength -= 1; |
| } |
| } |
| } |
| |
| if (mergedLog.length == 0) { |
| return; |
| } |
| var logTextHeader = document.createElement('h2'); |
| logTextHeader.innerText = 'Merged Log View'; |
| var logTextHolder; |
| var fileLabel; |
| var logTextHolder2; |
| var anchor; |
| var fileMark; |
| var mark; |
| for (var i = 0; i < mergedLog.length; i++) { |
| mark = null; |
| logTextHolder = document.createElement('pre'); |
| fileLabel = document.createElement('span'); |
| fileLabel.class = 'inner-pre file'; |
| fileLabel.textContent = mergedLog[i].file; |
| fileMark = document.createElement('mark'); |
| fileMark.className = mergedLog[i].file; |
| fileMark.appendChild(fileLabel); |
| logTextHolder.appendChild(fileMark); |
| logTextHolder2 = document.createElement('span'); |
| logTextHolder2.class = 'inner-pre'; |
| logTextHolder2.textContent += mergedLog[i].text; |
| if (mergedLog[i].tag) { |
| mark = document.createElement('mark'); |
| mark.className = mergedLog[i].type; |
| anchor = document.createElement('a'); |
| anchor.name = mergedLog[i].tag; |
| logTextHolder.appendChild(anchor); |
| } |
| if (mark) { |
| mark.appendChild(logTextHolder2); |
| logTextHolder.appendChild(mark); |
| } else { |
| logTextHolder.appendChild(logTextHolder2); |
| } |
| logTextHeader.appendChild(logTextHolder); |
| } |
| var manager = document.getElementsByClassName('managers'); |
| manager[0].appendChild(logTextHeader); |
| } |
| |
| /** |
| * Helper method to move and append one log to another. |
| * |
| * @param {Object[]} toCopy LogLine entries to move. |
| */ |
| Array.prototype.appendLog = function(toCopy) { |
| toCopy.forEach(function(x) { |
| this.push(x); |
| }, |
| this); |
| toCopy.length = 0; |
| }; |