Node-RED custom logger to a dynamic web page

I responded to a thread the other day where Nick reminded me that Node-RED has a custom logger capability and I realised that I'd not made use of it at all - how rude of me! :crazy_face:

So for some light relief, I thought I'd have a go. Now, because I'm doing some heavy development (read pulling apart and putting back together in a more sensible way!) of uibuilder, I often want to see the trace-level logs for uibuilder but not for everything else (that's a LOT of lines).

The custom logger is great for this.

The other thing I wanted was a more colourful and easier to read output than in a file or the console. So I've revisited something I did way back in the earlier days of Node-RED - create a web-based logger (you can probably find my old attempt in the flows site). And, of course, it is nice to throw in some MQTT - so I did!

So here is a quick and fairly dirty creation of a web-based logger that uses an MQTT broker as the message queue. Of course, since the data is going to MQTT, you could use a completely different interface if you wanted to and even this example allows you to use a flow to output the log entries as well as a direct connection from the web page to MQTT. So you can use it either way.

Note a slight limitation however, this approach doesn't work so well if Node-RED itself is taking part in the output of the log since you will likely miss the early log output. I've used my live instance of node-red to host the web-page but I want the log output from my dev environment so this isn't a problem for me. However, you could easily do away with Node-RED for the web page, either use a stand-alone node.js/Python web server or NGINX/Caddy/Apache.

You need to install an mqtt client into your userDir: cd ~/.node-red && npm install mqtt.

This code goes in the logging section of your settings.js:

        mqttLog: {
            level: 'trace',
            metrics: false,
            audit: false,
            handler: function(settings) {
                const nrLogLevels = {
                    10: 'FATAL', 20: 'ERROR', 30: 'WARN ', 40: 'INFO ', 50: 'DEBUG', 60: 'TRACE', 98: 'AUDIT', 99: 'MTRIC'

                const mqtt = require('mqtt')
                const client  = mqtt.connect('mqtt://')

                return function(msg) {
                    if ( msg.level < 51 || msg.msg.includes('[uibuilder') || msg.msg.startsWith('+-') || msg.msg.startsWith('| ') || msg.msg.startsWith('>>') ) {
                        client.publish( 'nrlog/dev', JSON.stringify(msg) )

For the web front-end, I just added a uibuilder node with no inputs or outputs. This is rather overkill of course and you could easily use just a pair of http-in/out nodes with a template for the code.

Note that direct DOM manipulation is used - no framework needed. I started with uibuilder's basic, blank template.


/* jshint browser: true, esversion: 6, asi: true */
/* globals uibuilder */
// @ts-nocheck

const nrLogLevels = {
    10: 'FATAL', 20: 'ERROR', 30: 'WARN ', 40: 'INFO ', 50: 'DEBUG', 60: 'TRACE', 98: 'AUDIT', 99: 'MTRIC'

// Row tracker
let row = 1

function insertLogMsg(msg) {
    const level = nrLogLevels[msg.level]
    //console.log(topic, payload)

    const dLog = document.getElementById('log')
        `<pre id="r${row}" class="${level}"><span>${level}| </span><span>${msg.msg}</span></pre>`

// run this function when the document is loaded
window.onload = function() {
    // Connect to MQTT
    // Note the use of ws over TLS - you need to configure your broker for this or use non-secure connection
    const client = mqtt.connect('wss://')
    // Subscribe to the correct MQTT topic

    // Listen to messages direct from MQTT broker
    client.on("message", function (topic, payload) {
        // MQTT messages can only be strings so decode that here
        payload = JSON.parse(payload)
        // Insert to the DOM UI
    // Start up uibuilder

    // Listen for incoming messages from Node-RED
    uibuilder.onChange('msg', function(msg){


<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Node-RED UI Builder - Blank template</title>
    <meta name="description" content="Node-RED UI Builder - Blank template">
    <link rel="icon" href="./images/node-blue.ico">

    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

    <h1>uibuilder Node-RED Logger</h1>

    <div id="log"></div>

    <script src="../uibuilder/vendor/"></script>
    <script src=""></script>
    <script src="./uibuilderfe.min.js"></script>
    <script src="./index.js"></script>



#log {color:white;background-color:black;}
pre { margin:0;}

/* Log level colours */
.FATAL {color: red;}
.ERROR {color: red;}
.WARN  {color: orange;}
.INFO  {color: yellow;}
.DEBUG {color: green;}
.TRACE {color: cyan;}
.AUDIT {color: grey;}
.MTRIC {color: grey;}

The output looks like this (yes, I know the colours aren't great - it is just something quick and dirty after all!):