Debug node output -- show complete message options

Hi,

The default output of the debug node is to show msg.payload in the sidebar.
The first thing I usually do after dragging a debug node into the canvas is to edit it to show the complete message object. While this allows to see more than the payload, it also has the disadvantage of showing the message id, which can be distracting.

There might be a JSONata expression which does that, but it is not really convenient to configure that all the time.

Some ideas:

  • The debug node shows the message.payload and message.topic as per default now, but always has an expand button that shows the complete message. A disadvantage might be a performance impact.
  • Make the last point an option
  • Introduce an option in the debug node that shows the complete message object without the message id. This has the advantage that only relevant information is shown.

Furthermore, I really would like to have a button somewhere which turns off all debug nodes of a current flow or even all flows. This is something different than the option of just showing the output of a flow or selected nodes.

“Complete msg object” means complete, not incomplete (ie without id). Note that it is for debugging only. I don’t have any debug nodes in my flows as there is nothing to debug.

2 Likes

the point of this feature request is to have an additional option that does not show the message id, since this one is rarely needed. Debugging is an inherent part of computer science, so I do not get what you want to say with your post.

If in the majority of cases one wants to see more than just the payload, but not the message id, then I think that this should be an option, since it reflects the actual use case.
Of course you can disagree and say that the payload is the only relevant information, but this is, at least in my flows, only true for simple cases.

I think the point is that if you are showing the complete message then you presumably have a number of additional attributes so the overhead of having the id is trivial.

Show complete msg should really mean what it says...
or why just pick on _msgid...? could/should it apply to any properties starting with _ (which is a convention for private variables ? )

I totally agree that the "complete msg" should not be modified

In all the years I am using Node-RED, it just was always way more common to be interested in more than the payload only, but I was never really interested in the _msgid property so far. And if you have more than one but also not a crazy amount of properties, I think the _msgid property is actually distracting.

But I see your point about what to do with other "private" variables like the http req object or other things (are there many others at all?)

Perhaps something like this to compliment "complete msg" would be more appropriate?
:ballot_box_with_check: Exclude _msgid

Personally, it doesn't bother me too much but I must admit a persistent settable preference would be very welcome (so i dont have to open pretty much every debug node I add to the editor & set a "show complete message")

Use case: two MQTT-In nodes, each connected to a debug node then both going to a function node and a debug node connected to the output of the function node. The _msgid will identify which MQTT-in node the msg came from. Maybe the input nodes are on one tab and the final debug is on another (using link nodes)

If you want to try that out whether it is actually time-saving, you have to change
line 47 value:"false" to value:"true"
line 335 "msg" to "full"
in 21-debug.html

and add delete msg['_msgid']; before line 91 in 21-debug.js

I actually did this and will see for a while if it makes life easier :slight_smile:

This is a hack and not a solution

Hi,

I have been testing this for a while, and I think I know now what I did not like about the _msgid property, namely that it very often appears first in the debug output.

I was looking into the code and if one is willing to add something like

var keys = Object.keys(obj).sort(function(a, b) {
                if(a.startsWith('_') && !b.startsWith('_')) { return 1; }
                if(b.startsWith('_') && !a.startsWith('_')) { return -1; }
                if(a < b) { return -1; }
                if(a > b) { return 1; }
                return 0;
            });

in @node-red/editor-client/src/js/ui/utils.js in line 524, then "private" properties, i.e. especially _msgid will come last and therefore is probably not distracting at all. Furthermore, the output is sorted (also private properties will be sorted, but they come after all others).

Any reason why the properties have not been sorted before?

My guess is that it would then not represent the object structure correctly. If you would iterate the object properties, you'd get them in the order they are currently displayed.

Edit: also it likely is now as JSON.stringify would output it which is how I'd assume most familiar with JavaScript would expect it to be.

while js recently has some ordering guarantees for objects properties, one should not rely on them when iterating (if one needs that, there is map)

Agreed but I'd still side on the "natural" order in this case even though I find the extras mildly distracting also. Maybe the "private" fields could rather be printed in a dimmer colour?

1 Like

but what is the "natural" order? This totally depends on the implementation of the node where the message is coming from. If the implementation changes, then you get a different ordering (maybe).
Therefore, sorting introduces consistency.

Thanks for the discussion, by the way :slight_smile:

Without knowing anything about how the debug node/sidebar output is implemented, wouldn't it rather depend on the JS interpreter (Node.js version)? Of course the node where the message arrived could rearrange the properties but it wouldn't change anything as the debug node should represent what the message object looks like when it enters.

