Skip to content

Commit

Permalink
Merge pull request #1 from frankthelen/feature-access-entire-log-item
Browse files Browse the repository at this point in the history
provide log item to mapping functions // add observer function
  • Loading branch information
frankthelen committed Jan 12, 2018
2 parents 9745ecd + 7d6ff78 commit 8c76797
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 10 deletions.
15 changes: 8 additions & 7 deletions README.md
@@ -1,7 +1,7 @@
# good-map

Transform stream for [Hapi Good](https://github.com/hapijs/good) process monitoring.
Properties of the incoming object stream can be modified with your own mapping functions.
Modify the incoming object stream with your own mapping functions.

[![Build Status](https://travis-ci.org/frankthelen/good-map.svg?branch=master)](https://travis-ci.org/frankthelen/good-map)
[![Coverage Status](https://coveralls.io/repos/github/frankthelen/good-map/badge.svg?branch=master)](https://coveralls.io/github/frankthelen/good-map?branch=master)
Expand Down Expand Up @@ -63,10 +63,11 @@ await server.start();

The `good-map` transform stream can be placed anywhere in the pipeline where log values are still objects, e.g., after `Squeeze`.

`GoodMap(rules, [options])` has the following parameters:
`args` in the Good configuration for `good-map` is an array with two arguments which are passed to `GoodMap(rules, [options])`:

* `rules`: An object with the following parameters:
- `events`: An optional list of Hapi server events, e.g., `['request']`, for filtering purposes.
- `tags`: An optional list of event tags, e.g., `['error']`, for filtering purposes.
- `map`: An object mapping log item properties (including deep properties separated by dots), e.g., `timestamp` or `error.message`, using a mapping function of the form `value => 'newValue'`, e.g., `() => '***'` or `str => str.toUpperCase()`. If a property does not exist (before), it will *not* be set. If the mapping function returns `undefined`, the property will be deleted. If the mapping function throws an error (for whatever reason), the error will be suppressed.
* `options`: Optional configuration object that gets passed to the Node [Stream.Transform](http://nodejs.org/api/stream.html#stream_class_stream_transform) constructor. `objectMode` is always `true`.
* `rules`: An object with the following parameters (all optional):
- `events`: A list of Hapi server event types, e.g., `['request']`, for filtering purposes.
- `tags`: A list of event tags, e.g., `['error']`, for filtering purposes.
- `map`: An object that maps the log item's properties (including deep properties separated by dots), e.g., `timestamp` or `'error.message'` to a mapping function (synchronous). It has the form `(value) => 'newValue'`, e.g., `() => '***'` or `str => str.toUpperCase()`. If a property does not exist (before), it is *not* set. If the mapping function returns `undefined`, the property is deleted. If the mapping function throws an error, the error is ignored. For full flexibility, the second parameter provides access to the complete log item: `(value, item) => ...`.
- `observe`: Listen to the complete log item as it appears in the stream. The observer function (synchronous) has the form `(item) => { ... }`. If the observer function throws an error, the error is ignored.
* `options`: An optional configuration object that gets passed to the Node [Stream.Transform](http://nodejs.org/api/stream.html#stream_class_stream_transform) constructor. `objectMode` is always `true`.
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "good-map",
"version": "1.0.0",
"version": "1.1.0",
"description": "Transform stream for Hapi Good process monitoring",
"main": "src/index.js",
"repository": {
Expand Down
16 changes: 14 additions & 2 deletions src/index.js
Expand Up @@ -6,27 +6,32 @@ const unset = require('lodash.unset');
class GoodMap extends Stream.Transform {
constructor(rules = {}, options = {}) {
super({ ...options, objectMode: true });
const { events = [], tags = [], map = {} } = rules;
const {
events = [], tags = [], map = {}, observe = () => {},
} = rules;
this.events = events;
this.tags = tags;
this.map = map;
this.props = Object.keys(map);
this.observe = observe;
}
_transform(data, enc, next) {
// filter
const { event, tags = [] } = data;
if (this.events.length && !this.events.includes(event)) {
return next(null, data);
}
if (this.tags.length && !this.tags.reduce((acc, tag) => acc || tags.includes(tag), false)) {
return next(null, data);
}
// map properties
this.props.forEach((prop) => {
try {
const value = get(data, prop);
if (value === undefined) {
// do nothing
} else {
const newValue = this.map[prop](value);
const newValue = this.map[prop](value, data);
if (newValue === undefined) {
unset(data, prop);
} else {
Expand All @@ -37,6 +42,13 @@ class GoodMap extends Stream.Transform {
// ignore
}
});
// observe
try {
this.observe(data);
} catch (error) {
// ignore
}
// continue
return next(null, data);
}
}
Expand Down
32 changes: 32 additions & 0 deletions test/index.spec.js
Expand Up @@ -133,4 +133,36 @@ describe('GoodMap', () => {
expect(result.length).to.equal(3);
expect(result[2]).to.not.have.property('timestamp');
});

it('should provide the log item in the mapping function', async () => {
const collect = [];
const options = {
map: {
prop: (value, item) => { collect.push(item); return true; },
},
};
await pipe(options, testEvents);
expect(collect.length).to.equal(3);
expect(collect[0]).to.deep.equal({ ...testEvents[0], prop: true });
expect(collect[1]).to.deep.equal({ ...testEvents[1], prop: true });
expect(collect[2]).to.deep.equal({ ...testEvents[2], prop: true });
});

it('should call the observer function after mapping', async () => {
const collect = [];
const options = {
map: {
prop: () => true,
},
observe: (item) => {
item.bla = true; // eslint-disable-line no-param-reassign
collect.push(item);
},
};
await pipe(options, testEvents);
expect(collect.length).to.equal(3);
expect(collect[0]).to.deep.equal({ ...testEvents[0], prop: true, bla: true });
expect(collect[1]).to.deep.equal({ ...testEvents[1], prop: true, bla: true });
expect(collect[2]).to.deep.equal({ ...testEvents[2], prop: true, bla: true });
});
});

0 comments on commit 8c76797

Please sign in to comment.