How to pause the debug log?

While debugging, I have a large array being processed that pumps a lot of messages into the debug log. How can I pause the debug log so that I can read what's currently there? Currently, it scrolls for 10s, pauses for 3s, then the next set of messages comes in, and I can't read any of them.

Are you using the debug node?
You can click the button to the right of it, to stop it producing debug entries

if using node.debug(..) im not entirely sure you can?

But I've got around 20 active debug nodes - is pausing the whole debug log view still in feature requests, from what I've seen here? I would need to disable each individually and do a slow redeploy for the log to stop moving that way; wish there was a way to freeze the current log and discard any new messages. Edit: And that doesn't seem to work, as the log clears itself after around 10s.

I wrote a subflow to help with this, it sends data to a nod-red endpoint which can be accessed at http://ip:port/debug.
You can pause the input and delete unwanted messages, you can also copy messages.

It may help you.

[{"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                white-space: pre-wrap;\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.host + 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(msg.data);\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            dummy.select();\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":"3acabffc83517f90","type":"subflow:3decb5f518882686","z":"b9860b4b9de8c8da","name":"","x":140,"y":960,"wires":[[]]}]

Just add the subflow where you would put a debug node.

2 Likes

Thank you for that; I tried the code from your other post and this one, but keep getting TypeError: Cannot read properties of undefined (reading 'length') on importing the code; not sure what I'm doing wrong...

Are you importing correctly
How to import a flow

I believe so; Screen recording of failed import. Node-red v3.0.2 running off small Linux server

please use forum upload feature to post images, as i do not use drive or google.
The forum provides an easy to use and see feature, this means the image will not vanish when your drive is possibly closed down in the future.

Error occurs when opened tab is subflow and the imported thing is also subflow.

Seems a bug but if not, may be needs more clear error description..

Edit: The error seems to happen only for first attempt (or something similar). Trying after importing the subflow to regular tab, it works seamlessly no matter the active tab.

1 Like

Screen recording is MP4 (unfortunately not supported by forum); even compressing to GIF renders it too large to post. However, @hotNipi's solution seems to work: subflow must be imported into a non-subflow tab.

Thank you, both, for this workaround! Would be nice if this was built into node-red by default.

Perhaps you should open an issue on GitHub ?

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