Merge pull request #5079 from node-red/5068-improve-error-object-debug

Improve debug display of error objects
pull/5081/head
Nick O'Leary 2025-03-11 16:46:08 +00:00 committed by GitHub
commit a976f48be1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 108 additions and 97 deletions

View File

@ -1285,7 +1285,6 @@ RED.utils = (function() {
payload = JSON.parse(payload);
} else if (/error/i.test(format)) {
payload = JSON.parse(payload);
payload = (payload.name?payload.name+": ":"")+payload.message;
} else if (format === 'null') {
payload = null;
} else if (format === 'undefined') {

View File

@ -828,18 +828,25 @@ function encodeObject(msg,opts) {
debuglength = opts.maxLength;
}
var msgType = typeof msg.msg;
if (msg.msg instanceof Error) {
if (msg.msg instanceof Error || /Error/.test(msg.msg?.__proto__?.name)) {
msg.format = "error";
var errorMsg = {};
if (msg.msg.name) {
errorMsg.name = msg.msg.name;
const cause = msg.msg.cause
const value = {
__enc__: true,
type: 'error',
data: {
name: msg.msg.name,
message: hasOwnProperty.call(msg.msg, 'message') ? msg.msg.message : msg.msg.toString(),
cause: cause + "",
stack: msg.msg.stack,
}
}
if (hasOwnProperty.call(msg.msg, 'message')) {
errorMsg.message = msg.msg.message;
} else {
errorMsg.message = msg.msg.toString();
// Remove cause if not defined
if (!cause) {
delete value.data.cause
}
msg.msg = JSON.stringify(errorMsg);
msg.msg = JSON.stringify(value);
} else if (msg.msg instanceof Buffer) {
msg.format = "buffer["+msg.msg.length+"]";
msg.msg = msg.msg.toString('hex');
@ -857,6 +864,7 @@ function encodeObject(msg,opts) {
msg.format = "Object";
}
if (/error/i.test(msg.format)) {
// TODO: check if this is needed
msg.msg = JSON.stringify({
name: msg.msg.name,
message: msg.msg.message
@ -904,8 +912,22 @@ function encodeObject(msg,opts) {
__enc__: true,
type: "internal"
}
} else if (value instanceof Error) {
value = value.toString()
} else if (value instanceof Error || /Error/.test(value?.__proto__?.name)) {
const cause = value.cause
value = {
__enc__: true,
type: 'error',
data: {
name: value.name,
message: hasOwnProperty.call(value, 'message') ? value.message : value.toString(),
cause: cause + "",
stack: value.stack,
}
}
// Remove cause if not defined
if (!cause) {
delete value.data.cause
}
} else if (Array.isArray(value) && value.length > debuglength) {
value = {
__enc__: true,
@ -977,8 +999,19 @@ function encodeObject(msg,opts) {
return value;
});
} else {
try { msg.msg = msg.msg.toString(); }
catch(e) { msg.msg = "[Type not printable]" + util.inspect(msg.msg); }
try {
msg.msg = msg.msg.toString();
} catch(e) {
msg.msg.format = 'error'
msg.msg = JSON.stringify({
__enc__: true,
type: 'error',
data: {
message: "[Type not serializable]",
stack: e.stack
}
})
}
}
}
} else if (msgType === "function") {
@ -1009,17 +1042,14 @@ function encodeObject(msg,opts) {
return msg;
} catch(e) {
msg.format = "error";
var errorMsg = {};
if (e.name) {
errorMsg.name = e.name;
}
if (hasOwnProperty.call(e, 'message')) {
errorMsg.message = 'encodeObject Error: ['+e.message + '] Value: '+util.inspect(msg.msg);
} else {
errorMsg.message = 'encodeObject Error: ['+e.toString() + '] Value: '+util.inspect(msg.msg);
}
if (errorMsg.message.length > debuglength) {
errorMsg.message = errorMsg.message.substring(0,debuglength);
const errorMsg = {
__enc__: true,
type: 'error',
data: {
name: e.name,
message: 'encodeObject Error: ' + (hasOwnProperty.call(e, 'message') ? e.message : e.toString()),
stack: e.stack,
}
}
msg.msg = JSON.stringify(errorMsg);
return msg;

View File

@ -173,9 +173,19 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: new Error("oops")});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'{"name":"Error","message":"oops"}',property:"payload",format:"error",path:"global"}
}]);
const fullMsg = JSON.parse(msg)
fullMsg[0].should.have.property('topic', 'debug')
fullMsg[0].should.have.property('data')
fullMsg[0].data.should.have.property('id', 'n1')
fullMsg[0].data.should.have.property('property', 'payload')
fullMsg[0].data.should.have.property('format', 'error')
fullMsg[0].data.should.have.property('path', 'global')
fullMsg[0].data.should.have.property('msg')
const msgData = JSON.parse(fullMsg[0].data.msg)
msgData.should.have.property('__enc__', true)
msgData.should.have.property('type', 'error')
msgData.data.should.have.property('name', 'Error')
msgData.data.should.have.property('message', 'oops')
}, done);
});
});

View File

