diff --git a/.travis.yml b/.travis.yml index 4f0fc299f..821916301 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,8 @@ matrix: # since caches are tied to the language/version combination. directories: - node_modules + addons: + firefox: latest install: - npm install before_script: diff --git a/ui/css/logviewer.css b/ui/css/logviewer.css index db2c4f9ea..50473d38c 100644 --- a/ui/css/logviewer.css +++ b/ui/css/logviewer.css @@ -73,6 +73,11 @@ body { width: 3em; } +.lv-line-no.label { + border-radius: 0px; + margin: 0em 0.6em 0em 0em; +} + .lv-line-text { white-space: pre; } @@ -105,25 +110,15 @@ body { white-space: normal; } -.lv-log-container { +.logview-container { flex: 1; - overflow-x: auto; - font-family: monospace; - font-size: small; - background: #f8f8f8; + overflow: hidden; } -.lv-log-container > div:nth-child(even) { - background: #fff; -} - -/* Equal weight selector needs to follow the above nth-child() */ -.lv-log-container > .lv-log-line > .text-danger { - background: #fbe3e3; -} - -.lv-log-container > div.lv-log-line > div.lv-line-text.lv-selected-lines { - background: #F8EEC7; +.logview-container > iframe { + width: 100%; + height: 100%; + overflow: auto; } .lv-line-highlight { @@ -197,30 +192,6 @@ div.lv-step { color: #8a6d3b; } -.lv-line-no.label { - border-radius: 0px; - margin: 0em 0.6em 0em 0em; -} - -.lv-log-msg { - position: relative; - padding: 26px 0 0 20px; - font-weight: bold; - font-family: sans-serif; - font-size: 11px; -} - -.lv-log-overlay { - padding: 6px; - border: 2px solid #4cae4c; - border-radius: 3px; - background: #fff; -} - -.lv-log-error { - border: 2px solid #ff0000; -} - .job-header { max-height: 240px; overflow-y: auto; diff --git a/ui/js/components/logviewer/logviewer.js b/ui/js/components/logviewer/logviewer.js new file mode 100644 index 000000000..3480b71cf --- /dev/null +++ b/ui/js/components/logviewer/logviewer.js @@ -0,0 +1,32 @@ +'use strict'; + +treeherder.component('thLogViewer', { + templateUrl: 'partials/logviewer/logviewer.html', + controller: ($sce, $location, $element, $scope) => { + const logParams = () => { + const q = $location.search(); + let params = { lineHeight: 13 }; + + if (q.lineNumber) { + const lines = q.lineNumber.split('-'); + + params.lineNumber = lines[0]; + params.highlightStart = lines[0]; + params.highlightEnd = lines.length === 2 ? lines[1] : lines[0]; + } + + return Object.keys(params) + .reduce((qs, key) => `${qs}&${key}=${params[key]}`, ''); + }; + + $element.find('iframe').bind('load', () => $scope.$parent.logviewerInit()); + + $scope.$parent.$watch('rawLogURL', () => { + const parent = $scope.$parent; + + if ($scope.$parent.rawLogURL) { + $element[0].childNodes[0].src = $sce.trustAsResourceUrl(`${parent.logBasePath}?url=${parent.rawLogURL}${logParams()}`); + } + }); + } +}); diff --git a/ui/js/controllers/logviewer.js b/ui/js/controllers/logviewer.js index e558b7097..052f5bf23 100644 --- a/ui/js/controllers/logviewer.js +++ b/ui/js/controllers/logviewer.js @@ -1,441 +1,262 @@ 'use strict'; logViewerApp.controller('LogviewerCtrl', [ - '$anchorScroll', '$http', '$location', '$q', '$rootScope', '$scope', - '$timeout', '$resource', 'ThTextLogStepModel', 'ThJobDetailModel', 'ThLog', - 'ThLogSliceModel', 'ThJobModel', 'thNotify', 'dateFilter', 'ThResultSetModel', + '$location', '$window', '$document', '$rootScope', '$scope', + '$timeout', '$resource', 'ThTextLogStepModel', 'ThJobDetailModel', + 'ThJobModel', 'thNotify', 'dateFilter', 'ThResultSetModel', 'thDateFormat', 'thReftestStatus', function Logviewer( - $anchorScroll, $http, $location, $q, $rootScope, $scope, - $timeout, $resource, ThTextLogStepModel, ThJobDetailModel, ThLog, - ThLogSliceModel, ThJobModel, thNotify, dateFilter, ThResultSetModel, + $location, $window, $document, $rootScope, $scope, + $timeout, $resource, ThTextLogStepModel, ThJobDetailModel, + ThJobModel, thNotify, dateFilter, ThResultSetModel, thDateFormat, thReftestStatus) { - // changes the size of chunks pulled from server - var LINE_BUFFER_SIZE = 100; - var LogSlice; - + const query_string = $location.search(); + $scope.css = ''; + $rootScope.logBasePath = 'https://taskcluster.github.io/unified-logviewer/'; $rootScope.urlBasePath = $location.absUrl().split('logviewer')[0]; - var query_string = $location.search(); if (query_string.repo !== "") { $rootScope.repoName = query_string.repo; } + if (query_string.job_id !== "") { $scope.job_id= query_string.job_id; - LogSlice = new ThLogSliceModel($scope.job_id, LINE_BUFFER_SIZE); } - $scope.displayedLogLines = []; - $scope.loading = false; - $scope.logError = false; - $scope.jobExists = true; - $scope.currentLineNumber = 0; - $scope.highestLine = 0; - $scope.showSuccessful = true; - $scope.willScroll = false; - $scope.$watch('steps', function () { + $scope.loading = false; + $scope.jobExists = true; + $scope.showSuccessful = true; + + $scope.$watch('steps', () => { if (!$scope.steps) { return; } + $scope.showSuccessful = !$scope.hasFailedSteps(); }); - $scope.$watch('[selectedBegin, selectedEnd]', function(newVal) { - var newHash = (newVal[0] === newVal[1]) ? newVal[0] : newVal[0] + "-L" + newVal[1]; - if (!isNaN(newVal[0])) { - $location.hash("L" + newHash); - } else if ($scope.steps) { - $location.hash(""); - } - }); + $scope.logPostMessage = (values) => { + const { lineNumber, highlightStart } = values; - $scope.$on("$locationChangeSuccess", function() { - var oldLine = parseInt($scope.currentLineNumber); - getSelectedLines(); - - var newLine = parseInt($scope.selectedBegin); - var range = LINE_BUFFER_SIZE / 2; - if ((newLine <= (oldLine - range) || newLine >= oldLine + range) && !$scope.willScroll) { - if ($scope.steps) { - moveScrollToLineNumber(newLine); - } + if (lineNumber && !highlightStart) { + values.highlightStart = lineNumber; + values.highlightEnd = lineNumber; } - $scope.willScroll = false; - }); - $scope.click = function(line, $event) { - $scope.willScroll = true; - if ($event.shiftKey) { - if (line.index < $scope.selectedBegin) { - $scope.selectedEnd = $scope.selectedBegin; - $scope.selectedBegin = line.index; - } else { - $scope.selectedEnd = line.index; - } - } else { - $scope.selectedBegin = $scope.selectedEnd = line.index; - } + updateQuery(values); + $document[0].getElementById('logview').contentWindow.postMessage(values, $rootScope.logBasePath); }; - // Erase the value of selectedBegin, used to erase the hash value when - // the user clicks on the error step button - $scope.eraseSelected = function() { - $scope.selectedBegin = 'undefined'; - }; + $scope.hasFailedSteps = () => { + const steps = $scope.steps; - $scope.setLineNumber = function(number) { - $scope.selectedBegin = number; - $scope.selectedEnd = number; - }; - - $scope.hasFailedSteps = function () { - var steps = $scope.steps; if (!steps) { return false; } - for (var i = 0; i < steps.length; i++) { + for (let i = 0; i < steps.length; i++) { // We only recently generated step results as part of ingestion, // so we have to check the results property is present. // TODO: Remove this when the old data has expired, so long as // other data submitters also provide a step result. if ('result' in steps[i] && steps[i].result !== 'success' && steps[i].result !== 'skipped') { - return true; } } + return false; }; // Get the css class for the result, step buttons and other general use - $scope.getShadingClass = function(result) { + $scope.getShadingClass = (result) => { return "result-status-shading-" + result; }; - $scope.loadMore = function(bounds, element) { - var deferred = $q.defer(), range, above, below; - - if (!$scope.loading) { - // move the line number either up or down depending which boundary was hit - $scope.currentLineNumber = moveLineNumber(bounds); - - range = { - start: $scope.currentLineNumber, - end: $scope.currentLineNumber - }; - - if (bounds.top) { - above = getChunkAbove(range); - } else if (bounds.bottom) { - below = getChunkBelow(range); - } else { - range = getChunksSurrounding($scope.currentLineNumber); - } - - // dont do the call if we already have all the lines - if (range.start === range.end) { - return deferred.promise; - } - - $scope.loading = true; - var lineRangeParams = { - job_id: $scope.job_id, - start_line: range.start, - end_line: range.end - }; - LogSlice.get_line_range(lineRangeParams, { - buffer_size: LINE_BUFFER_SIZE - }).then(function(data) { - drawErrorLines(data); - - if (bounds.top) { - for (var i = data.length - 1; i >= 0; i--) { - // make sure we are inserting at the right place - if ($scope.displayedLogLines[0].index !== data[i].index + 1) { - continue; - } - $scope.displayedLogLines.unshift(data[i]); - } - - $timeout(function () { - if (above) { - removeChunkBelow(); - } - }, 100); - } else if (bounds.bottom) { - var sh = element.scrollHeight; - var lines = $scope.displayedLogLines; - - for (var j = 0; j < data.length; j++) { - // make sure we are inserting at the right place - if (lines[lines.length - 1].index !== data[j].index - 1) { - continue; - } - $scope.displayedLogLines.push(data[j]); - } - - $timeout(function () { - if (below) { - removeChunkAbove(); - element.scrollTop -= element.scrollHeight - sh; - } - }, 100); - } else { - $scope.displayedLogLines = data; - } - - $scope.loading = false; - deferred.resolve(); - }, function () { - $scope.loading = false; - $scope.logError = true; - thNotify.send("The log no longer exists or has expired", 'warning', true); - deferred.reject(); - }); - } else { - deferred.reject(); - } - - return deferred.promise; - }; - // @@@ it may be possible to do this with the angular date filter? - $scope.formatTime = function(startedStr, finishedStr) { + $scope.formatTime = (startedStr, finishedStr) => { if (!startedStr || !finishedStr) { - return ""; + return ''; } - var sec = Math.abs(new Date(startedStr) - new Date(finishedStr)) / 1000.0; - var h = Math.floor(sec/3600); - var m = Math.floor(sec%3600/60); - var s = Math.floor(sec%3600 % 60); - var secStng = sec.toString(); - var ms = secStng.substr(secStng.indexOf(".")+1, 2); - return ((h > 0 ? h + "h " : "") + (m > 0 ? m + "m " : "") + - (s > 0 ? s + "s " : "") + (ms > 0 ? ms + "ms " : "00ms")); + const sec = Math.abs(new Date(startedStr) - new Date(finishedStr)) / 1000.0; + const h = Math.floor(sec / 3600); + const m = Math.floor(sec % 3600 / 60); + const s = Math.floor(sec % 3600 % 60); + const secStng = sec.toString(); + const ms = secStng.substr(secStng.indexOf(".") + 1, 2); + + return ((h > 0 ? h + 'h ' : '') + (m > 0 ? m + 'm ' : '') + + (s > 0 ? s + 's ' : '') + (ms > 0 ? ms + 'ms ' : '00ms')); }; - $scope.displayTime = function(started, finished) { - var start = started ? started.substr(started.indexOf(" ")+1, 8) : '?'; - var end = finished ? finished.substr(finished.indexOf(" ")+1, 8) : '?'; - return start + "-" + end; + $scope.displayTime = (started, finished) => { + const start = started ? started.substr(started.indexOf(' ') + 1, 8) : '?'; + const end = finished ? finished.substr(finished.indexOf(' ') + 1, 8) : '?'; + + return start + '-' + end; }; - $scope.init = function() { - + $scope.init = () => { $scope.logProperties = []; - // HACK: check if this was a link to an older job log with a - // project specific id, and rewrite the job_id if so ThJobModel.get_list($scope.repoName, { project_specific_id: $scope.job_id }).then(function(jobList) { if (jobList.length > 0) { $scope.job_id = jobList[0]['id']; } - - ThJobModel.get($scope.repoName, $scope.job_id).then(function(job) { + ThJobModel.get($scope.repoName, $scope.job_id).then(job => { // set the title of the browser window/tab $scope.logViewerTitle = job.get_title(); + if (job.logs && job.logs.length) { $scope.rawLogURL = job.logs[0].url; } + // set the result value and shading color class - $scope.result = {label: "Result", value: job.result}; + $scope.result = {label: 'Result', value: job.result}; $scope.resultStatusShading = $scope.getShadingClass(job.result); // other properties, in order of appearance $scope.logProperties = [ - {label: "Job", value: $scope.logViewerTitle}, - {label: "Machine", value: job.machine_name}, - {label: "Start", value: dateFilter(job.start_timestamp*1000, thDateFormat)}, - {label: "End", value: dateFilter(job.end_timestamp*1000, thDateFormat)} + {label: 'Job', value: $scope.logViewerTitle}, + {label: 'Machine', value: job.machine_name}, + {label: 'Start', value: dateFilter(job.start_timestamp * 1000, thDateFormat)}, + {label: 'End', value: dateFilter(job.end_timestamp * 1000, thDateFormat)} ]; // Test to expose the reftest button in the logviewer actionbar - $scope.isReftest = function() { + $scope.isReftest = () => { if (job.job_group_name) { return thReftestStatus(job); } }; // get the revision and linkify it - ThResultSetModel.getResultSet($scope.repoName, job.push_id).then(function(data){ - var revision = data.data.revision; - $scope.logProperties.push({label: "Revision", value: revision}); + ThResultSetModel.getResultSet($scope.repoName, job.result_set_id).then(data => { + const revision = data.data.revision; + + $scope.logProperties.push({label: 'Revision', value: revision}); }); - ThJobDetailModel.getJobDetails({job_guid: job.job_guid}).then(function(jobDetails) { + ThJobDetailModel.getJobDetails({job_guid: job.job_guid}).then(jobDetails => { $scope.job_details = jobDetails; }); - }, function () { + }, () => { $scope.loading = false; $scope.jobExists = false; - thNotify.send("The job does not exist or has expired", 'danger', true); - }); - - ThTextLogStepModel.query({ - project: $rootScope.repoName, - jobId: $scope.job_id - }, function(textLogSteps) { - $scope.steps = textLogSteps; - - // add an ordering to each step - textLogSteps.forEach((step, i) => {step.order = i;}); - - // If the log contains no errors load the head otherwise - // load the first failure step line. We also need to test - // for the 0th element for outlier jobs. - var allErrors = _.flatten(textLogSteps.map(s => s.errors)); - if (allErrors.length === 0) { - angular.element(document).ready(function () { - if (isNaN($scope.selectedBegin)) { - for (var i = 0; i < $scope.steps.length; i++) { - var step = $scope.steps[i]; - if (step.result !== "success") { - $scope.selectedBegin = step.started_line_number; - $scope.selectedEnd = step.finished_line_number; - break; - } - } - } - moveScrollToLineNumber($scope.selectedBegin); - }); - } else { - $scope.setLineNumber(allErrors[0].line_number); - moveScrollToLineNumber($scope.selectedBegin); - } + thNotify.send('The job does not exist or has expired', 'danger', true); }); }); }; + $scope.logviewerInit = () => { + // Listen for messages from child frame + setLogListener(); + + ThTextLogStepModel.query({ + project: $rootScope.repoName, + jobId: $scope.job_id + }, textLogSteps => { + let shouldPost = true; + const allErrors = _.flatten(textLogSteps.map(s => s.errors)); + const q = $location.search(); + $scope.steps = textLogSteps; + + // add an ordering to each step + textLogSteps.forEach((step, i) => {step.order = i;}); + + // load the first failure step line else load the head + if (allErrors.length) { + $scope.css = $scope.css + errorLinesCss(allErrors); + + if (!q.lineNumber) { + $scope.logPostMessage({ lineNumber: allErrors[0].line_number + 1, customStyle: $scope.css }); + shouldPost = false; + } + } else if (!q.lineNumber) { + for (let i = 0; i < $scope.steps.length; i++) { + let step = $scope.steps[i]; + + if (step.result !== "success") { + $scope.logPostMessage({ + lineNumber: step.started_line_number + 1, + highlightStart: step.started_line_number + 1, + highlightEnd: step.finished_line_number + 1, + customStyle: $scope.css + }); + + break; + } + } + } + + if (shouldPost) { + $scope.logPostMessage({ customStyle: $scope.css }); + } + }); + }; + + $scope.setDisplayedStep = (step) => { + const highlightStart = step.started_line_number + 1 || step.line_number + 1; + const highlightEnd = step.finished_line_number + 1 || step.line_number + 1; + $scope.displayedStep = step; + + $scope.logPostMessage({ lineNumber: highlightStart, highlightStart, highlightEnd }); + }; + + function errorLinesCss(errors) { + return errors + .map(({ line_number }) => `a[id="${line_number + 1}"]+span`) + .join(',') + .concat('{background:#fbe3e3;color:#a94442}'); + } + + function logCss() { + const hideToolbar = '#toolbar{display:none}'; + const body = 'html,body{background:#f8f8f8;color:#333;font-size:12px}'; + const highlight = '#log p.highlight a,#log p.highlight span{background:#f8eec7!important}'; + const hover = '#log p:hover{background:transparent}#log p a:hover,#log p.highlight a:hover{background:#f8eec7;color:#000}'; + const stripe = '.lazy-list p:nth-child(2n){background:#fff!important}.lazy-list p:nth-child(2n+1){background:#f8f8f8!important}'; + const linePadding = '#log p{padding:0 15px 0 35px}'; + const lineNumber = '#log p a,#log p.highlight a{color:rgba(0,0,0,.3)}'; + const font = '#log{font-family:monospace}'; + + return hideToolbar + body + highlight + hover + stripe + lineNumber + linePadding + font; + } + /** utility functions **/ - function moveScrollToLineNumber(linenumber) { - $scope.currentLineNumber = linenumber; - $scope.displayedStep = getStepFromLine(linenumber); - $scope.loadMore({}).then(function () { - $timeout(function () { - var raw = $('.lv-log-container')[0]; - var line = $('.lv-log-line[line="' + linenumber + '"]'); - raw.scrollTop += line.offset().top - $('.run-data').outerHeight() - - $('.navbar').outerHeight() - 120; - }); - }); + function updateQuery(values) { + const data = typeof values === 'string' ? JSON.parse(values) : values; + const { lineNumber, highlightStart, highlightEnd } = data; + + if (highlightStart !== highlightEnd) { + $location.search('lineNumber', `${highlightStart}-${highlightEnd}`); + } + else if (highlightStart) { + $location.search('lineNumber', highlightStart); + } else { + $location.search('lineNumber', lineNumber); + } } - function getStepFromLine(linenumber) { - return $scope.steps.find(function(step) { - return (step.started_linenumber <= linenumber && - step.finished_linenumber >= linenumber); - }); - } + function setLogListener() { + let workerReady = false; - function getSelectedLines () { - var urlHash = $location.hash(); - var regexSelectedlines = /L(\d+)(-L(\d+))?$/; - if (regexSelectedlines.test(urlHash)) { - var matchSelectedLines = urlHash.match(regexSelectedlines); - if (isNaN(matchSelectedLines[3])) { - matchSelectedLines[3] = matchSelectedLines[1]; + $window.addEventListener('message', (e) => { + // Send initial css when child frame loads URL successfully + if (!workerReady) { + workerReady = true; + + $scope.css = $scope.css + logCss(); + $scope.logPostMessage({ customStyle: $scope.css }); } - $scope.selectedBegin = matchSelectedLines[1]; - $scope.selectedEnd = matchSelectedLines[3]; - } - } - function logFileLineCount () { - var steps = $scope.steps; - return steps[ steps.length - 1 ].finished_line_number + 1; - } - - function moveLineNumber (bounds) { - var lines = $scope.displayedLogLines, newLine; - - if (bounds.top) { - return lines[0].index; - } else if (bounds.bottom) { - newLine = lines[lines.length - 1].index + 1; - return (newLine > logFileLineCount()) ? logFileLineCount(): newLine; - } - - return $scope.currentLineNumber; - } - - function drawErrorLines (data) { - if (data.length === 0) { - return; - } - - var min = data[0].index; - var max = data[ data.length - 1 ].index; - - $scope.steps.forEach(function(step) { - step.errors.forEach(function(err) { - var line = err.line_number; - - if (line < min || line > max) { - return; - } - - var index = line - min; - data[index].hasError = true; - }); + $timeout(updateQuery(e.data)); }); } - - function getChunksSurrounding(line) { - var request = {start: null, end: null}; - - getChunkContaining(line, request); - getChunkAbove(request); - getChunkBelow(request); - - return request; - } - - function getChunkContaining (line, request) { - var index = Math.floor(line/LINE_BUFFER_SIZE); - - request.start = index * LINE_BUFFER_SIZE; - request.end = (index + 1) * LINE_BUFFER_SIZE; - } - - function getChunkAbove (request) { - request.start -= LINE_BUFFER_SIZE; - request.start = Math.floor(request.start/LINE_BUFFER_SIZE)*LINE_BUFFER_SIZE; - - if (request.start >= 0) { - return true; - } - - request.start = 0; - return false; - } - - function getChunkBelow (request) { - var lastLine = logFileLineCount(); - - request.end += LINE_BUFFER_SIZE; - request.end = Math.ceil(request.end/LINE_BUFFER_SIZE)*LINE_BUFFER_SIZE; - - if (request.end <= lastLine) { - return true; - } - - request.end = lastLine; - return false; - } - - function removeChunkAbove () { - $scope.displayedLogLines = $scope.displayedLogLines.slice(LINE_BUFFER_SIZE); - } - - function removeChunkBelow () { - var endSlice = $scope.displayedLogLines.length - LINE_BUFFER_SIZE; - $scope.displayedLogLines = $scope.displayedLogLines.slice(0, endSlice); - } } ]); diff --git a/ui/js/directives/treeherder/log_viewer_infinite_scroll.js b/ui/js/directives/treeherder/log_viewer_infinite_scroll.js deleted file mode 100644 index bd0b6598a..000000000 --- a/ui/js/directives/treeherder/log_viewer_infinite_scroll.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -treeherder.directive('lvInfiniteScroll', ['$timeout', function ($timeout) { - return function (scope, element) { - element.bind('scroll', function () { - var raw = element[0]; - var sh = raw.scrollHeight; - - if (raw.scrollTop <= 100) { - scope.loadMore({top: true}, raw).then(function(haltScrollTop) { - if (!haltScrollTop) { - $timeout(function () { - raw.scrollTop = raw.scrollHeight - sh; - }); - } - }); - } else if (raw.scrollTop >= (raw.scrollHeight - $(element).height() - 100)) { - scope.loadMore({bottom: true}, raw); - } - }); - }; -}]); diff --git a/ui/js/directives/treeherder/log_viewer_lines.js b/ui/js/directives/treeherder/log_viewer_lines.js deleted file mode 100644 index 457ef6296..000000000 --- a/ui/js/directives/treeherder/log_viewer_lines.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -treeherder.directive('lvLogLines', ['$parse', function () { - function getOffsetOfStep (order) { - var el = $('.lv-step[order="' + order + '"]'); - var parentOffset = el.parent().offset(); - - return el.offset().top - - parentOffset.top + el.parent().scrollTop() - - parseInt($('.steps-data').first().css('padding-bottom')); - } - - function onScroll ($scope) { - var lines = $('.lv-log-line'); - var scrollTop = $('.lv-log-container').scrollTop(); - - for (var i = 0, ll = lines.length; i < ll; i++) { - if (lines[i].offsetTop > scrollTop) { - var steps = $scope.steps; - var lineNumber = +$(lines[i]).attr('line'); - - for (var j = 0, sl = steps.length; j < sl; j++) { - if (lineNumber > (steps[j].started_line_number - 1) && - lineNumber < (steps[j].finished_line_number + 1)) { - // make sure we aren't updating when its already correct - if ($scope.displayedStep && - $scope.displayedStep.order === steps[j].order) { - return; - } - - $scope.displayedStep = steps[j]; - - // scroll to the step - scrollTop = getOffsetOfStep(steps[j].order); - $('.steps-data').scrollTop(scrollTop); - - if (!$scope.$$phase) { - $scope.$apply(); - } - - return; - } - } - } - } - } - - /* -------------------------------------------------------------------- */ - - return { - restrict: 'A', - templateUrl: 'partials/logviewer/lvLogLines.html', - link: function (scope, element) { - $(element).scroll(onScroll.bind(this, scope)); - } - }; -}]); diff --git a/ui/js/directives/treeherder/log_viewer_steps.js b/ui/js/directives/treeherder/log_viewer_steps.js index a061870ad..b8cc754db 100644 --- a/ui/js/directives/treeherder/log_viewer_steps.js +++ b/ui/js/directives/treeherder/log_viewer_steps.js @@ -1,9 +1,9 @@ 'use strict'; -treeherder.directive('lvLogSteps', ['$timeout', '$q', function ($timeout, $q) { +treeherder.directive('lvLogSteps', ['$timeout', $timeout => { function getOffsetOfStep (order) { - var el = $('.lv-step[order="' + order + '"]'); - var parentOffset = el.parent().offset(); + const el = $('.lv-step[order="' + order + '"]'); + const parentOffset = el.parent().offset(); return el.offset().top - parentOffset.top + el.parent().scrollTop() - @@ -15,32 +15,12 @@ treeherder.directive('lvLogSteps', ['$timeout', '$q', function ($timeout, $q) { return { restrict: 'A', templateUrl: 'partials/logviewer/lvLogSteps.html', - link: function (scope) { - scope.scrollTo = function($event, step, linenumber) { - scope.currentLineNumber = linenumber; - - scope.loadMore({}).then(function () { - $timeout(function () { - var raw = $('.lv-log-container')[0]; - var line = $('.lv-log-line[line="' + linenumber + '"]'); - raw.scrollTop += line.offset().top - $('.run-data').outerHeight() - - $('.navbar').outerHeight() - 9; - }); - }, function () { - // there is an error so bomb out - return $q.reject(); - }); - - if (scope.displayedStep && scope.displayedStep.order === step.order) { - $event.stopPropagation(); - } - }; - - scope.toggleSuccessfulSteps = function () { + link: (scope) => { + scope.toggleSuccessfulSteps = () => { scope.showSuccessful = !scope.showSuccessful; - var firstError = scope.steps.filter(function (step) { - return step.result && step.result !== "success"; + const firstError = scope.steps.filter(step => { + return step.result && step.result !== 'success'; })[0]; if (!firstError) { @@ -48,26 +28,10 @@ treeherder.directive('lvLogSteps', ['$timeout', '$q', function ($timeout, $q) { } // scroll to the first error - $timeout(function () { - var scrollTop = getOffsetOfStep(firstError.order); - $('.steps-data').scrollTop( scrollTop ); - }); - }; + $timeout(() => { + const scrollTop = getOffsetOfStep(firstError.order); - scope.displayLog = function (step, state) { - scope.displayedStep = step; - scope.currentLineNumber = step.started_line_number; - scope.selectedBegin = step.started_line_number; - scope.selectedEnd = step.finished_line_number; - scope.loadMore({}).then(function () { - $timeout(function () { - var raw = $('.lv-log-container')[0]; - var line = $('.lv-log-line[line="' + step.started_line_number + '"]'); - if (state !== 'initialLoad') { - raw.scrollTop += line.offset().top - $('.run-data').outerHeight() - - $('.navbar').outerHeight() - 9; - } - }); + $('.steps-data').scrollTop(scrollTop); }); }; } diff --git a/ui/js/models/log_slice.js b/ui/js/models/log_slice.js deleted file mode 100644 index 874ca0c7e..000000000 --- a/ui/js/models/log_slice.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict'; - -treeherder.factory('ThLogSliceModel', [ - '$http', '$q', '$timeout', 'thUrl', - function($http, $q, $timeout, thUrl) { - - // ThLogSliceModel is the js counterpart of logslice - - var ThLogSliceModel = function(job_id, buffer_chunk_size, buffer_size) { - this.job_id = job_id; - this.chunk_size = buffer_chunk_size || 500; - this.buffer_size = buffer_size || 10; - this.buffer = {}; - }; - - ThLogSliceModel.get_uri = function(){return thUrl.getProjectUrl("/logslice/");}; - - ThLogSliceModel.prototype.find_in_buffer = function (options) { - var ret = [], arr; - - for (var i = options.start_line; i < options.end_line; i += this.chunk_size) { - arr = this.buffer[Math.floor(i/this.chunk_size)] || false; - - if (arr) { - // update for LRU - arr.used = Date.now(); - ret = ret.concat(arr.data); - } else { - return false; - } - } - - return ret; - }; - - ThLogSliceModel.prototype.insert_into_buffer = function (options, res) { - for (var i = options.start_line, j = 0; i < options.end_line; i += this.chunk_size, j++) { - this.buffer[Math.floor(i/this.chunk_size)] = { - data: res.slice(j * this.chunk_size, (j+1) * this.chunk_size), - used: Date.now() - }; - } - - var size = this.buffer_size + 1; - - while (size > this.buffer_size) { - size = 0; - var indexLRU = 0, baseDate = Date.now(); - - for (var k in this.buffer) { - if (this.buffer.hasOwnProperty(k)) { - size++; - if (this.buffer[k].used < baseDate) { - baseDate = this.buffer[k].used; - indexLRU = k; - } - } - } - - if (size > this.buffer_size) { - delete this.buffer[indexLRU]; - } - } - }; - - ThLogSliceModel.prototype.get_line_range = function(options, config) { - config = config || {}; - var timeout = config.timeout || null; - var found = this.find_in_buffer(options); - var self = this; - var deferred = $q.defer(); - - if (found) { - deferred.resolve(found); - return deferred.promise; - } - - return $http.get(ThLogSliceModel.get_uri(),{ - params: options, - timeout: timeout - }).then(function (res) { - self.insert_into_buffer(options, res.data); - - return res.data; - }, function () { - return $q.reject("Log not found"); - }); - - }; - - return ThLogSliceModel; - }]); diff --git a/ui/logviewer.html b/ui/logviewer.html index 2b16bd507..9b2d74ba9 100644 --- a/ui/logviewer.html +++ b/ui/logviewer.html @@ -113,11 +113,7 @@
- -
-
+ @@ -139,11 +135,12 @@ - - + + + @@ -153,7 +150,6 @@ - diff --git a/ui/partials/logviewer/logviewer.html b/ui/partials/logviewer/logviewer.html new file mode 100644 index 000000000..032c8a415 --- /dev/null +++ b/ui/partials/logviewer/logviewer.html @@ -0,0 +1,3 @@ +