1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Debug to status option (#1499)

* Let debug optionally target the status line (32 chars only)

* Add batching of messages to debug ws comms

* let Debug handle simple case of NaN

would also close #1530

* Fixup debug tests for batch comms (no new tests yet)

* mixup comms/api test to match new batch mode (no new tests)

* Add test for NaN being sent OK.

* redo original fix to padding / labels for new debug options

* fix debug test (re-add fix from #1444)

* Fix up merge issues in debug tests
This commit is contained in:
Dave Conway-Jones 2018-01-13 16:14:03 +00:00 committed by Nick O'Leary
parent 7bd8d8c3ae
commit 7b1787fdbb
9 changed files with 260 additions and 134 deletions

View File

@ -64,7 +64,9 @@ RED.comms = (function() {
}
}
ws.onmessage = function(event) {
var msg = JSON.parse(event.data);
var message = JSON.parse(event.data);
for (var m = 0; m < message.length; m++) {
var msg = message[m];
if (pendingAuth && msg.auth) {
if (msg.auth === "ok") {
pendingAuth = false;
@ -76,7 +78,8 @@ RED.comms = (function() {
connectWS();
})
}
} else if (msg.topic) {
}
else if (msg.topic) {
for (var t in subscriptions) {
if (subscriptions.hasOwnProperty(t)) {
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
@ -91,6 +94,7 @@ RED.comms = (function() {
}
}
}
}
};
ws.onclose = function() {
if (!active) {

View File

@ -6,11 +6,22 @@
<input id="node-input-complete" type="hidden">
</div>
<div class="form-row">
<label for="node-input-console"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
<select type="text" id="node-input-console" style="display: inline-block; width: 250px; vertical-align: top;">
<option value="false" data-i18n="debug.debtab"></option>
<option value="true" data-i18n="debug.tabcon"></option>
</select>
<label for="node-input-tosidebar"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
<label for="node-input-tosidebar" style="width:70%">
<input type="checkbox" id="node-input-tosidebar" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toSidebar"></span>
</label>
</div>
<div class="form-row">
<label for="node-input-console"> </label>
<label for="node-input-console" style="width:70%">
<input type="checkbox" id="node-input-console" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toConsole"></span>
</label>
</div>
<div class="form-row" id="node-tostatus-line">
<label for="node-input-tostatus"> </label>
<label for="node-input-tostatus" style="width:70%">
<input type="checkbox" id="node-input-tostatus" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toStatus"></span>
</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@ -26,7 +37,7 @@
<p>Alongside each message, the debug sidebar includes information about the time the message was received, the node that sent it and the type of the message.
Clicking on the source node id will reveal that node within the workspace.</p>
<p>The button on the node can be used to enable or disable its output. It is recommended to disable or remove any Debug nodes that are not being used.</p>
<p>The node can also be configured to send all messages to the runtime log.</p>
<p>The node can also be configured to send all messages to the runtime log, or to send short (32 characters) to the status text under the debug node.</p>
</script>
<script src="debug/view/debug-utils.js"></script>
@ -38,7 +49,9 @@
defaults: {
name: {value:""},
active: {value:true},
console: {value:"false"},
tosidebar: {value:true},
console: {value:false},
tostatus: {value:false},
complete: {value:"false", required:true}
},
label: function() {
@ -100,7 +113,6 @@
}
},
onpaletteadd: function() {
var options = {
messageMouseEnter: function(sourceId) {
if (sourceId) {
@ -170,7 +182,6 @@
var sourceNode = RED.nodes.node(o.id) || RED.nodes.node(o.z);
if (sourceNode) {
o._source = {id:sourceNode.id,z:sourceNode.z,name:sourceNode.name};
}
RED.debug.handleDebugMessage(o);
if (subWindow) {
@ -225,6 +236,15 @@
delete RED._debug;
},
oneditprepare: function() {
if (this.tosidebar === undefined) {
this.tosidebar = true;
$("#node-input-tosidebar").prop('checked', true);
}
if (typeof this.console === "string") {
this.console = (this.console == 'true');
$("#node-input-console").prop('checked', this.console);
$("#node-input-tosidebar").prop('checked', true);
}
$("#node-input-typed-complete").typedInput({types:['msg', {value:"full",label:RED._("node-red:debug.msgobj"),hasValue:false}]});
if (this.complete === "true" || this.complete === true) {
// show complete message object
@ -240,6 +260,16 @@
) {
$("#node-input-typed-complete").typedInput('value','payload');
}
if ($("#node-input-typed-complete").typedInput('type') === 'msg') {
$("#node-tostatus-line").show();
}
else { $("#node-tostatus-line").hide(); }
});
$("#node-input-complete").on('change',function() {
if ($("#node-input-typed-complete").typedInput('type') === 'msg') {
$("#node-tostatus-line").show();
}
else { $("#node-tostatus-line").hide(); }
});
},
oneditsave: function() {
@ -250,7 +280,6 @@
$("#node-input-complete").val($("#node-input-typed-complete").typedInput('value'));
}
}
});
})();
</script>

View File

@ -13,14 +13,36 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.complete = (n.complete||"payload").toString();
if (this.complete === "false") {
this.complete = "payload";
}
this.console = n.console;
if (this.complete === "false") { this.complete = "payload"; }
this.console = ""+(n.console || false);
this.tostatus = n.tostatus || false;
this.tosidebar = n.tosidebar;
if (this.tosidebar === undefined) { this.tosidebar = true; }
this.severity = n.severity || 40;
this.active = (n.active === null || typeof n.active === "undefined") || n.active;
this.status({});
var node = this;
var levels = {
off: 1,
fatal: 10,
error: 20,
warn: 30,
info: 40,
debug: 50,
trace: 60,
audit: 98,
metric: 99
};
var colors = {
"0": "grey",
"10": "grey",
"20": "red",
"30": "yellow",
"40": "grey",
"50": "green",
"60": "blue"
};
this.on("input",function(msg) {
if (this.complete === "true") {
@ -28,10 +50,11 @@ module.exports = function(RED) {
if (this.console === "true") {
node.log("\n"+util.inspect(msg, {colors:useColors, depth:10}));
}
if (this.active) {
if (this.active && this.tosidebar) {
sendDebug({id:node.id, name:node.name, topic:msg.topic, msg:msg, _path:msg._path});
}
} else {
}
else {
// debug user defined msg property
var property = "payload";
var output = msg[property];
@ -53,8 +76,16 @@ module.exports = function(RED) {
}
}
if (this.active) {
if (this.tosidebar == true) {
sendDebug({id:node.id, z:node.z, name:node.name, topic:msg.topic, property:property, msg:output, _path:msg._path});
}
if (this.tostatus === true) {
var st = util.inspect(output);
if (st.length > 32) { st = st.substr(0,32) + "..."; }
node.oldStatus = {fill:colors[node.severity], shape:"dot", text:st};
node.status(node.oldStatus);
}
}
}
});
}
@ -196,9 +227,14 @@ module.exports = function(RED) {
if (state === "enable") {
node.active = true;
res.sendStatus(200);
if (node.tostatus) { node.status({}); }
} else if (state === "disable") {
node.active = false;
res.sendStatus(201);
if (node.tostatus && node.hasOwnProperty("oldStatus")) {
node.oldStatus.shape = "ring";
node.status(node.oldStatus);
}
} else {
res.sendStatus(404);
}

View File

@ -439,7 +439,9 @@ RED.debug = (function() {
$('<span class="debug-message-name">'+name+'</span>').appendTo(metaRow);
}
if (format === 'Object' || /^array/.test(format) || format === 'boolean' || format === 'number' ) {
if ((format === 'number') && (payload === "NaN")) {
payload = Number.NaN;
} else if (format === 'Object' || /^array/.test(format) || format === 'boolean' || format === 'number' ) {
payload = JSON.parse(payload);
} else if (/error/i.test(format)) {
payload = JSON.parse(payload);

View File

@ -103,9 +103,13 @@
"output": "Output",
"msgprop": "message property",
"msgobj": "complete msg object",
"to": "to",
"to": "To",
"debtab": "debug tab",
"tabcon": "debug tab and console",
"toSidebar": "debug window",
"toConsole": "system console",
"toStatus": "node status (32 characters)",
"severity": "Level",
"notification": {
"activated": "Successfully activated: __label__",
"deactivated": "Successfully deactivated: __label__"

View File

@ -63,7 +63,7 @@ function start() {
// https://github.com/websockets/ws/pull/632
// that is fixed in the 1.x release of the ws module
// that we cannot currently pickup as it drops node 0.10 support
perMessageDeflate: false
//perMessageDeflate: false
});
wsServer.on('connection',function(ws) {
@ -189,15 +189,27 @@ function publish(topic,data,retain) {
}
}
var stack = [];
var ok2tx = true;
function publishTo(ws,topic,data) {
var msg = JSON.stringify({topic:topic,data:data});
if (topic && data) { stack.push({topic:topic,data:data}); }
if (ok2tx && (stack.length > 0)) {
ok2tx = false;
try {
ws.send(msg);
ws.send(JSON.stringify(stack));
} catch(err) {
removeActiveConnection(ws);
removePendingConnection(ws);
log.warn(log._("comms.error-send",{message:err.toString()}));
}
stack = [];
var txtout = setTimeout(function() {
ok2tx = true;
publishTo(ws);
}, 50); // TODO: OK so a 50mS update rate should prob not be hard-coded
}
}
function handleRemoteSubscription(ws,topic) {

View File

@ -51,10 +51,14 @@ describe('inject node', function() {
function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('topic', 't1');
msg.should.have.property('payload');
should(msg.payload).be.lessThan(timestamp.getTime());
done();
} catch(err) {
done(err);
}
});
});
});

View File

@ -25,6 +25,12 @@ describe('debug node', function() {
helper.startServer(done);
});
beforeEach(function (done) {
setTimeout(function() {
done();
}, 55);
});
afterEach(function() {
helper.unload();
});
@ -47,10 +53,10 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",name:"Debug",msg:"test",
format:"string[4]",property:"payload"}
});
}]);
}, done);
});
});
@ -63,9 +69,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string[4]"}
});
}]);
count++;
}, function() {
try {
@ -92,10 +98,10 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{\n "payload": "test"\n}',format:"Object"}
});
}]);
}, done);
});
});
@ -107,10 +113,10 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{\n "payload": "test"\n}',format:"Object"}
});
}]);
}, function() {
try {
helper.log().called.should.be.true();
@ -135,9 +141,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test", foo:"bar"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string[3]"}
});
}]);
}, done);
});
});
@ -149,9 +155,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test", foo: {bar:"bar"}});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string[3]"}
});
}]);
}, done);
});
});
@ -163,9 +169,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: new Error("oops")});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'{"name":"Error","message":"oops"}',property:"payload",format:"error"}
});
}]);
}, done);
});
});
@ -177,9 +183,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: true});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg: 'true',property:"payload",format:"boolean"}
});
}]);
}, done);
});
});
@ -191,9 +197,23 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: 7});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"7",property:"payload",format:"number"}
}]);
}, done);
});
});
it('should publish a NaN', function(done) {
var flow = [{id:"n1", type:"debug", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Number.NaN});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"NaN",property:"payload",format:"number"}
}]);
}, done);
});
});
@ -205,9 +225,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"undefined"}
});
}]);
}, done);
});
});
@ -219,9 +239,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:null});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"null"}
});
}]);
}, done);
});
});
@ -233,10 +253,10 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: {type:'foo'}});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{\n "type": "foo"\n}',property:"payload",format:"Object"}
});
}]);
}, done);
});
});
@ -248,11 +268,11 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: [0,1,2,3]});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg: '[\n 0,\n 1,\n 2,\n 3\n]',format:"array[4]",
property:"payload"}
});
}]);
}, done);
});
});
@ -266,14 +286,14 @@ describe('debug node', function() {
o.o = o;
n1.emit("input", {payload: o});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{
id:"n1",
msg:'{\n "name": "bar",\n "o": "[Circular ~]"\n}',
property:"payload",format:"Object"
}
});
}]);
}, done);
});
});
@ -285,9 +305,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: {type:'foo'}});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'{\n "type": "foo"\n}',property:"payload",format:"Object"}
});
}]);
}, function() {
try {
helper.log().called.should.be.true();
@ -312,9 +332,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload:"test\ntest"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"test\ntest",property:"payload",format:"string[9]"}
});
}]);
}, function() {
try {
helper.log().called.should.be.true();
@ -340,7 +360,7 @@ describe('debug node', function() {
n1.emit("input", {payload:Array(1002).join("X")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a.should.eql([{
topic:"debug",
data:{
id:"n1",
@ -348,7 +368,7 @@ describe('debug node', function() {
property:"payload",
format:"string[1001]"
}
});
}]);
}, done);
});
});
@ -361,7 +381,7 @@ describe('debug node', function() {
n1.emit("input", {payload: {foo: Array(1002).join("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a.should.eql([{
topic:"debug",
data:{
id:"n1",
@ -369,7 +389,7 @@ describe('debug node', function() {
property:"payload",
format:"Object"
}
});
}]);
}, done);
});
});
@ -382,7 +402,7 @@ describe('debug node', function() {
n1.emit("input", {payload: Array(1001).fill("X")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a.should.eql([{
topic:"debug",
data:{
id:"n1",
@ -395,7 +415,7 @@ describe('debug node', function() {
property:"payload",
format:"array[1001]"
}
});
}]);
}, done);
});
});
@ -408,7 +428,7 @@ describe('debug node', function() {
n1.emit("input", {payload: {foo: Array(1001).fill("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a.should.eql([{
topic:"debug",
data:{
id:"n1",
@ -423,7 +443,7 @@ describe('debug node', function() {
property:"payload",
format:"Object"
}
});
}]);
}, done);
});
});
@ -436,7 +456,7 @@ describe('debug node', function() {
n1.emit("input", {payload: Buffer(501).fill("\"")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a[0].should.eql({
topic:"debug",
data:{
id:"n1",
@ -457,7 +477,7 @@ describe('debug node', function() {
n1.emit("input", {payload: {foo: Buffer(1001).fill("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a[0].should.eql({
topic:"debug",
data:{
id:"n1",
@ -482,9 +502,9 @@ describe('debug node', function() {
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: new Buffer('HELLO', 'utf8')});
n1.emit("input", {payload: new Buffer.from('HELLO', 'utf8')});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{
id:"n1",
@ -492,7 +512,7 @@ describe('debug node', function() {
property:"payload",
format:"buffer[5]"
}
});
}]);
}, done);
});
});
@ -510,9 +530,9 @@ describe('debug node', function() {
n1.emit("input", {payload:"message 2"});
});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string[9]"}
});
}]);
}, done);
});
});
@ -577,8 +597,12 @@ function websocket_test(open_callback, message_callback, done_callback) {
var close_callback = function() { ws.close(); };
ws.on('open', function() { open_callback(close_callback); });
ws.on('message', function(msg) {
try {
message_callback(msg, close_callback);
ws.close();
done_callback();
} catch(err) {
done_callback(err);
}
});
}

