Cara Membuat Aplikasi Web System Monitor dengan Node.js

Tags



Aplikasi berbasis web semakin mudah dibuat akhir-akhir ini.

Dengan lahirnya beberapa front-end javascript framework seperti Vue.js, pengembangan aplikasi web menjadi lebih ter-manage dengan baik.

Apalagi, front-end framework tersebut didukung oleh teknologi back-end dari Node.js.

Dengan menggunakan Node.js, kita dapat melakukan sebuah operasi secara nonblocking.

Artinya, server bisa merespon request lebih cepat.

Dengan adanya peluang tersebut, saya sebagai penulis berpendapat bahwa ada gunanya mempelajari teknologi-teknologi tersebut.

Oleh karena itulah, kali ini kita akan mempelajari pembuatan aplikasi web berbasis Node.js dan framework front-end yang cukup populer, yakni Vue.js.

Download source code project ini di sini:


Apa yang Akan Kita Buat

Kali ini, kita akan belajar membuat aplikasi web system monitoring dengan Node.js, Express.js, Vue.js, dan Chart.js.

Aplikasi ini berguna untuk memantau system performance, misalnya, CPU usage, memory usage, dan berbagai info lainnya.

Kurang lebih, aplikasi ini mirip Task Manager-nya Windows, tetapi berjalan di web.

Akan tetapi, berbeda dengan Windows Task Manager, aplikasi ini akan mengukur system performance dari server di mana aplikasi ini di-host, bukan untuk komputer user, walaupun user dapat melihat hasilnya di komputer user melalui browser.

Adapun data-data yang akan diperoleh melalui aplikasi ini adalah:

  • Current CPU usage
  • CPU usage over time
  • Current memory usage
  • Memory usage over time
  • Jumlah core CPU
  • Platform server yang meng-host aplikasi ini
  • Free memory
  • Total memory
  • System uptime
  • Process uptime


Untuk melihat aplikasi ini yang sudah jadi, buka browser Anda ke:
https://ls-web-system-monitor.herokuapp.com
Atau Anda boleh lihat screenshotnya di sini:


Mengapa Kita Membuatnya

Aplikasi web system monitoring ini berguna untuk memantau server performance, sehingga kita dapat dengan mudah mengetahui apakah server sedang terbebani atau tidak.

Selain itu, pembuatan aplikasi ini sangat bermanfaat untuk mempelajari konsep-konsep yang berkaitan dengan Node.js dan package-package pendukungnya dan yang terpenting adalah memberikan gambaran umum bagaimana aplikasi web single-page berbasis Node.js dibuat.

Bagaimana Kita Membuatnya
Untuk membuatnya, kita perlu memahami rancangan dari aplikasi ini.

Perhatikanlah gambar di bawah ini:


Pertama-tama, kita berasumsi bahwa Node.js telah terinstall di server.

Begitu pula dengan script aplikasi ini yang telah selesai dibuat.

Pada saat user membuka URL (misalnya server Anda adalah localhost port 5000):
http://localhost:5000/
Maka server akan meresponnya dengan sebuah file "index.html" yang diolah di browser.

Setelah itu "index.html" akan mengeksekusi AJAX untuk melakukan request ke localhost port 5000 mengenai data yang harus ditampilkan di browser, misalnya CPU usage, memory usage dan lain lain.

Eksekusi javascript ini akan dilakukan setiap selang waktu satu detik.

Server akan meresponnya dengan data terkait dalam format JSON.

Data dalam bentuk JSON tersebut akan diolah di browser menjadi data yang siap saji, misalnya dalam bentuk grafik.

Selanjutnya javascript di "index.html" akan mengupdate grafik juga setiap selang waktu satu detik.

Hal ini terjadi terus-menerus sampai user menutup halaman web tersebut.

Yang Anda Butuhkan sebelum Kita Belajar

Perlu saya akui, bahwa level artikel ini adalah menengah. Saya berasumsi bahwa Anda telah memahami pemrograman javascript dan setidaknya pernah membuat aplikasi web sebelumnya.

Di samping itu, Anda juga membutuhkan ini:

  • Node.js dan NPM.
  • Text Editor apapun, tapi saya menyarankan Visual Studio Code (gratis, silahkan cari di google).
  • Web Browser apapun, tapi saya menyarankan Chrome.
  • Koneksi internet.

Menginstall Node.js dan NPM

Langkah pertama dalam pembuatan system monitor ini adalah menginstall Node.js dan NPM. Caranya adalah dengan mendownload installer Node.js dari situs resminya.

Installer tersebut sudah mencakup Node.js dan NPM.

Cara menginstallnya cukup sederhana, hanya klik next dan finish saja.


Setelah Node.js terinstall, maka NPM juga akan terinstall.

Membuat Project Node.js

Setelah menginstall Node.js dan NPM, langkah selanjutnya adalah membuat project Node.js.

Pertama-tama, buatlah folder dengan nama "ls-web-system-monitor".

Lokasi folder tersebut bisa di mana saja, pastikan di tempat yang mudah dijangkau.

Untuk membuatnya, bisa menggunakan Right Click>New>Folder di Windows Explorer.

Setelah itu, masuklah ke dalam folder tersebut.

Lalu di Windows Explorer, pergi ke menu File>Open Windows Power Shell>Open Windows Power Shell as Administrator.

Dengan mengklik menu tersebut, Power Shell akan dibuka sebagai administrator pada folder "ls-web-system-monitor".

Power Shell ini sebenarnya mirip dengan Command Prompt, hanya saja ada fiturfitur tambahan seperti beberapa perintah yang mirip dengan Terminal yang ada di Linux.

