Node-red-context-monitor: A node to monitor a Node-RED context

Recently we had an exchange on the options to emit a msg from a node that had no output terminals ... and of course wasn't designed to support such a use case.
This node yet facilitated a function like interface; send() was inoperational - but setting context was possible.

What if - there was a node capable to monitor a(ny) context and create a msg in case the context is written to?

Welcome node-red-context-monitor - a node designed to monitor a Node-RED context! :tada:

image

This node allows to setup the reference to a context, then sends a message when this context is written to.

It sends a dedicated message on a separate port in case it detects that the value of the context was changed.

The message sent will carry the current value of the context as msg.payload. Monitoring details will be provided as msg.monitoring.

It is possible to monitor an infinite number of contexts with each instance of this node.

This node supports the three context scope levels Global, Flow & Node.

Feedback welcome - as always!

8 Likes

Hi @ralphwetzel - Nice!

Can it be password protected?
I don't really want my Wife monitoring my Context?

Note: I moved this into Share Your Nodes (hope that's alright)

Not getting anything when injecting into function node that is setting context


image

[edit] Ignore
I've seen my error :slight_smile:

Hi @ralphwetzel,

Nice!!!

I am just wondering if this works in all cases. Because for some reason I have always thought this feature wasn't possible to implement in Node-RED, but I can't find the details quickly... Here is one of the related posts, but it doesn't contain much info. Or was there a problem with some types of context stores like e.g. an in-memory store. Don't remember it anymore. Getting old ...

Bart

Great! I've been wanting to do something like this for a few years, just never had the time. Though I was thinking of it in terms of a new context type that had events built in. But this should work. Definitely going to give this a go, thanks for contributing it.

1 Like

After install and adding to a flow:

image

0|Node-RED  | *** Error while loading node-red-context-monitor:
0|Node-RED  | Failed to calculate correct patch path.
0|Node-RED  | Please raise an issue @ our GitHub repository, stating the following information:
0|Node-RED  | > require.main.path: D:\src\nr\node_modules\node-red
0|Node-RED  | > utils.js: D:\src\nr\node_modules\node-red\node_modules\@node-red\runtime\lib\nodes\context\index.js
0|Node-RED  | 2 Nov 16:50:16 - [info] Added node types:
0|Node-RED  | 2 Nov 16:50:16 - [info]  - @ralphwetzel/node-red-context-monitor:context-monitor
0|Node-RED  | 2 Nov 16:51:30 - [info] Stopping modified nodes
0|Node-RED  | 2 Nov 16:51:31 - [info] [mqtt-broker:home] Disconnected from broker: mqtt://home.knightnet.co.uk:1883
0|Node-RED  | 2 Nov 16:51:31 - [info] Stopped flows
0|Node-RED  | 2 Nov 16:51:31 - [info] Updated flows
0|Node-RED  | 2 Nov 16:51:31 - [info] Waiting for missing types to be registered:
0|Node-RED  | 2 Nov 16:51:31 - [info]  - context-monitor

Also, not specifying a thing to monitor does not mark the node as being in error and maybe it needs to?

Looks like an issue finding the correct file for patching in your directory tree. I'll check.
Thanks for giving it a try!

It is slowly getting back...

I mostly store objects in memory, instead of primitives. Then I get (a reference) to that object in memory, and I update the object (via that reference). In this case the API does not know that something has been changed, because I don't use set.

This simple flow for your node explains what I mean:

image

[{"id":"f2874060375785b7","type":"inject","z":"4980f94dd2c07b62","name":"Set","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"set","x":130,"y":80,"wires":[["b1d17d7418f7a28f"]]},{"id":"b1d17d7418f7a28f","type":"function","z":"4980f94dd2c07b62","name":"set or update object on node context","func":"if (msg.topic == \"set\") {\n    context.set(\"node_test\", {\n        someProperty: 25\n    });\n}\n\nif (msg.topic == \"change\") {\n    let context_reference = context.get(\"node_test\");\n    // Update the context via the reference:\n    context_reference.someProperty = 99;\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":80,"wires":[[]]},{"id":"42634cda458394e4","type":"context-monitor","z":"4980f94dd2c07b62","name":"","monitoring":[{"scope":"node","flow":"4980f94dd2c07b62","node":"b1d17d7418f7a28f","key":"node_test"},{"scope":"global","key":"global_test"},{"scope":"flow","flow":"4980f94dd2c07b62","node":"f2874060375785b7","key":"flow_test"}],"x":140,"y":200,"wires":[[],["12a15905dd250321"]]},{"id":"12a15905dd250321","type":"debug","z":"4980f94dd2c07b62","name":"debug on change","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":370,"y":200,"wires":[]},{"id":"46c751417be5d30d","type":"inject","z":"4980f94dd2c07b62","name":"Change","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"change","x":130,"y":120,"wires":[["b1d17d7418f7a28f"]]}]

The first Inject node automatically stores an object in the Function node's context memory (with property value 25). When I press the second Inject node, I get the object from memory and I update its property value to 99. That new value indeed arrives in my memory context store:

image

In this case your node doesn't send an output message, because nobody is aware that I changed the object in my memory...

[EDIT 1] This is of course only an issue when you use an in-memory context store. When you store e.g. your context in a db, then you need to call set to update the object in the db.

[EDIT 2] Not sure whether Object.observe() or Object.prototype.watch() could somehow be a workaround for those cases, to keep an eye on the object changing...

It is not my intention at all to be a partystopper, because you created a real useful node! But it might be useful to mention this somewhere in the readme page.

Keep up the good work!!
Bart

You've also got a node.on("input", function(msg, send, done) { ... } but you don't actually have an input so that will never be called.

I was wondering how you were managing to achieve this - // patching into - ah, monkey patching. I wonder then, if this is a useful node, perhaps it should be adopted into core so that you didn't need to patch those core functions?

Nope. :wink:
Of course this will be called:

You are discovering why you should always be using get/set Bart. :slight_smile:

Don't need it :wink:
And I assume lots of others will use it also that way...

What am I missing? That seems to be calling a receive function in a referenced node, not in this node?

Ah, yes, you've forgotten that node.js apps can be installed in multiple ways. In my case, Node-RED is installed locally not globally and the userDir is a sub-folder of the node-red install folder. But of course, it could be somewhere else entirely.

Until you do. I've been caught out like that before, far too many times to count. So I'm rather more conservative these days. Because going back and re-coding - even finding - all those references is a pain in the rear-end.

I've no doubt.

Yes I know you are right: if you want to be able to switch easily (without having to change anything) afterwards to another context store, you should call the set function always.

But you know that - with a development background - you will never do that when you have a reference to your object available...

The localisation algorithm usually is quite robust - but still an algorithm.
Could you do me a favour and locate the absolute path of node_modules\@node-red\runtime\lib\nodes\context\index.js in your directory tree?

Thanks a lot for your feedback! That can't ever be a partystopper! :grinning:
Think I'll follow your proposal & amend the documentation accordingly.

1 Like

I manually hacked the node to get it to work:

const context_manager_path = path.join(__dirname, '../../../../node_modules/@node-red/runtime/lib/nodes/context/index.js')

I've reworked the localisation algorithm.
Could please check if the latest commit (@GitHub) is able to spot the right path for your system?

Hi @BartButenaers

The node should now be able to monitor referenced object changes as well - even nested.
Could you please verify? You need to install the latest commit from Github.
Disclaimer: Currently this should work only for objects. Array support is next ToDo on the list...