Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for exceptions handling #52

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const TransportStream = module.exports = function TransportStream(options = {})
this.format = options.format;
this.level = options.level;
this.handleExceptions = options.handleExceptions;
this.exceptionsLevel = options.exceptionsLevel;
this.handleRejections = options.handleRejections;
this.silent = options.silent;

Expand Down Expand Up @@ -71,13 +72,20 @@ TransportStream.prototype._write = function _write(info, enc, callback) {
return callback(null);
}

// Reset logging level if handling exception
if (info.exception === true && this.exceptionsLevel) {
// Clone info so that the logging level is only changed for this transport
info = cloneObj(info);
info.level = info[LEVEL] = this.exceptionsLevel;
}

// Remark: This has to be handled in the base transport now because we
// cannot conditionally write to our pipe targets as stream. We always
// prefer any explicit level set on the Transport itself falling back to
// any level set on the parent.
const level = this.level || (this.parent && this.parent.level);

if (!level || this.levels[level] >= this.levels[info[LEVEL]]) {
if (info.exception === true || !level || this.levels[level] >= this.levels[info[LEVEL]]) {
if (info && !this.format) {
return this.log(info, callback);
}
Expand Down Expand Up @@ -115,6 +123,18 @@ TransportStream.prototype._write = function _write(info, enc, callback) {
* @private
*/
TransportStream.prototype._writev = function _writev(chunks, callback) {
// Clone chunks so any change made will apply to this transport only
chunks = cloneObjArray(chunks);

chunks.forEach((chunkEntry) => {
// Reset logging level of chunk (info) if handling exception
if (chunkEntry.chunk.exception === true && this.exceptionsLevel) {
// Clone info so that the logging level is only changed for this transport
chunkEntry.chunk = cloneObj(chunkEntry.chunk);
chunkEntry.chunk.level = chunkEntry.chunk[LEVEL] = this.exceptionsLevel;
}
});

if (this.logv) {
const infos = chunks.filter(this._accept, this);
if (!infos.length) {
Expand Down Expand Up @@ -210,6 +230,27 @@ TransportStream.prototype._nop = function _nop() {
return void undefined;
};

function cloneObj(obj) {
const clonedObj = {};

Object.getOwnPropertyNames(obj).forEach((propName) => {
Object.defineProperty(clonedObj, propName, Object.getOwnPropertyDescriptor(obj, propName));
});

Object.setPrototypeOf(clonedObj, Object.getPrototypeOf(obj));

return clonedObj;
}

function cloneObjArray(arr) {
if (typeof arr === 'object') {
if (Array.isArray(arr)) {
return arr.map(cloneObjArray);
}

return cloneObj(arr);
}
}

// Expose legacy stream
module.exports.LegacyTransportStream = require('./legacy');
12 changes: 9 additions & 3 deletions legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const LegacyTransportStream = module.exports = function LegacyTransportStream(op
this.transport = options.transport;
this.level = this.level || options.transport.level;
this.handleExceptions = this.handleExceptions || options.transport.handleExceptions;
this.exceptionsLevel = this.exceptionsLevel || options.transport.exceptionsLevel;

// Display our deprecation notice.
this._deprecated();
Expand Down Expand Up @@ -58,8 +59,13 @@ LegacyTransportStream.prototype._write = function _write(info, enc, callback) {

// Remark: This has to be handled in the base transport now because we
// cannot conditionally write to our pipe targets as stream.
if (!this.level || this.levels[this.level] >= this.levels[info[LEVEL]]) {
this.transport.log(info[LEVEL], info.message, info, this._nop);
if (info.exception === true || !this.level || this.levels[this.level] >= this.levels[info[LEVEL]]) {
this.transport.log(
info.exception === true && this.exceptionsLevel ? this.exceptionsLevel : info[LEVEL],
info.message,
info,
this._nop
);
}

callback(null);
Expand All @@ -77,7 +83,7 @@ LegacyTransportStream.prototype._writev = function _writev(chunks, callback) {
for (let i = 0; i < chunks.length; i++) {
if (this._accept(chunks[i])) {
this.transport.log(
chunks[i].chunk[LEVEL],
chunks[i].chunk.exception === true && this.exceptionsLevel ? this.exceptionsLevel : chunks[i].chunk[LEVEL],
chunks[i].chunk.message,
chunks[i].chunk,
this._nop
Expand Down
118 changes: 115 additions & 3 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,23 @@ describe('TransportStream', () => {
expected.forEach(transport.write.bind(transport));
});

it('{ level } should be ignored when { handleExceptions: true }', () => {
it('{ level } should be ignored when { handleExceptions: true }', done => {
const expected = testOrder.map(levelAndMessage).map(info => {
info.exception = true;
return info;
});

const transport = new TransportStream({
level: 'info',
handleExceptions: true,
log: logFor(testOrder.length, (err, infos) => {
// eslint-disable-next-line no-undefined
assume(err).equals(undefined);
if (err) {
return done(err);
}

assume(infos.length).equals(expected.length);
assume(infos).deep.equals(expected);
done();
})
});

Expand Down Expand Up @@ -192,6 +196,52 @@ describe('TransportStream', () => {

expected.forEach(transport.write.bind(transport));
});

it('should log specified level when { handleExceptions: true, exceptionsLevel: "exception_level" }', done => {
const actual = [];
const expected = [{
exception: true,
[LEVEL]: 'error',
level: 'error',
message: 'Test exception handling'
}, {
[LEVEL]: 'info',
level: 'info',
message: 'Testing ... 1 2 3.'
}];

const transport = new TransportStream({
level: 'info',
handleExceptions: true,
exceptionsLevel: 'fatal',
log(info, next) {
actual.push(info);

if (info.exception) {
assume(info.level).equals(transport.exceptionsLevel);
assume(info[LEVEL]).equals(transport.exceptionsLevel);
}
else {
assume(info).deep.equals(expected[actual.length - 1]);
}

if (actual.length === expected.length) {
return done();
}

next();
}
});

transport.levels = {
'fatal': 0,
'error': 1,
'warn': 2,
'info': 3
};

expected.forEach(transport.write.bind(transport));
});
});
});

Expand Down Expand Up @@ -326,6 +376,68 @@ describe('TransportStream', () => {
expected.forEach(transport.write.bind(transport));
transport.uncork();
});

describe('when { exception: true } in info', () => {
it('should log specified level when { handleExceptions: true, exceptionsLevel: "exception_level" }', done => {
const actual = [];
const expected = [{
exception: true,
[LEVEL]: 'error',
level: 'error',
message: 'Test exception handling #1'
}, {
[LEVEL]: 'info',
level: 'info',
message: 'Testing ... 1 2 3.'
}, {
exception: true,
[LEVEL]: 'error',
level: 'error',
message: 'Test exception handling #2'
}];

const transport = new TransportStream({
level: 'info',
handleExceptions: true,
exceptionsLevel: 'fatal',
log(info, next) {
actual.push(info);

if (info.exception) {
assume(info.level).equals(transport.exceptionsLevel);
assume(info[LEVEL]).equals(transport.exceptionsLevel);
}
else {
assume(info).deep.equals(expected[actual.length - 1]);
}

if (actual.length === expected.length) {
return done();
}

next();
}
});

transport.levels = {
'fatal': 0,
'error': 1,
'warn': 2,
'info': 3
};

//
// Make the standard _write throw to ensure that _writev is called.
//
transport._write = () => {
throw new Error('TransportStream.prototype._write should never be called.');
};

transport.cork();
expected.forEach(transport.write.bind(transport));
transport.uncork();
});
});
});

describe('parent (i.e. "logger") ["pipe", "unpipe"]', () => {
Expand Down
Loading