Setelah Power Shell dibuka ketik:
npm init
Nanti akan diajukan beberapa pertanyaan. Isilah dengan jawaban ini:
name: ls-web-system-monitor
version: 1.0.0
description: terserah
entry point: server.js
test command: kosongkan
git repository: kosongkan
keywords: kosongkan
author: nama Anda
license: ISC
is this ok: yes
Pastikan Anda berada dalam folder "ls-web-system-monitor".

Maka project Node.js dengan nama "ls-web-system-monitor" selesai dibuat.

Menginstall Package yang Dibutuhkan

Setelah project Node.js dibuat, kita akan menginstall package yang diperlukan dalam project ini.

Ada cukup banyak package yang diperlukan, tetapi cara menginstallnya cukup mudah.

Vue.js

npm install vue --save

Vue.js adalah package Node.js yang berfungsi sebagai framework untuk front-end. Framework ini berguna untuk mengembangkan aplikasi web yang single-page seperti yang sedang kita buat ini.

Perhatikan bahwa di sini kita menggunakan argument --save. Tujuannya adalah agar package yang kita install akan dimasukkan ke dalam daftar package di "package.json". Dengan ini, apabila kita memiliki source code tanpa package, kita akan bisa menginstall semua package sekaligus dengan:
npm install 

Vue-Resource

npm install vue-resource --save
Vue-Resource adalah HTTP client untuk Vue.js. Dengan ini kita bisa menggunakan HTTP request dari client side.

Bootstrap

npm install bootstrap --save
Bootstrap adalah library CSS untuk membuat tampilan web menjadi responsive.

JQuery

npm install jquery --save
JQuery dibutuhkan karena Bootstrap membutuhkannya.

Chart.js

npm install chart.js --save
Chart.js adalah package Node.js yang berfungsi sebagai library yang dapat menampilkan grafik. Kita akan membuat grafik pie dan line dengan Chart.js.

OS-Utils Module

npm install os-utils --save
Package ini berfungsi untuk mendapatkan informasi terkait dengan operating system seperti jumlah core pada CPU, penggunaan CPU, penggunaan memory, system uptime, dan process uptime.

Express.js

npm install express --save
Package ini merupakan framework Node.js. Framework ini berfungsi untuk menyederhanakan routing dari aplikasi yang akan kita buat.

FS Module

Module ini berfungsi untuk menulis file snapshot aplikasi ini yang nantinya akan bisa di-download oleh user ke komputer mereka. Kita tidak akan menginstall module ini, karena module ini sudah ada secara default bersama Node.js.

Setelah package tersebut kita install, perhatikanlah pada folder "ls-web-system-monitor". Di sana akan kita jumpai folder baru bernama "node_modules". Di sinilah semua package yang kita install berada.

Mengimpor Semua Package yang Telah Kita Install
Setelah semua package kita install, kita harus mengimpornya terlebih dahulu sebelum dapat digunakan.

Pertama-tama buatlah file bernama "server.js" di dalam folder "ls-web-system-monitor".

Selanjutnya, untuk mengimpor salah satu package, buka file target lalu tulis:

var apapun = require("nama_package");

Jadi, jika kita akan mengimpor package "os-utils", kita akan menulis ini pada file "server.js":

var osu = require("os-utils");

Kali ini, kita akan mengimpor semua package yang telah kita install, maka tulislah ini pada file "server.js":

var osu = require('os-utils');
var fs = require('fs');
var express = require('express');
var app = express();

Perhatikan bahwa pada saat mengimport Express.js, ada lanjutan seperti ini:

var app = express();

Hal itu dilakukan karena pada saat kita me-require Express.js, yang diimpor adalah fungsi. Untuk mendapatkan objectnya, fungsi harus dijalankan.

Lalu bagaimana dengan Vue.js dan Chart.js?

Kedua package tersebut merupakan package front-end. Dengan kata lain, package tersebut hanya digunakan dalam file HTML yang akan dieksekusi dari client.

Sementara, file "server.js" adalah server side script yang berjalan di server. Jadi file "server.js" tidak membutuhkan kedua package tersebut untuk dijalankan di server.

Untuk mengimpor kedua package tersebut, kita harus membuat file HTML terlebih dahulu, lalu mengimpornya seperti mengimpor javascript dalam HTML.

Hal itu akan dibahas pada bagian selanjutnya.

Sementara ini, fokuskan perhatian Anda terlebih dahulu pada server.

Membuat Kerangka Request Handler

Mengetes Request Handler Express

Sampai saat ini, secara teori kita sudah bisa membuat HTTP server sederhana.

Untuk mencobanya, tulis kode di bawah ini pada file baru bernama "servertest.js":

var express = require('express');
var app = express();

app.get('/', function (req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.end("Hello World\n");
});

app.listen(5000, function () {
    console.log('Node app is running on port 5000');
});

Saya sengaja menuliskannya pada file lain agar konsentrasi kita pada file "server.js" tidak buyar.

Setelah menuliskannya, jalankan perintah ini pada Power Shell:
node servertest.js
Tampilannya akan seperti ini:


Penjelasannya...

Pada saat user memasukkan input "http://localhost:5000/" pada browser, yang artinya browser akan menyambungkan dirinya dengan server pada alamat localhost, port 5000, maka request "/" akan diminta pada "servertest.js".

Request ini disaring melalui script "servertest.js" pada baris kode:

app.get('/', function (req, res) { //YANG INI
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.end("Hello World\n");
});

Tentu saja itu hanya bisa terjadi apabila "servertest.js" memiliki instruksi ini:

app.listen(5000, function () {//YANG INI
    console.log('Node app is running on port 5000');
});

Jadi fungsi get akan menyaring request GET dari browser, dan fungsi "app.listen" akan mendengarkannya pada port yang diberikan, dalam hal ini 5000.

Mengembangkan Request Handler Express Pada Aplikasi Kita

Dengan dasar percobaan tadi, sekarang kita bisa mengembangkan request handler untuk menangani semua request yang relevan pada aplikasi yang sedang kita kerjakan ini.