@ -518,8 +518,8 @@ describe("@node-red/util/util", function() {
}
function testToString(input,msg,expected) {
var result = util.normalisePropertyExpression(input,msg,true);
console.log("+",input);
console.log(result);
// console.log("+",input);
// console.log(result);
result.should.eql(expected);
}
it('pass a.b.c',function() { testABC('a.b.c',['a','b','c']); })
@ -784,9 +784,14 @@ describe("@node-red/util/util", function() {
var result = util.encodeObject(msg);
result.format.should.eql("error");
var resultJson = JSON.parse(result.msg);
resultJson.name.should.eql('encodeError');
resultJson.message.should.eql('encode error');
resultJson.should.have.property("__enc__",true);
resultJson.should.have.property("type","error");
resultJson.should.have.property("data");
resultJson.data.should.have.property("name","encodeError")
resultJson.data.should.have.property("message","encode error")
resultJson.data.should.have.property("stack")
});
it('encodes Error without message', function() {
var err = new Error();
err.name = 'encodeError';
@ -795,8 +800,12 @@ describe("@node-red/util/util", function() {
var result = util.encodeObject(msg);
result.format.should.eql("error");
var resultJson = JSON.parse(result.msg);
resultJson.name.should.eql('encodeError');
resultJson.message.should.eql('error message');
resultJson.should.have.property("__enc__",true);
resultJson.should.have.property("type","error");
resultJson.should.have.property("data");
resultJson.data.should.have.property("name","encodeError")
resultJson.data.should.have.property("message","error message")
resultJson.data.should.have.property("stack")
});
it('encodes Buffer', function() {
var msg = {msg:Buffer.from("abc")};
@ -988,7 +997,13 @@ describe("@node-red/util/util", function() {
var result = util.encodeObject(msg);
result.format.should.eql("array[1]");
var resultJson = JSON.parse(result.msg);
resultJson[0].should.eql('Error: encode error');
resultJson[0].should.have.property("__enc__",true);
resultJson[0].should.have.property("type","error");
resultJson[0].should.have.property("data");
resultJson[0].data.should.have.property("name","Error")
resultJson[0].data.should.have.property("message","encode error")
resultJson[0].data.should.have.property("stack")
});
it('long array in msg', function() {
var msg = {msg:{array:[1,2,3,4]}};
@ -1074,7 +1089,7 @@ describe("@node-red/util/util", function() {
var resultJson = JSON.parse(result.msg);
resultJson.socket.should.eql('[internal]');
});
it('object which fails to serialise', function(done) {
it('object which fails to serialise', function() {
var msg = {
msg: {
obj:{
@ -1093,13 +1108,13 @@ describe("@node-red/util/util", function() {
};
var result = util.encodeObject(msg);
result.format.should.eql("error");
var success = (result.msg.indexOf('cantserialise') > 0);
success &= (result.msg.indexOf('this exception should have been caught') > 0);
success &= (result.msg.indexOf('canserialise') > 0);
success.should.eql(1);
done();
const resultJson = JSON.parse(result.msg);
resultJson.should.have.property("__enc__",true);
resultJson.should.have.property("type","error");
resultJson.should.have.property("data");
resultJson.data.should.have.property("message","encodeObject Error: this exception should have been caught")
});
it('object which fails to serialise - different error type', function(done) {
it('object which fails to serialise - different error type', function() {
var msg = {
msg: {
obj:{
@ -1116,45 +1131,15 @@ describe("@node-red/util/util", function() {
},
}
};
var result = util.encodeObject(msg);
const result = util.encodeObject(msg);
result.format.should.eql("error");
var success = (result.msg.indexOf('cantserialise') > 0);
success &= (result.msg.indexOf('this exception should have been caught') > 0);
success &= (result.msg.indexOf('canserialise') > 0);
success.should.eql(1);
done();
const resultJson = JSON.parse(result.msg);
resultJson.should.have.property("__enc__",true);
resultJson.should.have.property("type","error");
resultJson.should.have.property("data");
resultJson.data.should.have.property("message","encodeObject Error: this exception should have been caught")
});
it('very large object which fails to serialise should be truncated', function(done) {
var msg = {
msg: {
obj:{
big:"",
cantserialise:{
message:'this will not be displayed',
toJSON: function(val) {
throw new Error('this exception should have been caught');
return 'should not display because we threw first';
},
},
canserialise:{
message:'this should be displayed',
}
},
}
};
for (var i = 0; i < 1000; i++) {
msg.msg.obj.big += 'some more string ';
}
var result = util.encodeObject(msg);
result.format.should.eql("error");
var resultJson = JSON.parse(result.msg);
var success = (resultJson.message.length <= 1000);
success.should.eql(true);
done();
});
it('test bad toString', function(done) {
it('test bad toString', function() {
var msg = {
msg: {
mystrangeobj:"hello",
@ -1166,25 +1151,12 @@ describe("@node-red/util/util", function() {
msg.msg.constructor = { name: "strangeobj" };
var result = util.encodeObject(msg);
var success = (result.msg.indexOf('[Type not printable]') >= 0);
success.should.eql(true);
done();
const resultJson = JSON.parse(result.msg);
resultJson.should.have.property("__enc__",true);
resultJson.should.have.property("type","error");
resultJson.should.have.property("data");
resultJson.data.should.have.property("message","[Type not serializable]")
});
it('test bad object constructor', function(done) {
var msg = {
msg: {
mystrangeobj:"hello",
constructor: {
get name(){
throw new Error('Exception in constructor name');
}
}
},
};
var result = util.encodeObject(msg);
done();
});
});
});