Skip to content

Commit

Permalink
Restored wreck logging. Closes #469. (#514)
Browse files Browse the repository at this point in the history
Cleaned up event handling logic and just added a boolean to control whether events are reported on not. It was much easier and required far less code to keep a handler to all the added event listeners.
  • Loading branch information
arb committed Dec 1, 2016
1 parent 6584880 commit 45606c7
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 86 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -16,3 +16,4 @@ config.json
coverage.*
lib-cov
complexity.md
.vscode/**
29 changes: 28 additions & 1 deletion API.md
Expand Up @@ -29,6 +29,7 @@ as general events are a process-wide facility and will result in duplicated log
- 'response' - the response was sent but request tails may still be pending.
- 'tail' - the response was sent and all request tails completed.
- `[extensions]` - an array of [hapi event names](https://github.com/hapijs/hapi/blob/master/API.md#server-events) to listen for and report via the good reporting mechanism. Can not be any of ['log', 'request-error', 'ops', 'request', 'response', 'tail']. **Disclaimer** This option should be used with caution. This option will allow users to listen to internal events that are not meant for public consumption. The list of available events can change with any changes to the hapi event system. Also, *none* of the official hapijs reporters have been tested against these custom events. The schema for these events can not be guaranteed because they vary from version to version of hapi.
- `[wreck]` - a boolean controlling wreck response logging. Defaults to `false`.
- `[reporters]` - Defaults to `{}`. `reporters` is a `key`, `value` pair where the `key` is a reporter name and the `value` is an array of mixed value types. Valid values for the array items are:
- streams specifications object with the following keys
- `module` - can be :
Expand Down Expand Up @@ -173,7 +174,7 @@ The `toJSON` method of `GreatError` has been overwritten because `Error` objects

### `RequestSent`

Event object associated with the `responseEvent` event option into Good. `request`
Event object associated with the `responseEvent` event option into Good.

- `event` - 'response'
- `timestamp` - JavaScript timestamp that maps to `request.info.received`.
Expand All @@ -196,6 +197,9 @@ Event object associated with the `responseEvent` event option into Good. `reques
- `log` - maps to `request.getLog()` of the hapi request object.
- `tags` - array of strings representing any tags from route config. Maps to `request.route.settings.tags`.
- `config` - plugin-specific config object combining `request.route.settings.plugins.good` and `request.plugins.good`. Request-level overrides route-level. Reporters could use `config` for additional filtering logic.
- `headers` - the request headers if `includes.request` includes "headers"
- `requestPayload` - the request payload if `includes.request` includes "payload"
- `responsePayload` - the response payload if `includes.response` includes "payload"

### `Ops`

Expand Down Expand Up @@ -240,6 +244,29 @@ Event object associated with the "request" event. This is the hapi event emitter
- `path` - incoming path requested. Maps to `request.path`.
- `config` - plugin-specific config object combining `request.route.settings.plugins.good` and `request.plugins.good`. Request-level overrides route-level. Reporters could use `config` for additional filtering logic.

### `WreckResponse`

Event object emitted whenever Wreck finishes making a request to a remote server.

- `event` - 'wreck'
- `timestamp` - timestamp of the incoming `event` object
- `timeSpent` - how many ms it took to make the request
- `pid` - the current process id
- `request` - information about the outgoing request
- `method` - `GET`, `POST`, etc
- `path` - the path requested
- `url` - the full URL to the remote resource
- `protocol` - e.g. `http:`
- `host` - the remote server host
- `headers` - object containing all outgoing request headers
- `response` - information about the incoming request
- `statusCode` - the http status code of the response e.g. 200
- `statusMessage` - e.g. `OK`
- `headers` - object containing all incoming response headers
- `error` - if the response errored, this field will be populated
- `message` - the error message
- `stack` - the stack trace of the error

### Extension Payloads

Because the extension payloads from hapi can vary from one version to another and one event to another, the payload is only loosely defined.
Expand Down
88 changes: 39 additions & 49 deletions lib/monitor.js
Expand Up @@ -23,11 +23,9 @@ class Monitor {
constructor(server, options) {

this.settings = options;
this._state = {
handlers: {},
extensions: {}
};
this._state = { report: false };
this._server = server;
this._reporters = {};

const reducer = (obj, value) => {

Expand All @@ -38,8 +36,6 @@ class Monitor {
const reqOptions = this.settings.includes.request.reduce(reducer, {});
const resOptions = this.settings.includes.response.reduce(reducer, {});

this._reporters = {};

this._ops = this.settings.ops && new Oppsy(server, this.settings.ops.config);

// Event handlers
Expand Down Expand Up @@ -69,19 +65,13 @@ class Monitor {
this.push(() => new Utils.Ops(results));
};

this._extensionHandler = function () {
if (this.settings.wreck) {
this._wreckHandler = (error, request, response, start, uri) => {

const args = Array(arguments.length);
for (let i = 0; i < arguments.length; ++i) {
args[i] = arguments[i];
}
const event = {
event: args.shift(),
timestamp: Date.now(),
payload: args
this.push(() => new Utils.WreckResponse(error, request, response, start, uri));
};
this.push(() => Object.assign({}, event));
};
}

}

startOps(interval) {
Expand All @@ -91,7 +81,7 @@ class Monitor {

start(callback) {

internals.forOwn(this.settings.reporters, (reporterName, streamsSpec) => {
internals.forOwn(this.settings.reporters, (streamsSpec, reporterName) => {

if (!streamsSpec.length) {
return;
Expand Down Expand Up @@ -140,57 +130,55 @@ class Monitor {
});
});

this._state.handlers.log = this._logHandler;
this._state.handlers['request-error'] = this._errorHandler;
this._state.handlers[this.settings.responseEvent] = this._responseHandler;
this._state.handlers.request = this._requestLogHandler;
this._state.report = true;

// Initialize Events
internals.forOwn(this._state.handlers, (event, handler) => {

this._server.on(event, handler);
});
this._server.on('log', this._logHandler);
this._server.on('request-error', this._errorHandler);
this._server.on(this.settings.responseEvent, this._responseHandler);
this._server.on('request', this._requestLogHandler);

if (this.settings.wreck) {
const wreck = Symbol.for('wreck');
process[wreck].on('response', this._wreckHandler);
}

if (this._ops) {
this._ops.on('ops', this._opsHandler);
this._ops.on('error', (err) => {

console.error(err);
});
this._ops.on('error', console.error);
}

const self = this;
// Events can not be any of ['log', 'request-error', 'ops', 'request', 'response', 'tail']
for (let i = 0; i < this.settings.extensions.length; ++i) {
const event = this.settings.extensions[i];
const handler = this._extensionHandler.bind(this, event);

this._state.extensions[event] = handler;
this._server.on(this.settings.extensions[i], handler);
// Can't use () => because of "arguments"
this._server.on(this.settings.extensions[i], function () {

const args = Array.from(arguments);
const payload = {
event,
timestamp: Date.now(),
payload: args
};
self.push(() => Object.assign({}, payload));
});
}

return callback();
}
stop(callback) {

const state = this._state;
state.report = false;

if (this._ops) {

this._ops.stop();
this._ops.removeAllListeners();
}

internals.forOwn(state.handlers, (event, handler) => {

this._server.removeListener(event, handler);
});

internals.forOwn(state.extensions, (event, handler) => {

this._server.removeListener(event, handler);
});

internals.forOwn(this._reporters, (name, reporter) => {
internals.forOwn(this._reporters, (reporter) => {

reporter.end();
});
Expand All @@ -201,10 +189,12 @@ class Monitor {
}
push(value) {

internals.forOwn(this._reporters, (name, reporter) => {
if (this._state.report) {
internals.forOwn(this._reporters, (reporter) => {

reporter.write(value());
});
reporter.write(value());
});
}
}
}

Expand All @@ -215,6 +205,6 @@ internals.forOwn = (obj, func) => {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
func(key, obj[key]);
func(obj[key], key);
}
};
3 changes: 2 additions & 1 deletion lib/schema.js
Expand Up @@ -29,5 +29,6 @@ exports.monitor = Joi.object().keys({
ops: Joi.alternatives([Joi.object(), Joi.bool().allow(false)]).default({
config: {},
interval: 15000
})
}),
wreck: Joi.bool().default(false)
}).unknown(false);
37 changes: 36 additions & 1 deletion lib/utils.js
Expand Up @@ -146,6 +146,40 @@ class RequestLog {
}
}

class WreckResponse {
constructor(error, request, response, start, uri) {

response = response || {};
uri = uri || {};
this.event = 'wreck';
this.timestamp = Date.now();
this.timeSpent = this.timestamp - start;
this.pid = process.pid;

if (error) {
this.error = {
message: error.message,
stack: error.stack
};
}

this.request = {
method: uri.method,
path: uri.path,
url: uri.href,
protocol: uri.protocol,
host: uri.host,
headers: request._headers
};

this.response = {
statusCode: response.statusCode,
statusMessage: response.statusMessage,
headers: response.headers
};
}
}


class NoOp extends Stream.Transform {
constructor() {
Expand All @@ -165,5 +199,6 @@ module.exports = {
Ops,
RequestLog,
RequestSent,
NoOp
NoOp,
WreckResponse
};
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -28,11 +28,11 @@
"async": "2.1.x",
"code": "4.x.x",
"hapi": "16.x.x",
"lab": "11.x.x"
"lab": "11.x.x",
"wreck": "10.x.x"
},
"scripts": {
"test": "lab -m 5000 -t 100 -v -La code",
"test-cov-html": "lab -m 5000 -r html -o coverage.html -La code"
"test": "lab -m 5000 -t 100 -v -La code"
},
"license": "BSD-3-Clause"
}

0 comments on commit 45606c7

Please sign in to comment.