Anda boleh menghapus atau memindahkan file "servertest.js" ke tempat lain agar tidak mengganggu konstentrasi. Itu terserah Anda.

Yang penting, sekarang kita fokuskan kembali pikiran kita pada file "server.js".

Sekarang, kita akan menambahkan baris kode ini di file "server.js":

app.get('/', function (req, res) {
    //di sini kita akan mengembalikan file index.html
});
app.get('/api/home', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten home
});
app.get('/api/about', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten about
});
app.get('/api/contact', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten contact
});
app.get('/api/os_utils_cpu_usage', function (req, res) {
    //di sini kita akan mengembalikan json untuk nilai cpu usage
});
app.get('/api/os_utils_memory_usage', function (req, res) {
    //di sini kita akan mengembalikan json untuk nilai memory usage
});
app.get('/api/os_utils_others', function (req, res) {
    //di sini kita akan mengembalikan json untuk info lainnya
});
app.get('/api/download_snapshot', function (req, res) {
    //di sini kita akan mengembalikan file snapshot untuk didownload
});

Tujuan dari setiap request handler dijelaskan melalui komentarnya. Semua dapat dijelaskan melalui komentar tersebut.

Hanya saja, perhatikan pada:

app.get('/', function (req, res) {
    //di sini kita akan mengembalikan file index.html
});

dan

app.get('/api/download_snapshot', function (req, res) {
    //di sini kita akan mengembalikan file snapshot untuk didownload
});

Yang pertama, "app.get('/' ....".

Di sana kita mengembalikan response berupa file HTML.

Sedangkan yang ke-2, "app.get('/api/download_snapshot' ...".

Di sana kita akan mengembalikan file snapshot untuk didownload.

Dengan kata lain, yang ke-2 adalah download link.

Sisanya, semua request diresponse dengan JSON.

Ini adalah prinsip utama dalam single-page web application, di mana hanya sebuah file HTML saja yang dikembalikan, sedangkan serah-terima data lainnya dari aplikasi tersebut dilakukan melalui JSON yang diterima melalui AJAX.

Dengan cara tersebut, user tidak perlu me-refresh halaman untuk mendapatkan update.

Jadi, sampai saat ini keseluruhan kode dalam file "server.js" adalah:

var osu = require('os-utils');
var fs = require('fs');
var express = require('express');
var app = express();

app.get('/', function (req, res) {
    //di sini kita akan mengembalikan file index.html
});
app.get('/api/home', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten home
});
app.get('/api/about', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten about
});
app.get('/api/contact', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten contact
});
app.get('/api/os_utils_cpu_usage', function (req, res) {
    //di sini kita akan mengembalikan json untuk nilai cpu usage
});
app.get('/api/os_utils_memory_usage', function (req, res) {
    //di sini kita akan mengembalikan json untuk nilai memory usage
});
app.get('/api/os_utils_others', function (req, res) {
    //di sini kita akan mengembalikan json untuk info lainnya
});
app.get('/api/download_snapshot', function (req, res) {
    //di sini kita akan mengembalikan file snapshot untuk didownload
});

app.listen(5000, function () {
    console.log('Node app is running on port', 5000);
});

Anda bisa mengetest server dengan:
node server.js
Sampai saat ini server belum bisa memberikan output apapun, tetapi telah melakukan listen di port 5000.

Membuat Pilihan Port yang Didengar Lebih Dinamis

Walaupun sebenarnya cukup dengan hanya membiarkan port yang didengarkan adalah port 5000, ada cara agar port yang kita tentukan lebih dinamis.

Jadi kita dapat mengganti port yang didengar aplikasi ini dengan port lain tanpa mengubah kode.

Caranya dengan menggunakan:
process.env.PORT
Dengan menggunakan itu, maka kita bisa menge-set port dalam environment variable, lalu nilai port tersebut akan digunakan dalam kode kita.

Sekarang coba tulis kode ini pada file "server.js" di atas request handler dan di bawah require:

//...
var express = require('express');
var app = express();
app.set('port', (process.env.PORT || 5000)); //TAMBAHKAN INI

//request handlers...
app.get('/'...


Maka sampai saat ini, kode file "server.js" adalah seperti ini:

var osu = require('os-utils');
var fs = require('fs');
var express = require('express');
var app = express();

app.set('port', (process.env.PORT || 5000)); //ADA TAMBAHAN INI

app.get('/', function (req, res) {
    //di sini kita akan mengembalikan file index.html
});
app.get('/api/home', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten home
});
app.get('/api/about', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten about
});
app.get('/api/contact', function (req, res) {
    //di sini kita akan mengembalikan json untuk konten contact
});
app.get('/api/os_utils_cpu_usage', function (req, res) {
    //di sini kita akan mengembalikan json untuk nilai cpu usage
});
app.get('/api/os_utils_memory_usage', function (req, res) {
    //di sini kita akan mengembalikan json untuk nilai memory usage
});
app.get('/api/os_utils_others', function (req, res) {
    //di sini kita akan mengembalikan json untuk info lainnya
});
app.get('/api/download_snapshot', function (req, res) {
    //di sini kita akan mengembalikan file snapshot untuk didownload
});

//ADA PERUBAHAN INI
app.listen(app.get('port'), function () {
    console.log('Node app is running on port', app.get('port'));
});

Lalu, pada Power Shell, ketikkan:
$env:PORT = 1234
Kemudian jalankan:
node server.js
Maka aplikasi ini akan memberitahu:
Node app is running on port 1234
Jika pada Power Shell kita langsung menjalankan aplikasi ini tanpa:
$env:PORT = 1234
Maka dia akan berjalan pada port 5000:
Node app is running on port 5000 (*)
Catatan:
(*)Untuk memastikannya, tutup Power Shell, lalu buka kembali pada folder "ls-web-system-monitor" sebagai administrator, lalu jalankan kembali aplikasi ini.

