In this article, we will be building a Real-time server monitoring app that allows observing some OS parameters, such as free memory available. In this article, we will be using the NodeJS, Angular and Chart.js, so ensure you have node and Angular studio IDE installed. To create our development server we will be using vagrant, so check here for instructions to install vagrant on your operating system.

Introducing Narrately- An AI powered Text to Speech engine – It’s FREE Try Today 

Introduction

We will be creating two code repos, the first is a node application that will monitor the OS parameters and sends it via websocket to the second app which is an Angular  application, the Angular app will utilize Chart.js to represent the server status graphically.

Server App

Let’s create our server app, by running the following commands in our terminal



mkdir duneserver
cd duneserver
vagrant init ubuntu/trusty64

We want our server to run Ubuntu 16.04, have a static IP address 192.168.50.4 and have NodeJS installed, so replace the content of Vagrantfile with


Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.provision :shell, path: "bootstrap.sh"
config.vm.network "private_network", ip: "192.168.50.4"
end

and create a provisioning script named bootstrap.sh with the content


#!/usr/bin/env bash

apt-get update
curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh
bash nodesource_setup.sh
sudo apt-get install nodejs -y
sudo apt-get install build-essential -y
npm install forever -g

In the script above, we first update the apt repository and then we install NodeJS from a private PPA, install build-essential which is required by some NodeJS modules and install forever which helps us keep a node script running forever. No we start the vagrant server with th command:


vagrant up

Lets create a package.json file with the content below in our duneserver directory


json
{
"name": "duneserver",
"version": "1.0.0",
"description": "Realtime server monitoring app with Angular 4 , NodeJS & d3 js",
"main": "index.js",
"scripts": {
"start": "forever start index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" "
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"os-monitor": "~1.0.5",
"nodemon": "~1.11.0",
"socket.io": "~2.0.3"
}
}

Our package.json contains 3 dependencies, os-monitor–a very simple monitor for the built-in os module in Node.js, nodemon–monitor for any changes in your node.js application and automatically restart the server – perfect for development, and socket.io–enables real-time bidirectional event-based communication.

Let us create our index.js file with the content

var io = require('socket.io')(8000);
var osm = require("os-monitor");

io.on('connect', function (socket) {
socket.emit('connected', {
status: 'connected',
type: osm.os.type(),
cpus: osm.os.cpus(),
});
});

io.on('disconnect', function (socket) {
socket.emit('disconnected');
});

osm.start({
delay: 3000 // interval in ms between monitor cycles
, stream: false // set true to enable the monitor as a Readable Stream
, immediate: false // set true to execute a monitor cycle at start()
}).pipe(process.stdout);

// define handler that will always fire every cycle
osm.on('monitor', function (monitorEvent) {
io.emit('os-update', monitorEvent);
});

In the code above we imported socket.io and created a server running on port 8000, next we imported os-monitor. The socket.io server will emit a connect event when a client app connects with it, so on connect we send the os type (osm.os.type) and number of cpus(osm.os.cpus()) to the client.

In the last two sections of the code above, we start monitoring the server using osm.start(), at an interval of 300ms. os-monitor emits a monitor event on each monitor cycle, so on monitor we use socket.io to send the monitorEvent to the client.

Let’s ssh into the vagrant server, install the node modules and start the web server

vagrant ssh
cd /vagrant # vagrant synced directory
npm install
npm run dev

Angular App, Socket.io and Chart,js

Let’s get started by firing up our Angular IDE and create a new Angular project named DuneServerMonitor.

new angular ide project

Create a new project

Once the project is created we can now add Socket.io and Chart.js to our project. Angular IDE has a terminal view from which we can execute commands, so from the menu select Window, then Show view then click Terminal +. In the opened terminal type the following commands to install Socket.io and Chart.js

npm install socket.io chart.js --save
install dependencies from terminal

Install dependencies from terminal

Monitoring the free memory

Let us include bootstrap css in our project, by adding the following lines to the head section of our src/index.html file.


<!-- Latest compiled and minified CSS --> <!-- Optional theme -->

Then we replace the content of src/app/app.component.html with

<div class="container">
<h1>Dune Server Monitor</h1>
<div class="" style="background: #eee;">
<div class="col-md-4">
<h4>Memory</h4>
 

</div>
<div class="col-md-4">
<h4>Cpu load average</h4>
 

</div>
</div>
</div>

Replace the content of src/app/app.component.ts with

// Imports
import { Component, OnInit } from '@angular/core';
import io from 'socket.io-client';
import { Chart, pattern } from 'chart.js';

//---------------------------------------------------Section I
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
socket = io.connect('192.168.50.4:8000');
memChart = undefined;
cpuChart = undefined;
cpuType = '';
noOfCpu = '';
ngOnInit() {
// ---------------------------------------------------Section II
const ctx = document.getElementById('mChart');
const doughnutGraphData = {
datasets: [{
data: [1, 0],
backgroundColor: ['#36a2eb', '#ff6384'],
}],
labels: [
'Free',
'Used',
]
};
this.memChart = new Chart(ctx, {
type: 'doughnut',
data: doughnutGraphData,
options: {}
});

const ctx2 = document.getElementById('cChart');
const cpuLoadGraphData = {
datasets: [{
label: '15 min average',
data: [],
backgroundColor: 'rgba(75, 192, 192, 0.4)',
}],
labels: ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'],

};
this.cpuChart = new Chart(ctx2, {
type: 'line',
data: cpuLoadGraphData,
options: {}
});

// ----------------------------------------------------------------------Section III
this.socket.on('connected', (connectData) =&gt; this.connected(connectData));
this.socket.on('os-update', (event) =&gt; this.updateCharts(event));
}

// -----------------------------------------------------------------------Section IV
updateCharts(event) {

this.memChart.data.labels.pop();
this.memChart.data.labels.pop();
this.memChart.data.labels.push(`Free:${this.formatBytes(event.freemem, 2)}`);
this.memChart.data.labels.push(`Used:${this.formatBytes(event.totalmem - event.freemem, 2)}`);

this.memChart.data.datasets.forEach((dataset) =&gt; {
dataset.data.pop();
dataset.data.pop();
dataset.data.push(event.freemem);
dataset.data.push(event.totalmem - event.freemem);
});
this.memChart.update(0);

this.cpuChart.data.datasets.forEach((dataset) =&gt; {
if ( dataset.data.length &gt; 9) {
dataset.data.shift();
}
dataset.data.push(event.loadavg[2]);
});
this.cpuChart.update(0);
}

formatBytes(bytes, decimals) {
if (bytes === 0) {
return '0 Bytes';
}
const k = 1000,
dm = decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

connected(connectData) {
this.cpuType = connectData.types;
this.noOfCpu = connectData.cpus;
}
}

Our client app looks like the image below, we’ll now go on to discuss the sections of the code.

In the next Chapter we will explain each code blocks used in this tutorial.