Live Debug
Send msg or msg property to a separate web page. You can set the property to send, delete any message, pause the message stream and copy any message. The webpage url is http://your_node-red_ip:port/debug, e.g. http://192.168.1.50:1880/debug. None of the messages are truncated.
Usage: Place the subflow as you would a debug node, or inline. Set the msg property you want to send, default is msg.
[{"id":"3decb5f518882686","type":"subflow","name":"live debug","info":"<A href=\"http://localhost:1880/debug\">localhost/debug</a>","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}')\"> ❒ </button>\n <button title=\"delete\" alt=\"delete\" onclick=\"delete_text('element${date}')\"> ❌ </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}')\"> ❌ </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, '&').replace(/\\\\\"/g, '"')\n .replace(/</g, '<').replace(/>/g, '>')\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":"3f4abb584169f0cc","type":"subflow:3decb5f518882686","z":"b9860b4b9de8c8da","name":"","x":160,"y":1980,"wires":[[]]}]