A Node-RED node to monitor the resources used by the OS processes. The node supports all processes, but can also be limited to Node-RED related processes.
Run the following npm command in your Node-RED user directory (typically ~/.node-red):
npm install node-red-contrib-process-resources
Please buy my wife a coffee to keep her happy, while I am busy developing Node-RED stuff for you ...
While my node-red-contrib-cpu node measures the CPU usage per core on the server, this node calculates the CPU/memory usage of the Node-RED process and all of its child processes. Both nodes have their own purpose for performance monitoring.
See this wiki page for an introduction of processes and threads. Note that there is also another wiki page with information about how to easily test load generated by child processes and worker threads._
When the system resources (memory, cpu...) of the host server are exhausted, the main thread will also be slowed down. Which means that Node-RED will become unresponsive. In that case you need to start a performance analysis, to determine which process is consuming most of the system resources.
This node has been developed in order to assist you a bit with such a performance analysis:
- It determines which child processes the main (Node-RED) process has.
- It determines the cpu and memory usage of both the main process and all its child processes.
Currently I haven't found a way yet to determine the CPU usage per thread. As a result you cannot see how much CPU each worker thread is consuming. You can only see how much CPU the entire process consumes, i.e. the sum of the CPU usage of all its threads.
Simply inject a (random) input message, and as a result an output message will be send. The output message looks like this, in case of one single child process:
- The 'main' object contains information about the main process. The content of this object depends on the specified process type:
- For 'Node-RED process" this contains information about the Node-RED process.
- For 'Single process" this contains information about the process whose PID is specified in the input message.
- For "All processes" this will be empty, since there is no main process specified. All processes are listed under the 'chidren' field...
- The 'children' section contains the information about every child process.
- The 'cpuChildren' contains the total CPU usage for all child processes together.
- The 'memoryChildren' contains the total memory usage for all child processes together.
- The 'cpuTotal' contains the total CPU usage for all processes together (i.e. both the main process and all of its child processes).
- The 'memoryTotal' contains the total memory usage for all processes together (i.e. both the main process and all of its child processes).
- The 'processCount' contains the total number of processes (i.e. both the main process and all of its child processes).
For every process (both 'main' and 'children' fields) the following information will be send in the output message:
- 'cpu': The CPU usage percentage, which lies within the range from 0 to 100 * the number of cores.
- 'memory': The memory usage in bytes.
- 'pid': The (unique) process identifier number.
- 'ppid': The parent process identifier number, i.e. the PID of the parent process.
- 'ctime': The CPU time used by a process, which is the sum of:
- 'User CPU time': The time spent executing user programs.
- 'System CPU time': The time spent by the kernel executing system calls, on behalf of the process.
- 'elapsed': The duration (in milliseconds) from when the process was started, as a "real wall clock" time.
- 'timestamp': The epoch timestamp when the process was started, expressed in milliseconds since January 1st 1970.
Line charts are a useful way to show the history of these metrics, which allow you to easily spot (interruptions of) trends.
The following flow fills 3 separate line charts with information (cpu, memory and process count) about the Node-RED processes:
[{"id":"cdf66cfb22d2793a","type":"ui_chart","z":"f3e346780eaa6c3c","name":"CPU - Line chart","group":"89749fb7.87f01","order":3,"width":"7","height":"6","label":"CPU per process - Line chart","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"100","removeOlderUnit":"3600","cutout":"30","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":870,"y":1140,"wires":[[]]},{"id":"1f27e7b00e21d17d","type":"change","z":"f3e346780eaa6c3c","name":"CPU main","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.main.cpu","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"CPU main","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":1100,"wires":[["cdf66cfb22d2793a"]]},{"id":"62ec78dcc422890b","type":"change","z":"f3e346780eaa6c3c","name":"CPU children","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.cpuChildren","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"CPU children","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":1140,"wires":[["cdf66cfb22d2793a"]]},{"id":"0ab7460febb7a54d","type":"change","z":"f3e346780eaa6c3c","name":"CPU total","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.cpuTotal","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"CPU total","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":1180,"wires":[["cdf66cfb22d2793a"]]},{"id":"ee1d323d938b76a4","type":"ui_chart","z":"f3e346780eaa6c3c","name":"Memory -Line chart","group":"89749fb7.87f01","order":3,"width":"7","height":"6","label":"Memory per process - Line chart","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"100","removeOlderUnit":"3600","cutout":"30","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":910,"y":1280,"wires":[[]]},{"id":"34c28f6c52347fa5","type":"change","z":"f3e346780eaa6c3c","name":"Memory main","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.main.memory","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"Memory main","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":1240,"wires":[["ee1d323d938b76a4"]]},{"id":"e79a7633f4eaeb91","type":"change","z":"f3e346780eaa6c3c","name":"Memory children","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.memoryChildren","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"Memory children","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":1280,"wires":[["ee1d323d938b76a4"]]},{"id":"d8a3dc8a6d5d631c","type":"change","z":"f3e346780eaa6c3c","name":"Memory total","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.memoryTotal","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"Memory total","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":1320,"wires":[["ee1d323d938b76a4"]]},{"id":"d4876cedf749a00b","type":"change","z":"f3e346780eaa6c3c","name":"Process count","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.processCount","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"Process count","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":1380,"wires":[["312a72d7e7486377"]]},{"id":"312a72d7e7486377","type":"ui_chart","z":"f3e346780eaa6c3c","name":"Process count - Line chart","group":"89749fb7.87f01","order":3,"width":"7","height":"6","label":"Process count - Line chart","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"100","removeOlderUnit":"3600","cutout":"30","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":890,"y":1380,"wires":[[]]},{"id":"b92483b542b2df4f","type":"inject","z":"f3e346780eaa6c3c","name":"Every 3 seconds","props":[{"p":"payload"}],"repeat":"3","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":230,"y":1100,"wires":[["86e589dc497a4da1"]]},{"id":"86e589dc497a4da1","type":"process-resources","z":"f3e346780eaa6c3c","name":"","process":"nodered","pidField":"payload","outputField":"payload","sortOrder":"asc","sortBy":"pid","analyzeChildren":true,"x":440,"y":1100,"wires":[["1f27e7b00e21d17d","62ec78dcc422890b","0ab7460febb7a54d","34c28f6c52347fa5","d4876cedf749a00b","d8a3dc8a6d5d631c","e79a7633f4eaeb91"]]},{"id":"89749fb7.87f01","type":"ui_group","name":"Message Profiler","tab":"d7901f40.2659d","order":2,"disp":true,"width":"16","collapse":false,"className":""},{"id":"d7901f40.2659d","type":"ui_tab","name":"Charts","icon":"dashboard","order":40,"disabled":false,"hidden":false}]
Which will result in something like this:
Note that there I have used 1 single line in the chart for all the child processes (which represents the sum of the CPU/memory usage of all processes), instead of a separate line per child process. The reason is that child processes can be spawned and aborted continuously, which would result in lines being stopped and started all over the place. That would look very messy. However in some cases it might be usefull to show them as separate lines, to determine easily which process (pid) is consuming the most CPU...
Pie charts are convenient to show the distribution of the CPU/memory usage across the individual processes.
[{"id":"4ce94890e439c5d3","type":"inject","z":"f3e346780eaa6c3c","name":"Every 3 seconds","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":290,"y":1120,"wires":[["eb58acd941571073"]]},{"id":"eb58acd941571073","type":"process-resources","z":"f3e346780eaa6c3c","name":"","process":"nodered","pidField":"payload","outputField":"payload","sortOrder":"asc","sortBy":"pid","analyzeChildren":true,"x":500,"y":1120,"wires":[["b46e1d8d4c5fb298","d5a9b1db1b664409"]]},{"id":"d5a9b1db1b664409","type":"function","z":"f3e346780eaa6c3c","name":"Compose cpu chart data","func":"var chartInput = {\n \"series\": [\"X\"],\n \"data\": [[]],\n \"labels\": []\n};\n\n// Avoid that the pie disappears (for main cpu value 0)\nif (msg.payload.main.cpu === 0) msg.payload.main.cpu = 0.1;\n\n// Add the cpu of the main process first\nchartInput.data[0].push(msg.payload.main.cpu);\nchartInput.labels.push(msg.payload.main.pid + \" (main)\");\n\n// Add the cpu of all child processes\nmsg.payload.children.forEach(function(child) {\n // Avoid that the pie disappears (for child cpu value 0)\n if (child.cpu === 0) child.cpu = 0.1;\n chartInput.data[0].push(child.cpu);\n chartInput.labels.push(child.pid + \" (child)\");\n});\n\nreturn { payload:[chartInput] };","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":1120,"wires":[["3d1bdc283ba51230"]]},{"id":"3d1bdc283ba51230","type":"ui_chart","z":"f3e346780eaa6c3c","name":"CPU per process id - Pie chart","group":"89749fb7.87f01","order":3,"width":"7","height":"6","label":"CPU per process id - Pie chart","chartType":"pie","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":"30","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1050,"y":1120,"wires":[[]]},{"id":"b46e1d8d4c5fb298","type":"function","z":"f3e346780eaa6c3c","name":"Compose memory chart data","func":"var chartInput = {\n \"series\": [\"X\"],\n \"data\": [[]],\n \"labels\": []\n};\n\n// Avoid that the pie disappears (for main memory value 0)\nif (msg.payload.main.memory === 0) msg.payload.main.memory = 0.1;\n\n// Add the memory of the main process first\nchartInput.data[0].push(msg.payload.main.memory);\nchartInput.labels.push(msg.payload.main.pid + \" (main)\");\n\n// Add the memory of all child processes\nmsg.payload.children.forEach(function(child) {\n // Avoid that the pie disappears (for child memory value 0)\n if (child.memory === 0) child.memory = 0.1;\n chartInput.data[0].push(child.memory);\n chartInput.labels.push(child.pid + \" (child)\");\n});\n\nreturn { payload:[chartInput] };","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":1160,"wires":[["dc248c1feb3c58a2"]]},{"id":"dc248c1feb3c58a2","type":"ui_chart","z":"f3e346780eaa6c3c","name":"Memory per process id - Pie chart","group":"89749fb7.87f01","order":3,"width":"7","height":"6","label":"Memory per process id - Pie chart","chartType":"pie","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":"30","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1060,"y":1160,"wires":[[]]},{"id":"89749fb7.87f01","type":"ui_group","name":"Message Profiler","tab":"d7901f40.2659d","order":2,"disp":true,"width":"16","collapse":false,"className":""},{"id":"d7901f40.2659d","type":"ui_tab","name":"Charts","icon":"dashboard","order":40,"disabled":false,"hidden":false}]
Which looks like this:
Remarks:
- In contradiction to the line chart example above, every child process get its own piece of the pie. Instead of one piece of the pie for all child processes (containing the sum of the CPU/memory usage of all processes). Because - in contradiction to the line charts - the pie doesn't show any historical data, so it is less messy if child processes are spawned or aborted. But of course there might be use cases, where it is more useful to show them all summed into a single piece of the pie...
- The pie charts can be changed to bar charts, by only changing the chart type. But keep in mind that bars will be added and removed continuously, when child processes are being spawned or aborted.
Specify which OS processes need to be analyzed:
Node-RED
: analyze only the main Node-RED process (and optionally its child processes).All
: analyze all the active OS processes. When you use this option, the above line chart should show the entire CPU usage of the entire host server.Single pid
: analyze the process whose pid has been injected via the specified input message field.
When this option is selected, the child processes are also being analyzed, which means the entire subtree of child processes will be included (i.e. also child processes being spawned by child processes...).
It might be useful to turn off this option, when you are only interested to monitor the resources used by the main thread. Because otherwise system resources would be wasted, by analyzing the process tree for nothing...
Specify in which field of the output message the result needs to be send.
Specify whether the child processes in the output (when available) need to be ordered ascending or descending.
Specify which field in the output message will be used to order the child processes (when available).