View File

@ -31,6 +31,13 @@ var address = '127.0.0.1';
var listenPort = 0; // use ephemeral port
describe("api/comms", function() {
beforeEach(function (done) {
setTimeout(function() {
done();
}, 55);
});
describe("with default keepalive", function() {
var server;
var url;
@ -72,7 +79,7 @@ describe("api/comms", function() {
comms.publish('topic1', 'foo');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic1","data":"foo"}');
msg.should.equal('[{"topic":"topic1","data":"foo"}]');
ws.close();
done();
});
@ -85,7 +92,8 @@ describe("api/comms", function() {
ws.send('{"subscribe":"topic2"}');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic2","data":"bar"}');
console.log(msg);
msg.should.equal('[{"topic":"topic2","data":"bar"}]');
ws.close();
done();
});
@ -100,7 +108,8 @@ describe("api/comms", function() {
comms.publish('topic3', 'new');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic3","data":"new"}');
console.log(msg);
msg.should.equal('[{"topic":"topic3","data":"new"}]');
ws.close();
done();
});
@ -115,7 +124,8 @@ describe("api/comms", function() {
comms.publish('topic3', 'correct');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic3","data":"correct"}');
console.log(msg);
msg.should.equal('[{"topic":"topic3","data":"correct"}]');
ws.close();
done();
});
@ -133,7 +143,8 @@ describe("api/comms", function() {
comms.publish('topic4', 'bar');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic4","data":"bar"}');
console.log(msg);
msg.should.equal('[{"topic":"topic4","data":"bar"}]');
ws.close();
done();
});
@ -325,7 +336,7 @@ describe("api/comms", function() {
var ws = new WebSocket(url);
var count = 0;
ws.on('message', function(data) {
var msg = JSON.parse(data);
var msg = JSON.parse(data)[0];
msg.should.have.property('topic','hb');
msg.should.have.property('data').be.a.Number;
count++;
@ -346,7 +357,7 @@ describe("api/comms", function() {
}, 50);
});
ws.on('message', function(data) {
var msg = JSON.parse(data);
var msg = JSON.parse(data)[0];
// It is possible a heartbeat message may arrive - so ignore them
if (msg.topic != "hb") {
msg.should.have.property('topic', 'foo');
@ -435,7 +446,7 @@ describe("api/comms", function() {
ws.send('{"subscribe":"foo"}');
comms.publish('foo', 'correct');
} else {
msg.should.equal('{"topic":"foo","data":"correct"}');
msg.should.equal('[{"topic":"foo","data":"correct"}]');
ws.close();
}
});
@ -509,7 +520,7 @@ describe("api/comms", function() {
},200);
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"foo","data":"correct"}');
msg.should.equal('[{"topic":"foo","data":"correct"}]');
count++;
ws.close();
});