Merancang User Interface

Sekarang saatnya merancang user interface. Untuk melakukannya ada beberapa hal yang perlu dilakukan di server, dan ada pula beberapa hal yang perlu dilakukan di client.

Memberi Akses File Statis Dari Server

Agar user dapat men-download single-page HTML yang akan menampilkan user interface beserta favicon dan client side scriptnya, kita perlu mengonfigurasi Express.js terlebih dahulu.

Dengan demikian, setelah hal tersebut dilakukan, user akan bisa mengakses favicon dalam file HTML dengan cara seperti ini:

<link rel="shortcut icon" type="image/png" href="assets/images/favicon.png"/>

Atau memasukkan javascript dari file HTML dengan cara seperti ini:

<script src="scripts/jquery.min.js"></script>
<script src="scripts/js/bootstrap.min.js"></script>
<script src="scripts/vue.js"></script>
<script src="scripts/vue-resource.min.js"></script>
<script src="scripts/Chart.js"></script>


Atau memasukkan script CSS dari file HTML dengan cara seperti ini:

<link rel="stylesheet" href="scripts/css/bootstrap.min.css">
<link rel="stylesheet" href="scripts/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="assets/css/style.css">

Perhatikan bagian yang digaris bawah. Secara default, Node.js tidak mengizinkan hal seperti ini.

Dengan kata lain, melakukan request misalnya:
http://localhost:5000/scripts/vue.js
Tidak akan dianggap sebagai request yang sah, karena tidak ada request handler untuk hal tersebut (Anda bisa cek lagi request handler yang telah kita buat sebelumnya).

Kecuali jika kita meminta izin terlebih dahulu kepada server di file "server.js".

Untuk meminta izin kepada server agar server bisa menyediakan akses kepada file-file tadi, tulis kode di bawah ini di file "server.js":

