2021-12-03 17:58:48 +01:00

220 lines
5.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
// -*- mode: js -*-
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var readline = require('readline');
var sprintf = require('util').format;
var nopt = require('nopt');
///--- Globals
var BUCKETS = {};
var REQUEST_IDS = {};
var OPTS = {
'average': Boolean,
'help': Boolean,
'end': Date,
'max-latency': Number,
'max-requests': Number,
'output': String,
'percentile': [Number, Array],
'period': Number,
'requests': Boolean,
'start': Date
};
var SHORT_OPTS = {
'a': ['--average'],
'h': ['--help'],
'i': ['--period'],
'e': ['--end'],
'l': ['--max-latency'],
'n': ['--max-requests'],
'o': ['--output'],
'p': ['--percentile'],
'r': ['--requests'],
's': ['--start']
};
///--- Functions
function percentile(p, vals) {
p = parseInt(p, 10);
return vals[(Math.round(((p / 100) * vals.length) + 1 / 2) - 1)].latency;
}
function report(buckets, output) {
Object.keys(buckets).sort(function (a, b) {
return parseInt(a, 10) - parseInt(b, 10);
}).forEach(function (k) {
var avg = 0;
var perc = [];
var req = buckets[k].length;
var sum = 0;
var t = Math.round(buckets[k]._time);
buckets[k] = buckets[k].sort(function (a, b) {
return a.latency - b.latency;
});
buckets[k].forEach(function (v) {
sum += v.latency;
});
if (sum > 0 && req > 0) {
if (output.average)
output.average.push([t, Math.round(sum / req)]);
if (output.requests)
output.requests.push([t, buckets[k].length]);
Object.keys(output.percentile).forEach(function (p) {
var _p = percentile(p, buckets[k]);
output.percentile[p].push([t, _p]);
});
}
});
return output;
}
function usage(code, message) {
var str = '';
Object.keys(SHORT_OPTS).forEach(function (k) {
if (!Array.isArray(SHORT_OPTS[k]))
return;
var opt = SHORT_OPTS[k][0].replace('--', '');
var type = OPTS[opt].name || 'string';
if (type && type === 'boolean')
type = '';
type = type.toLowerCase();
str += ' [--' + opt + ' ' + type + ']';
});
str += ' [file ...]';
if (message)
console.error(message);
console.error('usage: ' + path.basename(process.argv[1]) + str);
process.exit(code);
}
///--- Mainline
var parsed;
try {
parsed = nopt(OPTS, SHORT_OPTS, process.argv, 2);
} catch (e) {
usage(1, e.toString());
}
if (parsed.help)
usage(0);
if (!parsed.average && !parsed.percentile)
usage(1, '--average or --percentile required');
if (parsed.argv.remain.length < 1)
usage(1, 'log file required');
var config = {
average: parsed.average || false,
maxLatency: parsed['max-latency'] || 1000,
maxRequests: parsed['max-requests'] || 10000,
percentile: parsed.percentile || [],
period: parsed.period || 60,
requests: parsed.requests || false,
start: parsed.start ? (parsed.start.getTime() / 1000) : 0,
end: parsed.end ? (parsed.end.getTime() / 1000) : Number.MAX_VALUE
};
var buckets = {};
var done = 0;
parsed.argv.remain.forEach(function (f) {
var stream = readline.createInterface({
input: fs.createReadStream(f),
output: null
})
stream.on('line', function (l) {
var record;
var t = -1;
try {
record = JSON.parse(l);
} catch (e) {
}
if (!record)
return;
var t = -1;
if (record.time)
t = (new Date(record.time).getTime() / 1000);
if (record._audit !== true ||
REQUEST_IDS[record.req_id] ||
t < config.start ||
t > config.end) {
console.error('Skipping %s', l);
}
REQUEST_IDS[record.req_id] = true;
record.time = t;
var b = Math.round(record.time / config.period) + '';
if (!buckets[b])
buckets[b] = [];
buckets[b].push(record);
buckets[b]._time = record.time // good enough
});
stream.on('end', function () {
if (++done === parsed.argv.remain.length) {
console.error('Generating report...');
var output = {
average: config.average ? [] : false,
requests: config.requests ? [] : false,
percentile: {}
};
config.percentile.forEach(function (p) {
output.percentile[p] = [];
});
output = report(buckets, output);
var finalOutput = [];
if (output.average) {
finalOutput.push({
name: 'avg',
values: output.average
});
}
if (output.requests) {
finalOutput.push({
name: 'n',
values: output.requests
});
}
Object.keys(output.percentile).forEach(function (k) {
finalOutput.push({
name: 'p' + k,
values: output.percentile[k]
});
});
console.log(JSON.stringify(finalOutput));
}
});
});