Pausing the Debug window

Please we really need to be able to pause the debug window.

Now there has been suggestions to limit the debug traffic and add in delays.
However, to truly debug code you need the debug messages to be coming in order and provide a good debugging landscape, especially when having complex flows and actions nodes etc

It is a key feature on most IDEs, so please, please add this as a feature.


might node-red-debugger be of help?

How do you see this working?

  • Would the "pause" simply ignore incoming messages (so that you can view what is already there)?
  • Would the "pause" be client side (at your browser) & other debug messages queue up in the background until you release the "pause" which would then cause the many hundreds (potentially thousands or millions) of messages fly though the debug panel?
    • perhaps a limit would be set but how would that "buffer" be managed - stop new debug messages when full? ovewrite the last one? drop the 1st to make space for latest? etc etc
  • Would you expect the "server side" to queue up messages while paused & when you release the "pause" the many hundreds (potentially thousands or millions) of messages that were queued in the server start trickling across the network to yours (and everyone elses) browser?

The issue isn't providing this kind of feature, it is scoping it and designing it sensibly.

Current alternative options...

  1. You could use a contrib node that sends debug messages to file(s) or to your syslog (then you can use a separate log viewer)?
  2. Create a custom debug plugin?
  3. contribute to the core with design and implementation?

I prefer : Would the "pause" simply ignore incoming messages (so that you can view what is already there)?

It would be perfect if the buffer size could be set right in the Editor (not in settings.js)

Why not display in your own browser window
Here is a subflow you can attach any where like a debug, it will send the msg to the browser.

You can pause the incoming by clicking Pause in title, you can also copy values and delete them individually, to clear all refresh the browser.
Open web page at http://ip_to_node-red:1880/debug

