Display Node-Red Log Output using Dashboard 2

Running Node-Red on a remote computer with no display, it can be difficult to see exactly what is going on. This example allows you to see the Node-Red log output in a formatted web page.

How-To

Many people are now running Node-Red on a Pi or other Linux based computer using systemd and this enables the log information to be accessed via an application called journalctl. Whilst this is normally run at a command line (using SSH/PuTTY for example), it can also be called from within Node-Red which is what we do here.

Unfortunately, journalctl also outputs some additional runtime information, especially if you are running exec nodes. In this example, we strip out those extra lines so that we can see exactly what Node-Red is doing. You can easily amend the example to include them if you like, you could even add some formatting to them (see below).

We run journalctl with continual output, capture that output, tidy it up and present it in reverse order so that the latest information is at the top of the output page which is a lot easier to follow. We also restrict the output to 200 lines to prevent memory issues. Change that if you like.

We have to accumulate the output into a context variable since I haven't been able to find a way to append output to the dashboard.

Finally we pass the accumulated array of log entries to a Dashboard template node and use some Angular cleverness to display it.

Formatted output

As mentioned above, it would be possible to amend this example to add formatting in the function node rather than the template.

You might want to do that if you wanted different types of lines, or even different parts of a line formatted in different ways - with colour for example.

To do this, amend the function node and add HTML formatting to each line as appropriate. Each line should be wrapped in a <div> and you might want to move the current manual formatting (see the ng-style directive in the template node) to a separate <style> section to reduce the output.

Then amend the template, remove the inner <div> wrapper but don't forget to adjust the {{line}} to be {{{line}}} to make Angular realise that the content is HTML not text. If triple-brackets don't work, you will need instead to use an empty HTML element with the Angular directive ng-bind-html="line".

Other Log Types

Although this example uses journalctl to show the log, it wouldn't be difficult to amend for other log types. The easiest way would be to write the log to a file and use tail in the exec node instead of journalctl.

Other Possible Improvements

While this works, it isn't perhaps the most efficient way since it resends all the lines of output every time there is an update. It would probably be more efficient to use a template that created a websocket that you push the data to and let the browser accumulate the data. However, there doesn't appear to be an easy way to do that with Dashboard 2.0 at present.

[{"id":"e912b1f0.7a477","type":"inject","z":"106a5b82.ef95a4","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":true,"x":210,"y":4700,"wires":[["abce134a.6dac1"]]},{"id":"abce134a.6dac1","type":"exec","z":"106a5b82.ef95a4","command":"sudo journalctl -u nr-live2 -f -n 0 -o cat","addpay":false,"append":"","useSpawn":true,"timer":"","name":"","x":430,"y":4700,"wires":[["cf50d71c.ca8448"],["cf50d71c.ca8448"],[]]},{"id":"5f0f2004.2dd89","type":"ui_template","z":"106a5b82.ef95a4","group":"fa90f4d2.c24828","name":"NR-Live2","order":1,"width":"","height":"","format":"<div ng-repeat=\"line in msg.payload track by $index\">\n  <div ng-style=\"{'margin-bottom':'1em'}\">{{line}}</div>\n</div>","storeOutMessages":false,"fwdInMessages":false,"x":860,"y":4700,"wires":[[]]},{"id":"cf50d71c.ca8448","type":"function","z":"106a5b82.ef95a4","name":"Accumulate","func":"// Use a fn context variable to track\n// the output lines\nif ( msg.payload === false ) {\n    // This lets us reset the log list\n    // if we want to, just inject a false payload\n    var lines = [];\n} else {\n    var lines = context.get('jrnlLines')|| [];\n\n    // Add the new output TO THE START\n    // so that the latest output is at the top\n    // of the displayed page\n    \n    // BUT we only want to display ACTUAL\n    // NR log output not all the other stuff\n    // that the journal outputs. Actual NR log\n    // output starts with a date (numeric)\n    if ( isNaN(parseInt(msg.payload.substr(0,1))) ) {\n        // Not numeric at start so not NR output\n        // ignore\n    } else {\n        lines.unshift(msg.payload);\n    }\n    \n    // # lines could get VERY large so cause memory\n    // issues, limit to 200\n    if ( lines.length > 200 ) {\n        // too big so drop the last element\n        lines.pop();\n    }\n}\n\n// save the total output in the context var\ncontext.set('jrnlLines',lines);\n\n// output all the lines to the UI template\nmsg.payload = lines;\n\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":4700,"wires":[["5f0f2004.2dd89","fdc0ffcf.10a9d"]]},{"id":"fdc0ffcf.10a9d","type":"debug","z":"106a5b82.ef95a4","name":"","active":false,"console":"false","complete":"false","x":871,"y":4741,"wires":[]},{"id":"19461023.4f2c1","type":"inject","z":"106a5b82.ef95a4","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"x":531,"y":4745,"wires":[["cf50d71c.ca8448"]]},{"id":"a236dcf6.7dfde","type":"comment","z":"106a5b82.ef95a4","name":"Output Node-Red log to Dashboard (UI)","info":"See http://flows.nodered.org/flow/6dc1a137075ad93cd7c00224681afeda","x":538,"y":4654,"wires":[]},{"id":"fa90f4d2.c24828","type":"ui_group","z":"","name":"Log Live2","tab":"e5248672.10b838","order":2,"disp":true,"width":"12"},{"id":"e5248672.10b838","type":"ui_tab","z":"106a5b82.ef95a4","name":"Pi NR Log","icon":"dashboard","order":4}]
TotallyInformation

Flow Info

created 1 year, 1 month ago

Node Types

Core
  • comment (x1)
  • debug (x1)
  • exec (x1)
  • function (x1)
  • inject (x2)
Other
  • ui_group (x1)
  • ui_tab (x1)
  • ui_template (x1)

Tags

  • log
  • logging
  • UI
  • Dashboard
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option