You're welcome! Important discussion to be had just before bedtime. :grin:

This happens in the browser, not in the node.
Since ES6 there is actually a specification saying how they should be ordered in Object.keys() or when iterating.

Look at that flow

[{"id":"846bf235.e3d378","type":"inject","z":"9768715.0af491","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":760,"y":380,"wires":[["3b34238e.72120c"]]},{"id":"a4b65b84.6ac7c8","type":"function","z":"9768715.0af491","name":"","func":"return {payload: msg.payload, hello: \"out there\"};","outputs":1,"noerr":0,"x":1090,"y":380,"wires":[["b9e67143.e21d8"]]},{"id":"b9e67143.e21d8","type":"debug","z":"9768715.0af491","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1130,"y":460,"wires":[]},{"id":"3b34238e.72120c","type":"function","z":"9768715.0af491","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":930,"y":380,"wires":[["a4b65b84.6ac7c8","b9e67143.e21d8"]]}]

This is bad flow, but still, once "_msgid" is at the top, then at the bottom.

Just image you have an object with many properties and are looking for a specific one, would it not be nice to have it alphabetically?

Could you maybe give an example when it is actually interesting to see the insertion ordering of the message property, when this depends on the (hidden and probably changing) implementation of the node? (function node aside)

Edit:
If one wants to preserve that order, something like (untested)

var keys = Object.keys(obj).sort(function(a, b) {
                if(a.startsWith('_') && !b.startsWith('_')) { return 1; }
                if(b.startsWith('_') && !a.startsWith('_')) { return -1; }
                if(a.startsWith('_') && b.startsWith('_')) { 
                    if(a < b) { return -1; }
                    if(a > b) { return 1; }
                }
                return 0;
            });

would move private properties to the end (and sort them) while leaving the other as they are

I can't test the flow now, sorry (replying from my tablet). Maybe as a workaround you could wrap the debug node inside a subflow that'd first sort/filter the fields according to your preference?

The performance of debug is bad enough without having to sort every single object before sending.

The sorting would happen in the browser.
Do you really think that within that large debug showing code a sort makes a huge difference?
Of course one would need to benchmark that to be sure.

I added a timing function around function processDebugMessage(o) in debug-utils.js
and if I have not done something wrong, I cannot see a statistically consistent difference in performance of the following flow:

[{"id":"bb214363.bd86e","type":"inject","z":"1de5d2f8.1c869d","name":"","topic":"","payload":"many","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":120,"wires":[["35d1a878.be12e"]]},{"id":"35d1a878.be12e","type":"function","z":"1de5d2f8.1c869d","name":"","func":"var obj = {};\nlet n = 1000;\nfor(let i = 0; i < n; i++) {\n    let key = '';\n    for(let j = 0; j < 10; j++) {\n  \t    key += randLetter();\n    }\n    if(i % 10 == 0) {\n  \t    key = '_' + key;\n    }\n    obj[key] = key;\n}\n\n\nfor(let i = 0; i < 100; i++) {\n    send(obj);\n}\n\n\n\n\n\nfunction randLetter() {\n    let letters = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\"];\n    return letters[Math.floor(Math.random() * letters.length)];\n}","outputs":1,"noerr":0,"x":430,"y":120,"wires":[["3be20ccd.11e37c"]]},{"id":"3be20ccd.11e37c","type":"debug","z":"1de5d2f8.1c869d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":590,"y":120,"wires":[]},{"id":"961993a2.d4648","type":"inject","z":"1de5d2f8.1c869d","name":"","topic":"","payload":"little","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":180,"wires":[["ee893043.a81098"]]},{"id":"ee893043.a81098","type":"function","z":"1de5d2f8.1c869d","name":"","func":"var obj = {};\nlet n = 10;\nfor(let i = 0; i < n; i++) {\n    let key = '';\n    for(let j = 0; j < 10; j++) {\n  \t    key += randLetter();\n    }\n    if(i % 10 == 0) {\n  \t    key = '_' + key;\n    }\n    obj[key] = key;\n}\n\n\nfor(let i = 0; i < 100; i++) {\n    send(obj);\n}\n\n\n\n\n\nfunction randLetter() {\n    let letters = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\"];\n    return letters[Math.floor(Math.random() * letters.length)];\n}","outputs":1,"noerr":0,"x":430,"y":180,"wires":[["6930a424.bc7104"]]},{"id":"6930a424.bc7104","type":"debug","z":"1de5d2f8.1c869d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":590,"y":180,"wires":[]}]