[{"id":"3decb5f518882686","type":"subflow","name":"live debug","info":"","category":"","in":[{"x":240,"y":160,"wires":[{"id":"45dbf990.ba2408"}]}],"out":[{"x":560,"y":160,"wires":[{"id":"45dbf990.ba2408","port":1}]}],"env":[{"name":"debug_name","type":"str","value":"Debug","ui":{"label":{"en-US":"Name"},"type":"input","opts":{"types":["str"]}}},{"name":"debug_property","type":"str","value":"msg","ui":{"label":{"en-US":"Property Path"},"type":"input","opts":{"types":["str"]}}},{"name":"pass_debug_property","type":"bool","value":"false","ui":{"label":{"en-US":"Pass Property Path"},"type":"input","opts":{"types":["bool"]}}}],"meta":{},"color":"#DDAA99"},{"id":"45dbf990.ba2408","type":"function","z":"3decb5f518882686","name":"format time nicely","func":"let msg1 = {payload: RED.util.cloneMessage(msg)};\nmsg1.payload.debug_property = msg.debug_property || env.get(\"debug_property\") || \"msg\"\ntry {\n  msg1.payload.debug_property_array = RED.util.normalisePropertyExpression(msg1.payload.debug_property)\n}\ncatch(error){\n  msg.payload = error;\n  msg1.payload.error = error;\n}\nmsg1.payload.debug_name = env.get(\"debug_name\") || msg._msgid || \"name error\"\nmsg1.payload.pass_debug_property = env.get(\"pass_debug_property\") || false\nreturn [msg1,msg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":120,"wires":[["50da04b3.af25fc"],[]]},{"id":"50da04b3.af25fc","type":"websocket out","z":"3decb5f518882686","name":"","server":"985ecbc7.67a138","client":"","x":680,"y":120,"wires":[]},{"id":"1787be40.e87842","type":"http in","z":"3decb5f518882686","name":"","url":"/debug","method":"get","upload":false,"swaggerDoc":"","x":330,"y":40,"wires":[["1857548e.e7a8ab"]]},{"id":"1857548e.e7a8ab","type":"template","z":"3decb5f518882686","name":"Simple Web Page","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<!DOCTYPE HTML>\n<html>\n    <head>\n        <style>\n            span.debug_name {\n                color: black;\n            }\n            span.debug_error{\n                color: red;\n            }\n            span.debug_date{\n                color:blue;\n            }\n            pre[id^='json_data_']{\n                font-size:20px;\n                background-color: ghostwhite;\n                border: 1px solid silver;\n                padding: 10px 20px;\n                margin: 20px; \n            }\n            .json-key {\n                color: olive;\n            }\n            .json-value {\n                color: navy;\n            }\n            .json-string {\n                color: brown;\n            }\n\n        </style>\n    <title>Debug Messages</title>\n    <script type=\"text/javascript\">\n        var ws;\n        var wsUri = \"ws:\";\n        var loc = window.location;\n        console.log(loc);\n        if (loc.protocol === \"https:\") { wsUri = \"wss:\"; }\n        // This needs to point to the web socket in the Node-RED flow\n        wsUri += \"//\" + + loc.pathname.replace(\"debug\",\"ws/debug\");\n\n        function wsConnect() {\n            console.log(\"connect\",wsUri);\n            ws = new WebSocket(wsUri);\n            ws.onmessage = function(msg) {\n                segments = document.getElementById('json_data').innerHTML;\n                var paused = document.getElementById('paused').innerText === \"Pause\";\n                var data;\n                var line = \"\";\n                var error = \"\";\n                var date = new Date().toISOString();\n                // parse the incoming message as a JSON object\n                try { \n                    data = JSON.parse(;\n                } catch (json_error) {\n                    data = {error: [\"json parse error\", json_error]};\n                };   \n                if(!data.error){\n                    var debug_name = data.debug_name ;\n                    var debug_property = data.debug_property_array;\n                    var original_property = data.debug_property;\n                    if(!data.pass_debug_property) delete data.debug_property;\n                    delete data.pass_debug_property;\n                    delete data.debug_property_array;\n                    delete data.debug_name;\n                    debug_property.slice(1).forEach((prop, index) => {\n                        if(data[prop]) {                    \n                            data = data[prop];\n                        }else{\n                           error = \"Property path Error on or after - \" + debug_property[index];\n                        }\n                    });\n\n                    line = `<div id=\"element${date}\">\n                    <hr/>\n                    <span class=\"debug_name\">${debug_name}</span> <span class=\"debug_date\">${date}</span> \n                    <span class=\"debug_orginal\">${original_property}</span> <span class=\"debug_error\">${error}</span> \n                     <button title=\"copy\" alt=\"copy\" onclick=\"copy_text('json_data_${date}')\"> &#10066; </button>\n                    <button title=\"delete\" alt=\"delete\" onclick=\"delete_text('element${date}')\"> &#10060; </button> \n                    <span class=\"json_data\">\n                    <pre id=\"json_data_${date}\"><code id=data>${library.json.prettyPrint(data)}</code></pre></span> \n                    </div>`;\n                }else{\n                    line = `<div id=\"element${date}\">\n                    <hr/>\n                    <span class=\"debug_error\">Error: ${JSON.stringify(data.error)}</span> <span class=\"debug_date\">${date}</span>                  \n                    <button title=\"delete\" alt=\"delete\" onclick=\"delete_text('element${date}')\"> &#10060; </button> \n                    </div>`;\n                }\n            // append line to segment\n            if(paused){\n                document.getElementById('json_data').innerHTML = line + segments;\n            }\n            }\n            ws.onopen = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"connected\";\n                //ws.send(\"Open for data\");\n                console.log(\"connected\");\n            }\n            ws.onclose = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"not connected\";\n                // in case of lost connection tries to reconnect every 3 secs\n                setTimeout(wsConnect,3000);\n            }\n        }\n        function copy_text(input) {\n            // Get the text field\n            var copyText = document.getElementById(input).innerText;\n            var dummy = document.createElement(\"textarea\");\n            document.body.appendChild(dummy);\n            dummy.value = copyText;\n  ;\n            document.execCommand(\"copy\");\n            document.body.removeChild(dummy);\n            alert(\"Copied  Data to Clipboard\\n\\n\" + copyText)\n        }\n        function delete_text(input) {\n            // Get the text field\n            document.getElementById(input).innerHTML = \"\";\n        }\n        if (!library) var library = {};\n            library.json = {\n                replacer: function(match, pIndent, pKey, pVal, pEnd) {\n                    var key = '<span class=json-key>\"';\n                    var val = '<span class=json-value>';\n                    var str = '<span class=json-string>';\n                    var r = pIndent || '';\n                    if (pKey)\n                        r = r + key + pKey.replace(/[\": ]/g, '') + '\"</span>: ';\n                    if (pVal)\n                        r = r + (pVal[0] == '\"' ? str : val) + pVal + '</span>';\n                        return r + (pEnd || '');\n                    },\n                    prettyPrint: function(obj) {\n                        var jsonLine = /^( *)(\"[\\w]+\": )?(\"[^\"]*\"|[\\w.+-]*)?([,[{])?$/mg;\n                        return JSON.stringify(obj, null, 4)\n                            .replace(/&/g, '&amp;').replace(/\\\\\"/g, '&quot;')\n                            .replace(/</g, '&lt;').replace(/>/g, '&gt;')\n                            .replace(jsonLine, library.json.replacer);\n                    }\n                    \n                };\n        function pause() {\n            var element_paused = document.getElementById(\"paused\");\n            paused = (element_paused.innerText === \"Resume\") ? \"Pause\" : \"Resume\";\n            element_paused.innerText = paused;\n        }\n    \n    </script>\n    </head>\n    <body onload=\"wsConnect();\" onunload=\"ws.disconnect();\">\n        <font face=\"Arial\">\n        <h1>Debug Messages <button id=\"paused\" value=\"pause\" onclick=\"pause()\">Pause</button> </h1>\n        <div id=\"json_data\"></div>\n        <hr/>\n        <div id=\"status\">unknown</div>\n        </font>\n    </body>\n</html>\n","x":550,"y":40,"wires":[["42a28745.bd5d78"]]},{"id":"42a28745.bd5d78","type":"http response","z":"3decb5f518882686","name":"","x":730,"y":40,"wires":[]},{"id":"985ecbc7.67a138","type":"websocket-listener","z":"65617ffeb779f51c","path":"/ws/debug","wholemsg":"false"},{"id":"a89494ddb77304ef","type":"subflow:3decb5f518882686","z":"65617ffeb779f51c","name":"","env":[{"name":"debug_name","value":"debug","type":"str"}],"x":540,"y":3740,"wires":[["22d7b380a2a7a696"]],"info":"open web page http://ip_node_red:port/debug"}]


@E1cid that is indeed a neat idea.

Once V3.1.0 is mainstream, you could probably include deep linking URL/hyperlink back to the node that transmitted the message.

I could return a clicked message back to the subflow using the WS, but i had no need to. I was going to add a jsontree library to at some point. I also need to neaten the code as i have just hacked my OG Idea to add copy and delete.

Have not tested 3.1 yet will take a look at deep linking URL also.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.