//TAMBAHKAN INI
app.use('/scripts', express.static(__dirname + '/node_modules/chart.js/dist/'));
app.use('/scripts', express.static(__dirname + '/node_modules/vue/dist/'));
app.use('/scripts', express.static(__dirname + '/node_modules/vueresource/
dist/'));
app.use('/scripts', express.static(__dirname + '/node_modules/bootstrap/dist/'));
app.use('/scripts', express.static(__dirname + '/node_modules/jquery/dist/'));
app.use('/assets', express.static(__dirname + '/assets/'));
//////////////////////////////////////////

app.set('port', (process.env.PORT || 5000));// DI ATAS INI
//....

Dengan cara ini, maka folder "__dirname/node_modules/chart.js/dist/" akan dipetakan di URL sebagai:
http://localhost:5000/scripts/
Jadi, jika kita merequest:
http://localhost:5000/scripts/vue.js
Maka request itu menjadi sah dan browser akan menerima script "vue.js" sebagai response.

Hal yang sama berlaku dengan "assets". Bedanya kita akan menempatkan file "index.html", "favicon.png", "style.css", dan file snapshot di sana.

Saya sengaja membedakan request untuk package front-end yang telah kita install dengan NPM dengan file statis buatan kita di tempat berbeda agar kita tidak bingung.

Selain itu, perhatikan juga bahwa kita menggunakan variabel global "__dirname".

Variabel ini akan mengembalikan nilai string dari direktori di mana file "server.js" berada.

Jadi kita memberikan informasi pada server tentang lokasi file statis kita dengan menggabungkan "__dirname" dengan direktori di mana file target berada.

Mencoba Mengakses File Statis dari Server

Untuk memastikan bahwa cara ini berhasil, kita akan membuat response bagi request "/" berupa file "index.html".

Pertama-tama, buatlah sebuah folder bernama "assets" di dalam folder "ls-web-system-monitor".

Caranya dengan menggunakan Right Click>New>Folder. Pastikan bahwa folder "assets" berada di dalam folder "ls-web-system-monitor".

Lalu, buatlah folder bernama "css" di dalam folder "assets".

Kemudian, buatlah file baru bernama "index.html" di dalam folder "assets" dan file baru bernama "style.css" di dalam folder "css".

Isi dari file "style.css":

/*Ini Adalah File Style.css*/

Sedangkan isi dari file "index.html":

<!DOCTYPE html>
<html lang="en">
<head>
<title>Node.js System Information - App By Rakifsul</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
INI ADALAH FILE INDEX.HTML
</body>
</html>

Selain itu, kita memerlukan perubahan pada request handler "/" yang telah kita buat:

app.get('/', function (req, res) {
    res.sendfile("assets/index.html");
});

Setelah itu, jalankan aplikasi dengan:
node server.js
Maka dengan melakukan request melalui browser:
http://localhost:5000/
Akan tampil halaman web seperti ini:


Mari kita gali lebih lanjut dengan Right Click>View page source.

Nanti source dari halaman tersebut akan dibuka:


Selanjutnya cobalah klik bagian sini:


Ternyata, file "style.css" dapat diterima, yang artinya request tersebut adalah sah:


Merespon Setiap Request dalam Bentuk JSON dari Server

Setelah kita membuktikan bahwa izin untuk mengakses file statis dari server berjalan semestinya, sekarang saatnya kita membuat response dari setiap request yang telah kita definisikan.

Pada tahap ini, kita akan mengisi tiap request handler yang kita buat dengan kode-kode yang relevan di file "server.js".

Hanya pada request "/" saja kita akan membiarkan kodenya seperti sebelumnya, yakni:

app.get('/', function (req, res) {
    res.sendfile("assets/index.html");
});

Karena bagian itu tidak perlu diubah. Perubahan hanya akan dilakukan pada file "index.html" yang akan dilakukan nanti.

Response untuk Home, About, dan Contact

Untuk request "api/home", "api/about", dan "api/contact" kita hanya akan mengembalikan response berupa teks statis:

app.get('/api/home', function (req, res) {
res.json({title: "Home", message: "Selamat Datang di Node.js Web System Monitor!"});
});

app.get('/api/about', function (req, res) {
res.json({title: "About", message: "Node.js Web System Monitor dibuat oleh Lusfikar Sheba."});
});

app.get('/api/contact', function (req, res) {
res.json({title: "Contact", message: "Kontak Saya di http://www.ciyy.space atau Email ke rakifsul@gmail.com"});
});

Response tersebut akan diberikan ketika user mengakses navigasi "Home", "About", dan "Contact".

Karena tema kita kali ini adalah single page application, maka navigasi tersebut di-respond dengan JSON melalui AJAX.

Response untuk System Performance

Selanjutnya, kita akan membuat response mengenai system performance:

var cpuUsageVal = 0;
app.get('/api/os_utils_cpu_usage', function (req, res) {
    osu.cpuUsage(function(v){
        cpuUsageVal = v;
    });
    res.json({name: "CPU Usage", value: cpuUsageVal});
});

app.get('/api/os_utils_memory_usage', function (req, res) {
    res.json({name: "Memory Usage", value: osu.freememPercentage()});
});

app.get('/api/os_utils_others', function (req, res) {
    var ret = {
        platform: osu.platform(),
        cpuCount: osu.cpuCount(),
        freeMem: osu.freemem(),
        totalMem: osu.totalmem(),
        sysUpTime: osu.sysUptime(),
        processUpTime: osu.processUptime()
    };
    res.json({name: "Other Informations", value: ret});
});

Tidak ada yang membingungkan di kode tersebut, semua kode dapat dipahami dari sintaksnya.

Kecuali bagian:

var cpuUsageVal = 0;
app.get('/api/os_utils_cpu_usage', function (req, res) {
    osu.cpuUsage(function(v){
        cpuUsageVal = v;
    });
    res.json({name: "CPU Usage", value: cpuUsageVal});
});

Perhatikan bahwa di sini kita mendeklarasikan sebuah variabel bernama "cpuUsageVal".

Mengapa kita memerlukan itu?

Jawabannya adalah karena fungsi "osu.cpuUsage" mengembalikan hasilnya melalui callback, sedangkan kita harus merespon request dengan JSON sesegera mungkin.

Oleh karena itulah, dalam kode tersebut, hasil dari "osu.cpuUsage" akan disimpan dalam variabel "cpuUsageVal" sementara JSON akan direspon sesegera mungkin dengan:

res.json({name: "CPU Usage", value: cpuUsageVal});

Seandainya hasil dari "osu.cpuUsage" diberikan melalui return value, maka kita hanya perlu melakukan:

res.json({name: "CPU Usage", value: osu.cpuUsage ()});

Tapi tidak demikian bawaan dari package "os-utils" sehingga kita tidak melakukan itu.

Response untuk Mendapatkan File Snapshot

Respon yang terakhir ini agak rumit, tapi pada dasarnya kita hanya melakukan langkah-langkah ini:

  1. Mendapatkan nilai-nilai system performance di saat user melakukan request
  2. Mengubah nilai-nilai tersebut dalam bentuk JSON
  3. Mengubah nilai JSON menjadi string
  4. Menuliskan nilai tersebut dalam sebuah file bernama "snapshot.json"
  5. Merespon request dengan downloadable content, yakni file "snapshot.json" tersebut

Perhatikanlah kode-kode ini:

app.get('/api/download_snapshot', function (req, res) {
    var snapshot = {
        when: new Date(),
        cpuUsage: cpuUsageVal,
        freememPercentage: osu.freememPercentage(),
        platform: osu.platform(),
        cpuCount: osu.cpuCount(),
        freeMem: osu.freemem(),
        totalMem: osu.totalmem(),
        sysUpTime: osu.sysUptime(),
        processUpTime: osu.processUptime()
    };

    var jstr = JSON.stringify(snapshot,null,'\n');
    fs.writeFile(__dirname + '/assets/snapshot.json', jstr, function(err) {
        if(err)
            return console.error(err);
        console.log('done');
        res.download(__dirname + '/assets/snapshot.json');
    });
});

Kita mendapatkan nilai-nilai system performance dan mengubahnya dalam format JSON di sini:

var snapshot = {
    when: new Date(),
    cpuUsage: cpuUsageVal,
    freememPercentage: osu.freememPercentage(),
    platform: osu.platform(),
    cpuCount: osu.cpuCount(),
    freeMem: osu.freemem(),
    totalMem: osu.totalmem(),
    sysUpTime: osu.sysUptime(),
    processUpTime: osu.processUptime()
};

Kemudian kita mengubah JSON tersebut menjadi string di sini:

var jstr = JSON.stringify(snapshot,null,'\n');

Argument pertama diisi oleh "var snapshot" yang merupakan JSON.

Argument ke-2 digunakan sebagai whitelist dari property JSON yang dimasukkan.

Dalam kasus ini, nilainya null, yang artinya semua property dimasukkan.

Argument ke-3, yakni dengan parameter '\n', adalah dengan apa whitespace dari string JSON tersebut
diisi.

Dalam kasus ini, kita mengisinya dengan newline ('\n').

Kemudian kita menuliskan JSON string tersebut dalam sebuah file lalu memberikan downloadable content-nya kepada user:

fs.writeFile(__dirname + '/assets/snapshot.json', jstr, function(err) {
    if(err)
        return console.error(err);
    console.log('done');
    res.download(__dirname + '/assets/snapshot.json');
});

Perhatikan pada:

fs.writeFile(__dirname + '/assets/snapshot.json'...

Kita menge-save snapshot tersebut pada folder yang telah diizinkan oleh server.

Kita melakukan itu karena user harus dapat mendownload file snapshot tersebut.

Mengimplementasikan Client Side Script pada File Index.html

Setelah semua request handler diimplementasikan, sekarang saatnya membuat client side script "index.html".

Karena kita telah membuat file-nya, berarti sekarang tinggal mengubahnya agar sesuai dengan kebutuhan aplikasi system monitor ini.

Membuat atau Mengcopy Favicon.png Ke Folder Assets

Karena kita akan menggunakan favicon di sisi client, maka Anda akan membutuhkan file PNG apapun dengan ukuran 16x16 pixel.

Letakkan file PNG tersebut dalam folder "assets/images".

Beri nama "favicon.png".

Mengimpor Front-End Javascript dan CSS

Hal pertama yang perlu dilakukan adalah mengimpor front-end javascript dan CSS.

Untuk mengimpor CSS:

<link rel="stylesheet" href="scripts/css/bootstrap.min.css">
<link rel="stylesheet" href="scripts/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="assets/css/style.css">

Letakkan ini di antara tag "<head>" dan "</head>".

Sedangkan untuk mengimpor front-end javascript:

<script src="scripts/jquery.min.js"></script>
<script src="scripts/js/bootstrap.min.js"></script>
<script src="scripts/vue.js"></script>
<script src="scripts/vue-resource.min.js"></script>
<script src="scripts/Chart.js"></script>

Letakkan ini juga di antara tag "<head>" dan "</head>".

Perhatikan bahwa "vue.js", "vue-resource.min.js", dan "Chart.js" diimpor disini. Seperti yang telah saya isyaratkan di bagian awal tulisan ini.

Dengan demikian, keseluruhan file "index.html" adalah seperti ini:

<!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.js Web System Monitor - Aplikasi Buatan Lusfikar Sheba</title>

<meta name="description" content="Node.js Web System Monitor">
<meta name="author" content="Lusfikar Sheba">

<link rel="shortcut icon" type="image/png" href="assets/images/favicon.png"/>
<link rel="stylesheet" href="scripts/css/bootstrap.min.css">
<link rel="stylesheet" href="scripts/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="assets/css/style.css">
<script src="scripts/jquery.min.js"></script>
<script src="scripts/js/bootstrap.min.js"></script>
<script src="scripts/vue.js"></script>
<script src="scripts/vue-resource.min.js"></script>
<script src="scripts/Chart.js"></script>
</head>
<body>
<div id="app" class="container">
<nav class="navbar navbar-default navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Node.js System Monitor</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#" v-on:click="home">Home</a></li>
<li><a href="#" v-on:click="about">About</a></li>
<li><a href="#" v-on:click="contact">Contact</a></li>
</ul>
</div>
</div>
</nav>

<div class="jumbotron">
<h1>{{ jumboMessage.title }}</h1>
<p>{{ jumboMessage.message }}</p>
</div>

<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<b>Current {{ cpuUsage.name }}</b>
</div>
<div class="panel-body">
<canvas id="cpuUsageChartPie" width="100" height="50"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<b>{{ cpuUsage.name }} Over Time</b>
</div>
<div class="panel-body">
<canvas id="cpuUsageChart" width="100" height="50"></canvas>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<b>Current {{ memoryUsage.name }}</b>
</div>
<div class="panel-body">
<canvas id="memoryUsageChartPie" width="100" height="50"></canvas>
</div>
</div>
</div>

<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<b>{{ memoryUsage.name }} Over Time</b>
</div>
<div class="panel-body">
<canvas id="memoryUsageChart" width="100" height="50"></canvas>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<b>{{ otherInformations.name }}</b>
</div>
<table class="table table-striped">
<tbody>
<tr>
<td><b>Platform</b></td>
<td>{{ otherInformations.value.platform }}</td>
</tr>
<tr>
<td><b>CPU Count</b></td>
<td>{{ otherInformations.value.cpuCount }}</td>
</tr>
<tr>
<td><b>Free Memory</b></td>
<td>{{ otherInformations.value.freeMem }} MB</td>
</tr>
<tr>
<td><b>Total Memory</b></td>
<td>{{ otherInformations.value.totalMem }} MB</td>
</tr>
<tr>
<td><b>System Uptime</b></td>
<td>{{ otherInformations.value.sysUpTime }} second(s)</td>
</tr>
<tr>
<td><b>Process Uptime</b></td>
<td>{{ otherInformations.value.processUpTime }} second(s)</td>
</tr>
</tbody>
</table>
</div>
</div>

<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<b>Tools</b>
</div>
<div class="panel-body text-center">
<a class="btn btn-primary btn-lg" href="/api/download_snapshot">Get Snapshot!</a>
</div>
</div>
</div>
</div>

<nav class="navbar navbar-default navbar-inverse">
<div class="container-fluid">
<p class="navbar-text pull-right">Copyright &#169; Lusfikar Sheba 2017</p>
</div>
</nav>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
jumboMessage: {
title: "Home",
message: "Selamat Datang di Node.js Web System Monitor!"
},
cpuUsage: {
name: "",
value: ""
},
memoryUsage: {
name: "",
value: ""
},
otherInformations: {
name: "",
value: ""
}

},
created: function() {
this.os_utils_cpu_usage();
this.timer = setInterval(this.os_utils_cpu_usage, 1000);

this.os_utils_memory_usage();
this.timer = setInterval(this.os_utils_memory_usage, 1000);

this.os_utils_others();
this.timer = setInterval(this.os_utils_others, 1000);
},
methods: {
home: function () {
this.$http.get('/api/home').then(response => {
this.jumboMessage = response.body;
}, response => {

});
},
about: function () {
this.$http.get('/api/about').then(response => {
this.jumboMessage = response.body;
}, response => {

});
},
contact: function () {
this.$http.get('/api/contact').then(response => {
this.jumboMessage = response.body;
}, response => {

});
},
os_utils_cpu_usage: function () {
this.$http.get('/api/os_utils_cpu_usage').then(response => {
this.cpuUsage = response.body;
}, response => {

});
},
os_utils_memory_usage: function () {
this.$http.get('/api/os_utils_memory_usage').then(response => {
this.memoryUsage = response.body;
}, response => {

});
},
os_utils_others: function () {
this.$http.get('/api/os_utils_others').then(response => {
this.otherInformations = response.body;
}, response => {

});
}
}
});
</script>

