good-json
Version:
JSON transformer middleware for Good (HapiJS). Can be used to transport JSON-only logs to fluentd, etc
122 lines (94 loc) • 3.42 kB
JavaScript
const Hoek = require('hoek');
const Moment = require('moment');
const Stream = require('stream');
const SafeStringify = require('json-stringify-safe');
const internals = {
defaults: {
format: 'YYMMDD/HHmmss.SSS',
utc: true
}
};
internals.utility = {
formatOutput (event, settings) {
let timestamp = Moment(parseInt(event.timestamp, 10));
if (settings.utc) {
timestamp = timestamp.utc();
}
timestamp = timestamp.format(settings.format);
const strTags = event.tags.map(elem => `"${elem}"`);
const tags = ` [${strTags}] `;
// add event id information if available, typically for 'request' events
const id = event.id ? ` (${event.id})` : '';
const output = `{"time": "${timestamp}", "eventId": "${id}", "tags": ${tags}, "data": ${event.data}}`;
return `${output}\n`;
},
formatResponse (event, tags, settings) {
const query = event.query ? SafeStringify(event.query) : '';
const statusCode = event.statusCode || '';
// event, timestamp, id, instance, labels, method, path, query, responseTime,
// statusCode, pid, httpVersion, source, remoteAddress, userAgent, referer, log
// method, pid, error
const output = `{"instance": "${event.instance}", "method": "${event.method}", "path": "${event.path}", "query": "${query}", "statusCode": "${statusCode}". "responseTime": "${event.responseTime}ms"}`;
const response = {
id: event.id,
timestamp: event.timestamp,
tags,
data: output
};
return internals.utility.formatOutput(response, settings);
},
formatOps (event, tags, settings) {
const memory = Math.round(event.proc.mem.rss / (1024 * 1024));
const output = `{"memory": "${memory}Mb", "uptime": "${event.proc.uptime}", "load": "[${event.os.load}]"}`;
const ops = {
timestamp: event.timestamp,
tags,
data: output
};
return internals.utility.formatOutput(ops, settings);
},
formatError (event, tags, settings) {
const output = `{"message": "${event.error.message}", "stack": "${event.error.stack}"}`;
const error = {
id: event.id,
timestamp: event.timestamp,
tags,
data: output
};
return internals.utility.formatOutput(error, settings);
},
formatDefault (event, tags, settings) {
const defaults = {
timestamp: event.timestamp,
id: event.id,
tags,
data: SafeStringify(event.data) || '{"message": null}'
};
return internals.utility.formatOutput(defaults, settings);
}
};
class LogzBridge extends Stream.Transform {
constructor (config) {
super({ objectMode: true });
this._settings = Hoek.applyToDefaults(internals.defaults, config || {});
}
_transform (data, enc, next) {
const eventName = data.event;
let tags = data.tags;
if (!Array.isArray(tags)) {
tags = [tags];
}
tags.unshift(eventName);
if (eventName === 'error' || data.error instanceof Error) {
return next(null, internals.utility.formatError(data, tags, this._settings));
}
if (eventName === 'ops') {
return next(null, internals.utility.formatOps(data, tags, this._settings));
}
if (eventName === 'response') {
return next(null, internals.utility.formatResponse(data, tags, this._settings));
}
return next(null, internals.utility.formatDefault(data, tags, this._settings));
}
}
module.exports = LogzBridge;