From 95a51aafdcabe75b26d0108172adbc815e793a7e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 17 Jan 2020 16:53:01 +0000 Subject: [PATCH] Add path property to debug messages Fixes #2358 This property can be used to identify the full path to the node that logged a given message. If the node is inside a subflow (and maybe nested many levels deep), this path can be used to help find the node, rather than just the top-level subflow instance node. A side-effect of this change is the Debug sidebar is now able to show the message tools for a message coming from a deeply nested subflow --- .../@node-red/nodes/core/common/21-debug.html | 69 +++++++++++++++++-- .../@node-red/nodes/core/common/21-debug.js | 6 +- .../core/common/lib/debug/debug-utils.js | 11 ++- .../nodes/core/common/lib/debug/debug.js | 4 +- .../@node-red/runtime/lib/nodes/Node.js | 5 +- .../@node-red/runtime/lib/nodes/flows/Flow.js | 5 +- .../runtime/lib/nodes/flows/Subflow.js | 5 +- 7 files changed, 89 insertions(+), 16 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.html b/packages/node_modules/@node-red/nodes/core/common/21-debug.html index ebfb8532b..5e2f3ba49 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.html +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.html @@ -131,8 +131,32 @@ RED.view.redraw(); } }, - messageSourceClick: function(sourceId) { - RED.view.reveal(sourceId); + messageSourceClick: function(sourceId, aliasId, path) { + // Get all of the nodes that could have logged this message + var candidateNodes = [RED.nodes.node(sourceId)] + if (path) { + for (var i=2;i 1) { + // The node is in a subflow. Check to see if the active + // workspace is a subflow in the node's parentage. If + // so, reveal the relevant subflow instance node. + var ws = RED.workspaces.active(); + for (var i=0;i",o.id,o.z,o._alias); + // + // sourceNode should be the top-level node - one that is on a flow. + var sourceNode; + var pathParts; + if (o.path) { + // Path is a `/`-separated list of ids that identifies the + // complete parentage of the node that generated this message. + // flow-id/subflow-A-instance/subflow-A-type/subflow-B-instance/subflow-B-type/node-id + + // If it has one id, that is a top level flow + // each subsequent id is the instance id of a subflow node + // + pathParts = o.path.split("/"); + if (pathParts.length === 1) { + // The source node is on a flow - so can use its id to find + sourceNode = RED.nodes.node(o.id); + } else if (pathParts.length > 1) { + // Highlight the subflow instance node. + sourceNode = RED.nodes.node(pathParts[1]); + } + } else { + // This is probably redundant... + sourceNode = RED.nodes.node(o.id) || RED.nodes.node(o.z); + } if (sourceNode) { - o._source = {id:sourceNode.id,z:sourceNode.z,name:sourceNode.name,type:sourceNode.type,_alias:o._alias}; + o._source = { + id:sourceNode.id, + z:sourceNode.z, + name:sourceNode.name, + type:sourceNode.type, + // _alias identifies the actual logging node. This is + // not necessarily the same as sourceNode, which will be + // the top-level subflow instance node. + // This means the node's name is displayed in the sidebar. + _alias:o._alias, + path: pathParts + }; } RED.debug.handleDebugMessage(o); if (subWindow) { @@ -235,7 +294,7 @@ } else if (msg.event === "mouseLeave") { options.messageMouseLeave(msg.id); } else if (msg.event === "mouseClick") { - options.messageSourceClick(msg.id); + options.messageSourceClick(msg.id,msg._alias,msg.path); } else if (msg.event === "clear") { options.clear(); } diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js index 8477ed525..b00371bbd 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.js @@ -62,7 +62,7 @@ module.exports = function(RED) { if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); } else { - done(null,{id:node.id, z:node.z, name:node.name, topic:msg.topic, msg:value, _path:msg._path}); + done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); } }); } else { @@ -77,7 +77,7 @@ module.exports = function(RED) { output = undefined; } } - done(null,{id:node.id, z:node.z, name:node.name, topic:msg.topic, property:property, msg:output, _path:msg._path}); + done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, property:property, msg:output}); } } @@ -88,7 +88,7 @@ module.exports = function(RED) { node.log("\n"+util.inspect(msg, {colors:useColors, depth:10})); } if (this.active && this.tosidebar) { - sendDebug({id:node.id, z:node.z, name:node.name, topic:msg.topic, msg:msg, _path:msg._path}); + sendDebug({id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:msg}); } done(); } else { diff --git a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js index db1a85510..ddab5bbe2 100644 --- a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js +++ b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js @@ -406,10 +406,16 @@ RED.debug = (function() { msg.on("mouseenter", function() { msg.addClass('red-ui-debug-msg-hover'); if (o._source) { + // highlight the top-level node (could be subflow instance) config.messageMouseEnter(o._source.id); if (o._source._alias) { + // this is inside a subflow - highlight the node itself config.messageMouseEnter(o._source._alias); } + // if path.length > 2, we are nested - highlight subflow instances + for (var i=2;i'+name+'').appendTo(metaRow); diff --git a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug.js b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug.js index 97f6cc8a7..3cca62e01 100644 --- a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug.js +++ b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug.js @@ -7,8 +7,8 @@ $(function() { messageMouseLeave: function(sourceId) { window.opener.postMessage({event:"mouseLeave",id:sourceId},'*'); }, - messageSourceClick: function(sourceId) { - window.opener.postMessage({event:"mouseClick",id:sourceId},'*'); + messageSourceClick: function(sourceId, aliasId, path) { + window.opener.postMessage({event:"mouseClick",id:sourceId, _alias: aliasId, path: path},'*'); }, clear: function() { window.opener.postMessage({event:"clear"},'*'); diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/Node.js b/packages/node_modules/@node-red/runtime/lib/nodes/Node.js index d35ced395..c62895f7f 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/Node.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/Node.js @@ -52,7 +52,7 @@ function Node(n) { // the object (such as dashboard) will not like circular refs // The value must still be writable in the case that a node does: // Object.assign(this,config) - // as part of its constructure - config._flow will overwrite this._flow + // as part of its constructor - config._flow will overwrite this._flow // which we can tolerate as they are the same object. Object.defineProperty(this,'_flow', {value: n._flow, enumerable: false, writable: true }) this._asyncDelivery = n._flow.asyncMessageDelivery; @@ -462,6 +462,9 @@ function log_helper(self, level, msg) { if (self._alias) { o._alias = self._alias; } + if (self._flow) { + o.path = self._flow.path; + } if (self.z) { o.z = self.z; } diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js index 40855793f..db15e8eb5 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js @@ -53,6 +53,7 @@ class Flow { this.subflowInstanceNodes = {}; this.catchNodes = []; this.statusNodes = []; + this.path = this.id; } /** @@ -120,7 +121,7 @@ class Flow { * @return {[type]} [description] */ start(diff) { - this.trace("start "+this.TYPE); + this.trace("start "+this.TYPE+" ["+this.path+"]"); var node; var newNode; var id; @@ -234,7 +235,7 @@ class Flow { for (id in this.activeNodes) { if (this.activeNodes.hasOwnProperty(id)) { node = this.activeNodes[id]; - this.trace(" "+id.padEnd(16)+" | "+node.type.padEnd(12)+" | "+(node._alias||"")); + this.trace(" "+id.padEnd(16)+" | "+node.type.padEnd(12)+" | "+(node._alias||"")+(node._zAlias?" [zAlias:"+node._zAlias+"]":"")); if (node.type === "catch") { this.catchNodes.push(node); } else if (node.type === "status") { diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js index 6221ff318..a7bec1234 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js @@ -88,7 +88,7 @@ class Subflow extends Flow { * @param {[type]} subflowInstance [description] */ constructor(parent,globalFlow,subflowDef,subflowInstance) { - // console.log("CREATE SUBFLOW",subflowDef.id,subflowInstance.id); + // console.log("CREATE SUBFLOW",subflowDef.id,subflowInstance.id,"alias?",subflowInstance._alias); // console.log("SubflowInstance\n"+JSON.stringify(subflowInstance," ",2)); // console.log("SubflowDef\n"+JSON.stringify(subflowDef," ",2)); var subflows = parent.flow.subflows; @@ -140,6 +140,7 @@ class Subflow extends Flow { this.subflowDef = subflowDef; this.subflowInstance = subflowInstance; this.node_map = node_map; + this.path = parent.path+"/"+(subflowInstance._alias||subflowInstance.id); var env = []; if (this.subflowDef.env) { @@ -451,7 +452,7 @@ class Subflow extends Flow { function createNodeInSubflow(subflowInstanceId, def) { let node = clone(def); let nid = redUtil.generateId(); - // console.log("Create Node In subflow",node.id, "--->",nid, "(",node.type,")") + // console.log("Create Node In subflow",node._alias, "--->",nid, "(",node.type,")") // node_map[node.id] = node; node._alias = node.id; node.id = nid;