blob: 76ed5882a89727b09dc42c6234bea5dcd44341dc [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
'use strict';
const _ = require('lodash');
const fs = require('fs');
const jsonfile = require('jsonfile');
const glob = require('glob');
const path = require('path');
const process = require('process');
let baseDir;
let success = 0;
let failure = 0;
let unrecognized;
// TODO Verify that the errors are indeed in the log file
const reasons = {};
const regexes = {
aborted: [ /Build was aborted/ ],
network: [
/fatal: unable to access 'https:\/\/git-wip-us.apache.org/,
/fatal: read error: Connection reset by peer/,
],
docker: [
/Cannot connect to the Docker daemon. Is the docker daemon running on this host?/
],
libdl: [ /sed: error while loading shared libraries: libdl.so.2/ ],
eunit_replicator: [
/\\*\\*in function couch_replicator_filtered_tests:should_succeed/,
/\\*\\*error:\{assertion_failed,\[\{module,couch_replicator_compact_tests\}/,
],
eunit_compression: [
/couchdb_file_compression_tests:110: should_compare_compression_methods.*\\*failed\\*/,
/in call from couchdb_file_compression_tests:setup\/0 \(test\/couchdb_file_compression_tests.erl, line 38\)/
],
eunit: [ /XRROR: One or more eunit tests failed./ ],
};
// from https://gist.github.com/colingourlay/82506396503c05e2bb94
_.mixin({
'sortKeysBy': function (obj, comparator) {
var keys = _.sortBy(_.keys(obj), function (key) {
return comparator ? comparator(obj[key], key) : key;
});
return _.zipObject(keys, _.map(keys, function (key) {
return obj[key];
}));
}
});
function init() {
if (!process.env.JENKINS_LOGS_DIR) {
console.log('WARNING: JENKINS_LOGS_DIR is not set.\n');
}
baseDir = process.env.JENKINS_LOGS_DIR || __dirname;
// console.log('Will read logs from', baseDir, '\n\n');
process.on('exit', function() {
console.log('# Summary');
console.log('\nSuccesses:', success, 'Failures:', failure);
if (reasons.unrecognized.counter > 0) {
console.log('\n\n# Uncategorized Failures');
console.log('\n* Number of failures: ' + reasons.unrecognized.counter);
console.log('\n## Builds');
reasons.unrecognized.urls.forEach( url => {
console.log('* <' + url + '>');
});
}
// print results, most frequent errors first
_(reasons)
.omit(['unrecognized'])
.sortKeysBy((value, key) => {
return -value.counter;
})
.forOwn((reasonObject, reasonKey) => {
console.log('\n\n# Failures with reason "' + reasonKey + '"');
console.log('\n* Number of failures: ' + reasonObject.counter);
console.log('## Regular Expressions');
console.log('When one of these regular expression has a match in the build log, I assume this build failure falls into this category.\n');
regexes[reasonKey].forEach( regex => {
console.log('* `' + regex + '`');
});
console.log('\n## Builds\n');
console.log('Links to the build logs:\n');
reasonObject.urls.forEach( url => {
console.log('* <' + url + '>');
});
});
});
}
function initReasonObject() {
return {
counter: 0,
urls: [],
directories: [],
};
}
function analyzeLogs() {
init();
glob('+([0123456789])/', { cwd: baseDir, }, function (err, buildDirectories) {
if (err) {
exitOnError(err);
}
if (buildDirectories.length === 0) {
exitOnError(new Error('No matching directories found.'));
}
buildDirectories.forEach(buildDir => {
readBuild(path.join(baseDir, buildDir));
});
});
}
function readBuild(buildDirectory) {
fs.readdir(buildDirectory, function(err, runDirectories) {
if (err) {
return printWarning(err);
}
runDirectories.forEach(runDirectory => {
readRun(path.join(buildDirectory, runDirectory));
});
});
}
function readRun(directory) {
const metaDataFile = path.join(directory, 'metadata.json');
const buildLogFile = path.join(directory, 'build.log');
jsonfile.readFile(metaDataFile, 'utf-8', (err, metaData) => {
if (err) {
return printWarning(err);
}
const result = metaData.result[0];
if (metaData.result[0] !== 'SUCCESS') {
// console.error('Failure:', directory, result);
failure++;
fs.readFile(buildLogFile, 'utf-8', (err, buildLog) => {
if (err) {
return printWarning(err);
}
for (var key in regexes) {
for (var i = 0; i < regexes[key].length; i++) {
if (contains(buildLog, regexes[key][i])) {
appendToReason(key, metaData, directory);
return;
}
}
}
// console.error('Uncategorized failure', buildLogFile);
appendToReason('unrecognized', metaData, directory);
});
} else {
// console.error('Success:', directory, result);
success++;
}
});
}
function appendToReason(key, metaData, directory) {
let reasonObject = reasons[key];
if (!reasonObject) {
reasonObject = reasons[key] = initReasonObject();
}
reasonObject.counter++;
reasonObject.urls.push(metaData.url + 'consoleText');
reasonObject.directories.push(directory);
}
function contains(buildLog, regex) {
return buildLog.search(regex) >= 0;
}
function exitOnError(error) {
console.error(error);
process.exit(1);
}
function printWarning(error) {
console.error(error);
}
analyzeLogs();