blob: 8c23c7287046656244cec3a8f6d5f51c756483a0 [file] [log] [blame]
// 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.netlog) {
logHolder.netlog = netlogSummary.processLogLines(logHolder.netlog,
logSummary);
}
if (logHolder.syslog) {
logHolder.syslog = syslogSummary.processLogLines(logHolder.syslog);
}
if (logHolder.netlog) {
displayLogSummary(logSummary);
displayServiceSummary(logSummary);
}
if (logHolder.syslog || logHolder.netlog) {
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);
}
}
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;
var sysLength = 0;
if (logHolder.syslog)
sysLength = logHolder.syslog.length;
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;
};