<script>
var configCPUUsagePie = {
type: 'pie',
data: {
labels: [
"CPU Used",
"CPU Free"
],
datasets: [{
data: [100, 50],
backgroundColor: [
"#FF6384",
"#36A2EB"
],
hoverBackgroundColor: [
"#FF6384",
"#36A2EB"
]
}]
}
};

var cpuUsageChartPie = new Chart(document.getElementById("cpuUsageChartPie").getContext("2d"), configCPUUsagePie);

setInterval(function(){
configCPUUsagePie.data.datasets[0].data[0] = app.cpuUsage.value * 100;
configCPUUsagePie.data.datasets[0].data[1] = (1 - app.cpuUsage.value) * 100;
cpuUsageChartPie.update();
}, 1000);
</script>

<script>
var configCPUUsage = {
type: 'line',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
datasets: [{
label: "CPU Usage Over Time",
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
fill: false
}]
}
};

var cpuUsageChart = new Chart(document.getElementById("cpuUsageChart").getContext("2d"), configCPUUsage);

setInterval(function(){
configCPUUsage.data.datasets[0].data.shift();
configCPUUsage.data.datasets[0].data.push(app.cpuUsage.value * 100);
cpuUsageChart.update();
}, 1000);
</script>

<script>
var configMemoryUsagePie = {
type: 'pie',
data: {
labels: [
"Memory Used",
"Memory Free"
],
datasets: [{
data: [100, 50],
backgroundColor: [
"#FF6384",
"#36A2EB"
],
hoverBackgroundColor: [
"#FF6384",
"#36A2EB"
]
}]
}
};

