84 lines
3.2 KiB
JavaScript
84 lines
3.2 KiB
JavaScript
// Marks on a time chart: a thin line at each moment something was logged
|
|
// (#6002, the syslog errors of a host or a device), so a reading's jump can be
|
|
// read against what the device said at the time.
|
|
//
|
|
// A chart opts in by holding marks in options.plugins.stewardMarks.marks, as
|
|
// [{at: epoch ms, level, text}]. A linear x axis takes the time itself; a
|
|
// category axis of ISO times takes the nearest label. Hovering near a mark
|
|
// shows its text in the chart's title attribute.
|
|
(function () {
|
|
if (!window.Chart) return;
|
|
function xOf(chart, at) {
|
|
var x = chart.scales.x;
|
|
if (!x) return null;
|
|
if (x.type !== 'category') return x.getPixelForValue(at);
|
|
var labels = chart.data.labels || [];
|
|
if (!labels.length) return null;
|
|
var first = Date.parse(labels[0]), last = Date.parse(labels[labels.length - 1]);
|
|
if (at < first || at > last) return null;
|
|
var best = 0, gap = Infinity;
|
|
for (var i = 0; i < labels.length; i++) {
|
|
var d = Math.abs(Date.parse(labels[i]) - at);
|
|
if (d < gap) { gap = d; best = i; }
|
|
}
|
|
return x.getPixelForValue(best);
|
|
}
|
|
Chart.register({
|
|
id: 'stewardMarks',
|
|
afterDatasetsDraw: function (chart, args, opts) {
|
|
var marks = (opts && opts.marks) || [];
|
|
if (!marks.length) return;
|
|
var area = chart.chartArea, ctx = chart.ctx;
|
|
var crit = window.stewardColor ? stewardColor('--red') : 'red';
|
|
chart.$stewardMarks = [];
|
|
ctx.save();
|
|
ctx.lineWidth = 1;
|
|
ctx.setLineDash([3, 3]);
|
|
ctx.strokeStyle = crit;
|
|
ctx.globalAlpha = 0.55;
|
|
marks.forEach(function (m) {
|
|
var px = xOf(chart, m.at);
|
|
if (px == null || px < area.left || px > area.right) return;
|
|
ctx.beginPath();
|
|
ctx.moveTo(px, area.top);
|
|
ctx.lineTo(px, area.bottom);
|
|
ctx.stroke();
|
|
chart.$stewardMarks.push({ x: px, text: m.text, at: m.at });
|
|
});
|
|
ctx.restore();
|
|
},
|
|
afterEvent: function (chart, args) {
|
|
var near = (chart.$stewardMarks || []).filter(function (m) {
|
|
return Math.abs(m.x - args.event.x) <= 3;
|
|
});
|
|
chart.canvas.title = near.map(function (m) {
|
|
return new Date(m.at).toLocaleString([], { hour12: false }) + ' ' + m.text;
|
|
}).join('\n');
|
|
},
|
|
});
|
|
|
|
// Fetch marks from `url` and put them on each chart, redrawing it; `note`
|
|
// (an element, optional) says in words how many there are.
|
|
window.stewardMarkCharts = function (url, charts, note) {
|
|
// A deadline, so a slow answer leaves the charts unmarked rather than waiting on.
|
|
fetch(url, { credentials: 'same-origin', signal: AbortSignal.timeout(10000) })
|
|
.then(function (r) { return r.ok ? r.json() : { marks: [] }; })
|
|
.then(function (body) {
|
|
var marks = body.marks || [];
|
|
charts.forEach(function (ch) {
|
|
if (!ch) return;
|
|
ch.options.plugins.stewardMarks = { marks: marks };
|
|
ch.update('none');
|
|
});
|
|
if (note) {
|
|
note.hidden = !marks.length;
|
|
note.textContent = marks.length
|
|
? (body.more ? 'The newest ' : '') + marks.length + ' syslog error' + (marks.length === 1 ? '' : 's') +
|
|
' in this span marked with dashed red lines; hover one to read it.'
|
|
: '';
|
|
}
|
|
})
|
|
.catch(function () {});
|
|
};
|
|
})();
|