koa1-server-timing
Version:
Koa1 Server Timinig header generation middleware
48 lines (41 loc) • 2.06 kB
JavaScript
const assert = require('assert');
function getSlug(str) {
if (typeof str !== 'string') return '';
return str.toLowerCase().trim().replace(/\s+/g, '-').replace(/[^a-z-]/g, '');
}
module.exports = function ({ total } = { total: process.env.NODE_ENV !== 'production' }) {
return function * (next) {
const ctx = this;
// attaching timings object to state
ctx.state.timings = {
all: new Map(),
startSpan(spanDesc, spanSlug = getSlug(spanDesc)) {
assert.ok(!this.all.has(spanSlug), 'This span is running already, name muse be unique!');
assert.ok(spanSlug.length, 'Either slug or description must be non-empy');
this.all.set(spanSlug, { start: process.hrtime(), desc: spanDesc || '' });
return spanSlug;
},
stopSpan(spanName) {
const key = this.all.has(spanName) ? spanName : getSlug(spanName);
assert.ok(this.all.has(key), 'Span to stop is not found!');
assert.ok('start' in this.all.get(key), 'Span to stop were never started!');
const timing = this.all.get(key);
timing.stop = process.hrtime(timing.start);
}
};
// if we need total, then start it now
if (total) ctx.state.timings.startSpan('total');
// letting other things pass now
yield next;
// Terminate all spans that wasn't explicitely terminated
ctx.state.timings.all.forEach(timing => { if (!timing.stop) timing.stop = process.hrtime(timing.start); });
// constructing headers array
const metrics = [];
for (const [key, { stop: [sec, nanosec], desc }] of ctx.state.timings.all) {
const dur = (sec * 1000 + nanosec / 1000000).toFixed(2);
metrics.push(`${key};dur=${dur}${desc.length && key !== desc ? `;desc="${desc}"` : ''}`);
}
// Adding our headers now
if (metrics.length) ctx.append('Server-Timing', metrics.join(', '));
}
}