var memoryUsageChartPie = new Chart(document.getElementById("memoryUsageChartPie").getContext("2d"), configMemoryUsagePie);

setInterval(function(){
configMemoryUsagePie.data.datasets[0].data[0] = app.memoryUsage.value * 100;
configMemoryUsagePie.data.datasets[0].data[1] = (1 - app.memoryUsage.value) * 100;
memoryUsageChartPie.update();
}, 1000);
</script>

<script>
var configMemoryUsage = {
type: 'line',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
datasets: [{
label: "Memory Usage Over Time",
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
fill: false
}]
}
};

var memoryUsageChart = new Chart(document.getElementById("memoryUsageChart").getContext("2d"), configMemoryUsage);

setInterval(function(){
configMemoryUsage.data.datasets[0].data.shift();
configMemoryUsage.data.datasets[0].data.push(app.memoryUsage.value * 100);
memoryUsageChart.update();
}, 1000);
</script>
</body>
</html>

Penjelasan Kode dari File Index.html

Sekilas tidak ada yang aneh dengan file "index.html" karena hanya berupa file HTML biasa.

Tapi perhatikan baris-baris kode yang terdapat pada file "index.html" ini:

<b>Current {{ cpuUsage.name }}</b>

<b>{{ cpuUsage.name }} Over Time</b>

<b>Current {{ memoryUsage.name }}</b>

<b>{{ memoryUsage.name }} Over Time</b>

<b>{{ otherInformations.name }}</b>

<tr>
<td><b>Platform</b></td>
<td>{{ otherInformations.value.platform }}</td>
</tr>

<tr>
<td><b>CPU Count</b></td>
<td>{{ otherInformations.value.cpuCount }}</td>
</tr>

<tr>
<td><b>Free Memory</b></td>
<td>{{ otherInformations.value.freeMem }} MB</td>
</tr>

<tr>
<td><b>Total Memory</b></td>
<td>{{ otherInformations.value.totalMem }} MB</td>
</tr>

<tr>
<td><b>System Uptime</b></td>
<td>{{ otherInformations.value.sysUpTime }} second(s)</td>
</tr>

<tr>
<td><b>Process Uptime</b></td>
<td>{{ otherInformations.value.processUpTime }} second(s)</td>
</tr>

Di sana terdapat kode yang diapit oleh "{{" dan "}}". Ini bukan kode HTML bawaan.

Kode ini akan di-replace oleh Vue. js menjadi suatu nilai tertentu dari variable yang diletakkan di antara "{{" dan "}}".

Jadi, "{{ otherInformations.value.freeMem }}" misalnya, akan diisi nilai memory yang tidak terpakai yang didefinisikan di member variable "otherInformations.value.freeMem".

Lalu di mana "otherInformations" berada?

Jawabnya, variabel tersebut ada di dalam object Vue dari "vue.js" yang didefinisikan pada kode berikut ini:

