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!
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://home.knightnet.co.uk')
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.
index.js
/* 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) {
row++
const level = nrLogLevels[msg.level]
//console.log(topic, payload)
const dLog = document.getElementById('log')
dLog.insertAdjacentHTML(
'beforeend',
`<pre id="r${row}" class="${level}"><span>${level}| </span><span>${msg.msg}</span></pre>`
)
document.getElementById(`r${row}`).scrollIntoView()
}
// 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://home.knightnet.co.uk:8883')
// Subscribe to the correct MQTT topic
client.subscribe("nrlog/dev")
// 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
insertLogMsg(payload)
})
// Start up uibuilder
uibuilder.start()
// Listen for incoming messages from Node-RED
uibuilder.onChange('msg', function(msg){
insertLogMsg(msg.payload)
})
}
index.html
<!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">
</head><body>
<h1>uibuilder Node-RED Logger</h1>
<div id="log"></div>
<script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
<script src="./uibuilderfe.min.js"></script>
<script src="./index.js"></script>
</body></html>
index.css
#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!):