Skip to content

Commit

Permalink
Merge pull request #67 from thebergamo/updates
Browse files Browse the repository at this point in the history
Update to new interface of Good and es6
  • Loading branch information
arb committed Mar 30, 2016
2 parents addeaff + d7e3e52 commit a89a608
Show file tree
Hide file tree
Showing 6 changed files with 408 additions and 875 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Expand Up @@ -2,5 +2,5 @@ sudo: false
language: node_js

node_js:
- 0.10
- 4
- "4"
- "node"
27 changes: 8 additions & 19 deletions README.md
Expand Up @@ -8,35 +8,24 @@ Lead Maintainer: [Adam Bretz](https://github.com/arb)

## Usage

`good-console` is a [good](https://github.com/hapijs/good) reporter implementation to write [hapi](http://hapijs.com/) server events to the console.
`good-console` is a [good](https://github.com/hapijs/good) reporter implementation to transform streams of [hapi](http://hapijs.com/) server events into formatted strings.

## `GoodConsole(events, [config])`
## `GoodConsole([config])`
Creates a new GoodConsole object with the following arguments:

- `events` - an object of key value pairs.
- `key` - one of the supported [good events](https://github.com/hapijs/good) indicating the hapi event to subscribe to
- `value` - a single string or an array of strings to filter incoming events. "\*" indicates no filtering. `null` and `undefined` are assumed to be "\*"
- `[config]` - optional configuration object with the following available keys
- `format` - [MomentJS](http://momentjs.com/docs/#/displaying/format/) format string. Defaults to 'YYMMDD/HHmmss.SSS'.
- `utc` - boolean controlling Moment using [utc mode](http://momentjs.com/docs/#/parsing/utc/) or not. Defaults to `true`.
- `color` - a boolean specifying whether to output in color. Defaults to `true`.

## Good Console Methods
### `goodconsole.init(stream, emitter, callback)`
Initializes the reporter with the following arguments:

- `stream` - a Node readable stream that will be the source of data for this reporter. It is assumed that `stream` is in `objectMode`.
- `emitter` - an event emitter object.
- `callback` - a callback to execute when the start function has complete all the necessary set up steps and is ready to receive data.
The example above we use the module `good-squeeze` for filtering our events and pipe that to `GoodConsole` and finally to `stdout` as the `Good` API suggest.

## Output Formats

Below are example outputs for the designated event type:

- "ops" - 141225/093015.900, [ops, `event.tags`], memory: 10Mb, uptime (seconds): 1000, load: [ 1.650390625, 1.6162109375, 1.65234375 ]
- "error" - 141225/093015.900, [error, `event.tags`], message: there was an error, stack: `eventData.stack`
- "request" - 141225/093015.900, [request, `event.tags`], data: {"message":"you made a request to a resource"}
- "log" - 141225/093015.900, [log, `event.tags`], data: you logged a message
- "response" - 141223/164207.694, [response], localhost: post /data {"name":"adam"} 200 (150ms) response payload: {"foo":"bar","value":1}
- "wreck" - 141223/164207.694, [wreck], get: http://hapijs.com/test 200 OK (29ms)
- "wreck" (with error) - 151105/084704.603, [wreck], get: http://hapijs.com/test (7ms) error: some error stack: some stack trace
- "ops" - 160318/013330.957, [ops] memory: 29Mb, uptime (seconds): 6, load: [1.650390625,1.6162109375,1.65234375]
- "error" - 160318/013330.957, [error,`event.tags`] message: Just a simple error stack: `event.error.stack`
- "request" - 160318/013330.957, [request,`event.tags`] data: you made a request
- "log" - 160318/013330.957, [log,`event.tags`] data: you made a default
- "response" - 160318/013330.957, [response, `event.tags`] http://localhost:61253: post /data {"name":"adam"} 200 (150ms)
271 changes: 129 additions & 142 deletions lib/index.js
@@ -1,195 +1,182 @@
// Load modules
'use strict';

var Squeeze = require('good-squeeze').Squeeze;
var Hoek = require('hoek');
var Moment = require('moment');
var SafeStringify = require('json-stringify-safe');
var Through = require('through2');
// Load Modules

// Declare internals
const Hoek = require('hoek');
const Moment = require('moment');
const Stream = require('stream');
const SafeStringify = require('json-stringify-safe');

var internals = {
const internals = {
defaults: {
format: 'YYMMDD/HHmmss.SSS',
utc: true,
color: true
}
};

module.exports = internals.GoodConsole = function (events, config) {
internals.utility = {
formatOutput(event, settings) {

if (!(this instanceof internals.GoodConsole)) {
return new internals.GoodConsole(events, config);
}
config = config || {};
this._settings = Hoek.applyToDefaults(internals.defaults, config);
this._filter = new Squeeze(events);
};
let timestamp = Moment(parseInt(event.timestamp, 10));

internals.GoodConsole.prototype.init = function (stream, emitter, callback) {
if (settings.utc) {
timestamp = timestamp.utc();
}

var self = this;
timestamp = timestamp.format(settings.format);

if (!stream._readableState.objectMode) {
return callback(new Error('stream must be in object mode'));
}
event.tags = event.tags.toString();
const tags = ` [${event.tags}] `;

stream.pipe(this._filter).pipe(Through.obj(function goodConsoleTransform (data, enc, next) {
const output = `${timestamp},${tags}${event.data}`;

var eventName = data.event;
var tags = [];
return output;
},

/*eslint-disable */
if (Array.isArray(data.tags)) {
tags = data.tags.concat([]);
} else if (data.tags != null) {
tags = [data.tags];
}
/*eslint-enable */
formatMethod(method, settings) {

tags.unshift(eventName);
const methodColors = {
get: 32,
delete: 31,
put: 36,
post: 33
};

if (eventName === 'response') {
this.push(self._formatResponse(data, tags));
return next();
let formattedMethod = method.toLowerCase();
if (settings.color) {
const color = methodColors[method.toLowerCase()] || 34;
formattedMethod = `\x1b[1;${color}m${formattedMethod}\x1b[0m`;
}

if (eventName === 'wreck') {
this.push(self._formatWreck(data, tags));
return next();
return formattedMethod;
},

formatStatusCode(statusCode, settings) {

let color;
if (statusCode && settings.color) {
color = 32;
if (statusCode >= 500) {
color = 31;
}
else if (statusCode >= 400) {
color = 33;
}
else if (statusCode >= 300) {
color = 36;
}

return `\x1b[${color}m${statusCode}\x1b[0m`;
}

var eventPrintData = {
timestamp: data.timestamp || Date.now(),
tags: tags,
data: undefined
return statusCode;
},

formatResponse(event, tags, settings) {

const query = event.query ? JSON.stringify(event.query) : '';
const method = internals.utility.formatMethod(event.method, settings);
const statusCode = internals.utility.formatStatusCode(event.statusCode, settings) || '';

// event, timestamp, id, instance, labels, method, path, query, responseTime,
// statusCode, pid, httpVersion, source, remoteAddress, userAgent, referer, log
// method, pid, error
const output = `${event.instance}: ${method} ${event.path} ${query} ${statusCode} (${event.responseTime}ms)`;

const response = {
timestamp: event.timestamp,
tags,
data: output
};

if (eventName === 'ops') {
eventPrintData.data = Hoek.format('memory: %sMb, uptime (seconds): %s, load: %s',
Math.round(data.proc.mem.rss / (1024 * 1024)),
data.proc.uptime,
data.os.load);
return internals.utility.formatOutput(response, settings);
},

this.push(self._printEvent(eventPrintData));
return next();
}
formatOps(event, tags, settings) {

if (eventName === 'error') {
eventPrintData.data = 'message: ' + data.error.message + ' stack: ' + data.error.stack;
const memory = Math.round(event.proc.mem.rss / (1024 * 1024));
const output = `memory: ${memory}Mb, uptime (seconds): ${event.proc.uptime}, load: [${event.os.load}]`;

this.push(self._printEvent(eventPrintData));
return next();
}
const ops = {
timestamp: event.timestamp,
tags,
data: output
};

if (eventName === 'request' || eventName === 'log') {
eventPrintData.data = 'data: ' + (typeof data.data === 'object' ? SafeStringify(data.data) : data.data);
return internals.utility.formatOutput(ops, settings);
},

this.push(self._printEvent(eventPrintData));
return next();
}
formatError(event, tags, settings) {

// Event that is unknown to good-console, try a defualt.
if (data.data) {
eventPrintData.data = 'data: ' + (typeof data.data === 'object' ? SafeStringify(data.data) : data.data);
}
else {
eventPrintData.data = 'data: (none)';
}
const output = `message: ${event.error.message} stack: ${event.error.stack}`;

this.push(self._printEvent(eventPrintData));
return next();
})).pipe(process.stdout);
const error = {
timestamp: event.timestamp,
tags,
data: output
};

callback();
};
return internals.utility.formatOutput(error, settings);
},

internals.GoodConsole.prototype._printEvent = function (event) {
formatDefault(event, tags, settings) {

var m = Moment(parseInt(event.timestamp, 10));
if (!this._settings.utc) { m.local(); }
const data = typeof event.data === 'object' ? SafeStringify(event.data) : event.data;
const output = `data: ${data}`;

var timestring = m.format(this._settings.format);
var data = event.data;
var output = timestring + ', [' + event.tags.toString() + '], ' + data;
const defaults = {
timestamp: event.timestamp,
tags,
data: output
};

return output + '\n';
return internals.utility.formatOutput(defaults, settings);
}
};

internals.GoodConsole.prototype._formatResponse = function (event, tags) {
class GoodConsole extends Stream.Transform {
constructor(config) {

var query = event.query ? JSON.stringify(event.query) : '';
var responsePayload = '';
super({ objectMode: true });

if (typeof event.responsePayload === 'object' && event.responsePayload) {
responsePayload = 'response payload: ' + SafeStringify(event.responsePayload);
config = config || {};
this._settings = Hoek.applyToDefaults(internals.defaults, config);
}

var method = this._formatMethod(event.method);
var statusCode = this._formatStatusCode(event.statusCode) || '';

return this._printEvent({
timestamp: event.timestamp,
tags: tags,
//instance, method, path, query, statusCode, responseTime, responsePayload
data: Hoek.format('%s: %s %s %s %s (%sms) %s', event.instance, method, event.path, query, statusCode, event.responseTime, responsePayload)
});
};
_transform(data, enc, next) {

internals.GoodConsole.prototype._formatWreck = function (event, tags) {
const eventName = data.event;
let tags = [];

var data, method, statusCode;
method = this._formatMethod(event.request.method);
if (Array.isArray(data.tags)) {
tags = data.tags.concat([]);
}
else if (data.tags) {
tags = [data.tags];
}

if (event.error) {
data = Hoek.format('%s: %s (%sms) error: %s stack: %s', method, event.request.url, event.timeSpent, event.error.message, event.error.stack);
} else {
statusCode = this._formatStatusCode(event.response.statusCode);
data = Hoek.format('%s: %s %s %s (%sms)', method, event.request.url, statusCode, event.response.statusMessage, event.timeSpent);
}
tags.unshift(eventName);

if (eventName === 'error') {
return next(null, internals.utility.formatError(data, tags, this._settings));
}

return this._printEvent({
timestamp: event.timestamp,
tags: tags,
data: data
});
};
if (eventName === 'ops') {
return next(null, internals.utility.formatOps(data, tags, this._settings));
}

internals.GoodConsole.prototype._formatMethod = function (method) {

var color;
var methodColors = {
get: 32,
delete: 31,
put: 36,
post: 33
};
var formattedMethod = method.toLowerCase();
if (this._settings.color) {
color = methodColors[method.toLowerCase()] || 34;
formattedMethod = '\x1b[1;' + color + 'm' + formattedMethod + '\x1b[0m';
}
return formattedMethod;
};
if (eventName === 'response') {
return next(null, internals.utility.formatResponse(data, tags, this._settings));
}

internals.GoodConsole.prototype._formatStatusCode = function (statusCode) {

var color;
if (statusCode && this._settings.color) {
color = 32;
if (statusCode >= 500) {
color = 31;
} else if (statusCode >= 400) {
color = 33;
} else if (statusCode >= 300) {
color = 36;
if (!data.data) {
data.data = '(none)';
}
return '\x1b[' + color + 'm' + statusCode + '\x1b[0m';

return next(null, internals.utility.formatDefault(data, tags, this._settings));
}
return statusCode;
};
}

module.exports = GoodConsole;

internals.GoodConsole.attributes = {
pkg: require('../package.json')
};

0 comments on commit a89a608

Please sign in to comment.