var app = new Vue({
    el: '#app',
    data: {
        jumboMessage: {
            title: "Home",
            message: "Selamat Datang di Node.js Web System Monitor!"
        },
        cpuUsage: {
            name: "",
            value: ""
        },
        memoryUsage: {
            name: "",
            value: ""
        },
        otherInformations: { //<<==DI SINI
            name: "",
            value: ""
        }
//....

Di samping itu, nilai "freeMem" dari "otherInformations.value.freeMem" didapatkan dari server melalui AJAX dalam fungsi ini:

os_utils_others: function () {
    this.$http.get('/api/os_utils_others').then(response => {
        this.otherInformations = response.body;
    }, response => {
    });
}

Perhatikan bahwa kita melakukan GET pada request handler "/api/os_utils_others" yang telah kita implementasikan sebelumnya.

Request tersebut direspon dengan JSON, kemudian nilainya masuk ke "otherInformations".

Akan tetapi itu semua hanya terjadi bila variable tersebut (yang diapit "{{" dan "}}") berada dalam tag HTML dengan ID "app".

Nilainya harus sama dengan nilai "el" dari object Vue:

var app = new Vue({
el: '#app', //<<==YANG INI

Maka Anda akan dapat membuktikan bahwa semua variable yang diapit dengan "{{" dan "}}" berada dalam tag HTML dengan ID "app" (merupakan child-nya), dalam hal ini berupa "div" tag:

<div id="app" class="container"> <!-- YANG INI -->
<nav class="navbar navbar-default navbar-inverse">
<div class="container-fluid">

Tag tersebut tepat berada dibawah tag "<body>" jika Anda ingin melihatnya.

Kita melakukan hal yang sama pada variable yang lain:

data: {
    jumboMessage: {
        title: "Home",
        message: "Selamat Datang di Node.js Web System Monitor!"
    },
    cpuUsage: {
        name: "",
        value: ""
    },
    memoryUsage: {
        name: "",
        value: ""
    },
    otherInformations: {
        name: "",
        value: ""
    }
},

Semua nilai ini dapat diterima dalam tag HTML dengan mengapitnya dengan "{{" dan "}}".

Hal ini disebut sebagai data binding.

Nilai awal dari data binding tersebut didapat ketika object Vue baru diciptakan, kemudian nilai update-nya diambil tiap satu detik dengan menggunakan "setInterval":

created: function() {
    this.os_utils_cpu_usage();
    this.timer = setInterval(this.os_utils_cpu_usage, 1000);
    this.os_utils_memory_usage();
    this.timer = setInterval(this.os_utils_memory_usage, 1000);
    this.os_utils_others(); //<<===YANG INI
    this.timer = setInterval(this.os_utils_others, 1000); //<<==KEMUDIAN YANG INI
},

Khusus untuk variable "cpuUsage" dan "memoryUsage", nilai data yang di-binding akan dimasukkan sebagai input bagi grafik pie dan line.

Yang pada akhirnya grafik tersebut juga akan di-update setiap satu detik dengan "setInterval".

Jadi ada 2 penggunaan "setInterval" di sini, yakni untuk request dan untuk update grafik.

Sebagai contoh, untuk grafik pie "cpuUsage":

<script>
var configCPUUsagePie = {
type: 'pie',
data: {
labels: [
"CPU Used",
"CPU Free"
],
datasets: [{
data: [100, 50],
backgroundColor: [
"#FF6384",
"#36A2EB"
],
hoverBackgroundColor: [
"#FF6384",
"#36A2EB"
]
}]
}
};

var cpuUsageChartPie = new Chart(document.getElementById("cpuUsageChartPie").getContext("2d"), configCPUUsagePie);

setInterval(function(){ //<<==SET INTERVAL LAGI
configCPUUsagePie.data.datasets[0].data[0] = app.cpuUsage.value * 100; //<<==YANG INI
configCPUUsagePie.data.datasets[0].data[1] = (1 - app.cpuUsage.value) * 100; //<<==YANG INI
cpuUsageChartPie.update();
}, 1000);
</script>

Dan untuk grafik line cpuUsage:

<script>
var configCPUUsage = {
type: 'line',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
datasets: [{
label: "CPU Usage Over Time",
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
fill: false
}]
}
};

var cpuUsageChart = new Chart(document.getElementById("cpuUsageChart").getContext("2d"), configCPUUsage);

setInterval(function(){ //<<==SET INTERVAL LAGI
configCPUUsage.data.datasets[0].data.shift();
configCPUUsage.data.datasets[0].data.push(app.cpuUsage.value * 100);//<<==YANG
INI
cpuUsageChart.update();
}, 1000);
</script>

Hal yang serupa juga berlaku bagi "memoryUsage".

Bagian "Home", "About" dan "Contact" juga diperlakukan dengan cara yang serupa, hanya saja yang diambil nilainya hanya teks-nya saja dan nilainya tidak dimasukkan ke dalam grafik.

Agar user bisa berpindah dari "Home", ke "About", dan ke "Contact" maka digunakan event "v-on:click":

<ul class="nav navbar-nav navbar-right">
<li><a href="#" v-on:click="home">Home</a></li>
<li><a href="#" v-on:click="about">About</a></li>
<li><a href="#" v-on:click="contact">Contact</a></li>

Jika link-link tersebut di-click, maka fungsi dari object Vue ini akan dipanggil:

methods: {
home: function () {
this.$http.get('/api/home').then(response => {
this.jumboMessage = response.body;
}, response => {

});
},
about: function () {
this.$http.get('/api/about').then(response => {
this.jumboMessage = response.body;
}, response => {

});
},
contact: function () {
this.$http.get('/api/contact').then(response => {
this.jumboMessage = response.body;
}, response => {

});
},

Saat ini, kita telah selesai membuat aplikasi web system monitor. Hasil akhirnya dapat kita lihat di browser dengan mengakses:
http://localhost:5000/

Tampilannya kurang lebih seperti ini:


Apabila tombol Get Snapshot diklik, maka Anda akan mendapatkan file JSON berisi snapshot yang bisa di-download.

Penutup

Sekarang, kita telah mempelajari dasar-dasar Node.js yang walaupun singkat, tapi tetap menyeluruh.

Di samping itu, kita juga telah mencoba mempraktikkan dasar-dasar Node.js dalam pembuatan aplikasi sederhana.

Saya menyadari bahwa PASTI banyak kekurangan dalam tulisan saya ini.

Oleh karena itu, saya meminta maaf jika ada yang salah dalam buku ini.

Jika ada hal yang salah dalam buku ini, jangan diikuti, tapi perbaiki.

This Is The Newest Post


EmoticonEmoticon

Catatan: Hanya anggota dari blog ini yang dapat mengirim komentar.