1.はじめに
あるlinuxサーバで原因不明のメモリリークが発生しました。
しかし、そのサーバは陸の孤島にあったのです。
なんとか陸の孤島にたどり着き原因を解明し事なきを得ましたが、
次また問題が発生したときに同じ思いをすることになります。
「Webでリソース監視しよう。。」
git上に**デモページ**をupしましたので、先にご覧ください。
※本来はphp使ってるのですが、githubなのでhtml化にしてます
※csvは2月20日~22日に3日間で、Display Timeはアクセス時点から一時間前になるようにしてます
2.構成について
実はこのシステムはWeb/APサーバ3台をロードバランサーで負荷分散していました。
そのため以下のような流れで構成を考えました。
各サーバ、cronでシェルスクリプトを毎分実行し、1分毎のリソースをCSVデータに蓄積します。
Webページ本体はWeb/AP03に配置し、AP01とAP02のCSVデータを5分毎にAP03にSSHでコピーします。
そして、集積されたCSVデータより各サーバのリソース情報をChart.jsでビジュアライズ化します。
UIもラクしたかったので、今回は無料版のMDB(Material Design for Bootstrap)を使ってみることにしました。
3.環境について
- OS => CentOS release 6.9 (Final)
- Kernel => 2.6.32-696.16.1.el6.x86_64 x86_64
- Shell => GNU bash, version 4.1.2(2)-release (x86_64-redhat-linux-gnu)
- Chart.js => 2.7.3
- Material Design for Bootstrap => MDB FREE: 4.7.1
4.Chart.jsについて
Chart.jsとは、とってもかわいいグラフがサクッと作成できるJavascriptライブラリ。
Canvas要素を用いて描画しており、様々なグラフを描画することが可能です。
アニメーションもFeel so good.
見た目と使いやすさを兼ね備えたChart.jsでリソース情報を表示してモチベーションをアゲていきます。
5.リソース情報をCSVデータとして蓄積
cronでシェルスクリプトを一定時間間隔(今回は毎分)で実行し、結果をcsvファイルに蓄積していきます。
シェルスクリプトの中身ですが、
リソース結果を返してくれるコマンドを実行し、その結果からグラフ表示したい部分のみをカンマ区切りに整形します。
そして、整形した一行のデータをcsvファイル下部に追加していきます。
各データの頭には、実行時間(YYYY/MM/DD HH : mm : ss)を付与しておきます。
取得したいリソース情報を整理
以下コマンドを実行し得られる結果からそれぞれのグラフを描画していきます。
メモリ使用率
-
freeコマンド
- 実メモリの使用量
- バッファ・キャッシュの容量を加算したメモリの使用量
- スワップメモリの使用量
[root@localhost ~]# free
total used free shared buffers cached
Mem: 1205392 175156 1030236 468 10796 60060
-/+ buffers/cache: 104300 1101092
Swap: 1015804 0 1015804
freeコマンドの見方は以下をご覧ください。
Mem
=> 実メモリのサイズ
-/+ buffers/cache
=> ページキャッシュとバッファキャッシュを考慮したメモリサイズ
Swap
=> スワップに割り当てたサイズ
コマンド結果から以下のリソースをCSVファイルに蓄積していきます。
Mem:total
=>実メモリの全容量
Mem:used
=>実メモリの使用容量
-/+ buffers/cache:used
=>実メモリのusedからbuffersとcachedを引いた容量
Swap:total
=>スワップ領域の全容量
Swap:used
=>スワップ領域の使用容量
CPU使用率&スワップ
-
vmstatコマンド
- CPUの使用率
- スワップイン・アウトの情報
[root@localhost ~]# vmstat
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 1029624 11004 60072 0 0 196 9 51 74 1 2 97 0 0
vmstatコマンドの結果から、swap
とcpu
の部分を抽出します。
swap:si
=>ディスクからスワップインされたメモリ量
swap:so
=>ディスクにスワップアウトされたメモリ量
cpu:us
=>カーネル以外が使用したCPU使用率
cpu:sy
=>カーネルが使用したCPU使用率
cpu:id
=>アイドル状態の割合
cpu:wa
=>I/Oウェイトにかかった割合
cpu:st
=>ゲストOSがリソース要求を行ったが、CPUリソースを割り当ててもらえなかった時間の割合
ロードアベレージ
-
topコマンド
- 実行中のプロセス
- ロードアベレージの情報
[root@localhost ~]# top -a -b -n 1 | sed -n '8,17p'
1272 mysql 20 0 368m 27m 5048 S 0.0 2.3 0:00.55 mysqld
1380 root 20 0 255m 9140 3744 S 0.0 0.8 0:00.30 httpd
1416 daemon 20 0 591m 9128 1336 S 0.0 0.8 0:00.00 httpd
1417 daemon 20 0 591m 9128 1336 S 0.0 0.8 0:00.00 httpd
1415 daemon 20 0 591m 9124 1332 S 0.0 0.8 0:00.00 httpd
1611 root 20 0 102m 4604 3548 S 0.0 0.4 0:00.07 sshd
1537 postfix 20 0 81332 4164 3028 S 0.0 0.3 0:00.00 local
1535 postfix 20 0 81176 3600 2684 S 0.0 0.3 0:00.00 cleanup
1374 postfix 20 0 81100 3580 2656 S 0.0 0.3 0:00.00 qmgr
1373 postfix 20 0 81032 3480 2580 S 0.0 0.3 0:00.00 pickup
[root@localhost ~]# top -a -b -n 1 | sed -n "1p" | awk '{print $(NF-2) $(NF-1) $(NF)}'
0.00,0.00,0.00
一つ目のコマンドでコマンド実行時のプロセス上位10件を取得しています。
コマンドの詳細は以下になります。
top -a -b -n 1 | sed -n '8,17p'
=>topコマンドをメモリ使用率順にソートしバッチモードで1回実行し、
その結果の8行目~17行目を表示する
-a # メモリ使用率順にソート
-b # バッチモード
-n # 実行回数を指定(上記の場合1回)
sed # 入力に対してテキスト変換などを行うコマンド
-n # 指示した行だけを標準出力にする。(上記の場合8~17行目を出力)
二つ目のコマンドでロードアベレージを取得しています。
コマンドの詳細は以下になります。
top -a -b -n 1 | sed -n "1p" | awk '{print $(NF-2) $(NF-1) $(NF)}'
=>topコマンドをメモリ使用率順にソートしバッチモードで1回実行し、
その結果の1行目の最終フィールド、最終フィールドから1つ前、2つ前の3つを表示する
awk # 入力された列に対して、フィールド区切りなどを指定するコマンド
(上記の場合、$(NF)で行の最終フィールドを指定。$(NF-1)は最後から一つ前)
セッション数
-
netstatコマンド
- StateがESTABLISHEDのセッション数
- StateがTIME_WAITのセッション数
- StateがLISTENのセッション数
[root@localhost ~]# netstat -tan | grep ':80 ' | awk '{print $6}' | grep 'ESTABLISHED' | sort | uniq -c
1 ESTABLISHED
[root@localhost ~]# netstat -tan | grep ':80 ' | awk '{print $6}' | grep 'LISTEN' | sort | uniq -c
1 LISTEN
[root@localhost ~]# netstat -tan | grep ':80 ' | awk '{print $6}' | grep 'TIME_WAIT' | sort | uniq -c
1 TIME_WAIT
stateについては以下の通りです。
ESTABLISHED
=>TCPでの接続が確立されて、現在通信が行われている状態
TIME_WAIT
=>接続終了待ちの状態
LISTEN
=>利用可能なポートの待ち受け状態
シェルスクリプトでCSVファイルを作成&データ蓄積
各リソース情報の取得コマンドが決まりました!
あとはcronで毎分実行するシェルスクリプトを作成します。
※以下はWebページを配置しているWeb/AP03のシェルスクリプトです。
csvファイルのパスの部分のみ、各サーバ名に変更する必要があります。
# !/bin/bash
DIR='/home/hoge/resources'
CSVDIR='/home/hoge/resources/csv'
TIMESTAMP=`/bin/date '+%Y%m%d%H%M'`
MEMORYARRAY=(`free`)
CPUARRAY=(`vmstat`)
PROSECCARRAY=(`top -a -b -n 1 | sed -n '8,17p'`)
LOADAVARAGE=(`top -a -b -n 1 | sed -n "1p" | awk '{print $(NF-2) $(NF-1) $(NF)}'`)
STATE=(`netstat -tan | grep ':80 ' | awk '{print $6}' | grep 'ESTABLISHED' | sort | uniq -c`)
STATL=(`netstat -tan | grep ':80 ' | awk '{print $6}' | grep 'LISTEN' | sort | uniq -c`)
STATT=(`netstat -tan | grep ':80 ' | awk '{print $6}' | grep 'TIME_WAIT' | sort | uniq -c`)
if [ ! -d $CSVDIR/$(date +%Y) ]; then
mkdir $CSVDIR/$(date +%Y)
fi
if [ ! -d $CSVDIR/$(date +%Y)/$(date +%Y_%m) ]; then
mkdir $CSVDIR/$(date +%Y)/$(date +%Y_%m)
fi
if [ ! -d $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d) ]; then
mkdir $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)
mkdir $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP01
mkdir $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP02
mkdir $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP03
fi
# section "free" command
echo $TIMESTAMP,\
${MEMORYARRAY[7]},\
${MEMORYARRAY[8]},\
${MEMORYARRAY[15]},\
${MEMORYARRAY[18]},\
${MEMORYARRAY[19]} \
>> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP03/memory.csv
# section "vmstat" command
echo $TIMESTAMP,\
${CPUARRAY[29]},\
${CPUARRAY[30]},\
${CPUARRAY[35]},\
${CPUARRAY[36]},\
${CPUARRAY[37]},\
${CPUARRAY[38]},\
${CPUARRAY[39]} \
>> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP03/cpu.csv
# section "top" command
cnt=10
incnt=12
PICTMP=()
for i in `seq $cnt`
do
for j in `seq $incnt`
do
PICTMP+=(${PROSECCARRAY[(j-1)]})
done
done
RESULT=$(echo ${PROSECCARRAY[@]} | sed 's/ /,/g')
echo $TIMESTAMP,\
$LOADAVARAGE,\
$RESULT \
>> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP03/process.csv
# section "netstat" command
if [ "${STATE[0]}" = '' ]; then ANS1=0;else ANS1=${STATE[0]};fi
if [ "${STATL[0]}" = '' ]; then ANS2=0;else ANS2=${STATL[0]};fi
if [ "${STATT[0]}" = '' ]; then ANS3=0;else ANS3=${STATT[0]};fi
echo $TIMESTAMP,$ANS1,$ANS2,$ANS3 \
>> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP03/session.csv
上部の変数でディレクトリのパスやら、コマンドやらを格納しています。
※コマンド結果を変数に入れることができるのをこの時知りました。。
変数以下のif文でディレクトリがない場合作成し、その後、各リソース情報をcsvファイルに書き込んでいます。
csvファイルはコマンド単位で作成しています。
名前がわかりづらくなってしまいました。。
vmstatコマンドの結果 => cpu.csv
freeコマンドの結果 => memory.csv
topコマンドの結果 => process.csv
netstatコマンドの結果 => session.csv
そして、Web/AP01とWeb/AP02にはリソース取得のスクリプトのほかに、以下のシェルスクリプトを仕込みます。
# !/bin/bash
DIR='/home/hoge/resources'
CSVDIR='/home/hoge/resources/csv'
cat $DIR/cpu.csv | ssh 192.168.1.1 "cat >> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP01/cpu.csv"
cat $DIR/memory.csv | ssh 192.168.1.1 "cat >> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP01/memory.csv"
cat $DIR/process.csv | ssh 192.168.1.1 "cat >> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP01/process.csv"
cat $DIR/session.csv | ssh 192.168.1.1 "cat >> $CSVDIR/$(date +%Y)/$(date +%Y_%m)/$(date +%m%d)/WebAP01/session.csv"
: > $DIR/cpu.csv
: > $DIR/memory.csv
: > $DIR/process.csv
: > $DIR/session.csv
catコマンドとsshコマンドを組み合わせることで、Web/AP03にcsvファイルの内容を送信しています。
送信データのサイズを一定に保ちファイルの肥大化を防ぐため、下4行の「:>~」を書いておくことで、送信後にcsvファイルを空に上書きしています。
そのほかに1年分のリソースを取得した際(1月1日に実行されるcron)のシェルスクリプト等もありますが割愛します。
6.CSVデータをもとにChart.jsを描画
シェルスクリプトによって作成されたCSVファイルは以下のようになります。(一部抽出)
- freeコマンド
[root@localhost WebAP03]# cat memory.csv
201906051322,1205392,164536,98072,1015804,0
201906051323,1205392,172540,103196,1015804,0
201906051324,1205392,173448,103844,1015804,0
201906051325,1205392,174704,104872,1015804,0
201906051326,1205392,177324,106588,1015804,0
- topコマンド
[root@localhost WebAP03]# cat process.csv
201906051322,0.05,0.02,0.00,1274,mysql,20,0,368m,25m,5048,S,0.0,2.2,0:00.07,mysqld,1382,root,20,0,255m,9144,3744,S,0.0,0.8,0:00.02,httpd,1391,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1392,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1390,daemon,20,0,591m,9128,1332,S,0.0,0.8,0:00.00,httpd,1517,postfix,20,0,81332,4164,3032,S,0.0,0.3,0:00.00,local,1515,postfix,20,0,81176,3604,2684,S,0.0,0.3,0:00.00,cleanup,1376,postfix,20,0,81100,3564,2656,S,0.0,0.3,0:00.00,qmgr,1375,postfix,20,0,81032,3480,2580,S,0.0,0.3,0:00.00,pickup,1369,root,20,0,80952,3468,2556,S,0.0,0.3,0:00.01,master
201906051323,0.02,0.02,0.00,1274,mysql,20,0,368m,27m,5048,S,0.0,2.3,0:00.09,mysqld,1382,root,20,0,255m,9144,3744,S,0.0,0.8,0:00.03,httpd,1391,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1392,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1390,daemon,20,0,591m,9128,1332,S,0.0,0.8,0:00.00,httpd,1517,postfix,20,0,81332,4168,3032,S,0.0,0.3,0:00.00,local,1515,postfix,20,0,81176,3604,2684,S,0.0,0.3,0:00.00,cleanup,1376,postfix,20,0,81100,3580,2656,S,0.0,0.3,0:00.00,qmgr,1375,postfix,20,0,81032,3484,2580,S,0.0,0.3,0:00.00,pickup,1369,root,20,0,80952,3468,2556,S,0.0,0.3,0:00.01,master
201906051324,0.00,0.01,0.00,1274,mysql,20,0,368m,27m,5048,S,0.0,2.3,0:00.12,mysqld,1382,root,20,0,255m,9144,3744,S,0.0,0.8,0:00.03,httpd,1391,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1392,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1390,daemon,20,0,591m,9128,1332,S,0.0,0.8,0:00.00,httpd,1517,postfix,20,0,81332,4168,3032,S,0.0,0.3,0:00.00,local,1515,postfix,20,0,81176,3604,2684,S,0.0,0.3,0:00.00,cleanup,1376,postfix,20,0,81100,3580,2656,S,0.0,0.3,0:00.00,qmgr,1375,postfix,20,0,81032,3484,2580,S,0.0,0.3,0:00.00,pickup,1369,root,20,0,80952,3468,2556,S,0.0,0.3,0:00.01,master
201906051325,0.00,0.01,0.00,1274,mysql,20,0,368m,27m,5048,S,0.0,2.3,0:00.15,mysqld,1382,root,20,0,255m,9144,3744,S,0.0,0.8,0:00.04,httpd,1391,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1392,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1390,daemon,20,0,591m,9128,1332,S,0.0,0.8,0:00.00,httpd,1735,root,20,0,102m,4576,3540,S,4.0,0.4,0:00.02,sshd,1517,postfix,20,0,81332,4168,3032,S,0.0,0.3,0:00.00,local,1515,postfix,20,0,81176,3604,2684,S,0.0,0.3,0:00.00,cleanup,1376,postfix,20,0,81100,3580,2656,S,0.0,0.3,0:00.00,qmgr,1375,postfix,20,0,81032,3484,2580,S,0.0,0.3,0:00.00,pickup
201906051326,0.00,0.00,0.00,1274,mysql,20,0,368m,27m,5048,S,0.0,2.3,0:00.17,mysqld,1382,root,20,0,255m,9144,3744,S,0.0,0.8,0:00.04,httpd,1391,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1392,daemon,20,0,591m,9132,1336,S,0.0,0.8,0:00.00,httpd,1390,daemon,20,0,591m,9128,1332,S,0.0,0.8,0:00.00,httpd,1735,root,20,0,102m,4580,3544,S,0.0,0.4,0:00.06,sshd,1517,postfix,20,0,81332,4168,3032,S,0.0,0.3,0:00.00,local,1515,postfix,20,0,81176,3604,2684,S,0.0,0.3,0:00.00,cleanup,1376,postfix,20,0,81100,3580,2656,S,0.0,0.3,0:00.00,qmgr,1375,postfix,20,0,81032,3484,2580,S,0.0,0.3,0:00.00,pickup
- vmstatコマンド
[root@localhost WebAP03]# cat cpu.csv
201906051322,0,0,2,5,91,1,0
201906051323,0,0,1,3,95,1,0
201906051324,0,0,1,3,96,1,0
201906051325,0,0,1,2,97,0,0
201906051326,0,0,1,2,97,0,0
- netstatコマンド
[root@localhost WebAP03]# cat session.csv
201906051322,0,1,0
201906051323,0,1,0
201906051324,0,1,0
201906051325,0,1,0
201906051326,0,1,0
Chart.jsでCSVをグラフ化
csvデータをChart.jsで表示する方法はとてもカンタンです。
上記のように蓄積されたcsvデータを配列に変換し、そのデータをChart.jsに渡すだけ。
①csv=>配列にする関数を用意
function csv2Array(str) {
let csvData = [];
let lines = str.split("\n");
for (let i = 0; i < lines.length; ++i) {
let cells = lines[i].split(",");
csvData.push(cells);
}
return csvData;
}
②Chart.jsの描画用関数を用意(下記コードはcpuの円グラフ表示用)
html側ではcanvasを用意するだけです。
<canvas id="chart1"></canvas>
let chart1;
function drawCpuPieChart(cpudata) {
let setLabels = [], setData1 = [], setData2 = [], setData3 = [], setData4 = [], setData5 = [], setData6 = [], setData7 = [];
for (let row in cpudata) {
setLabels.push(cpudata[row][0]);
setData1.push(cpudata[row][1]);
setData2.push(cpudata[row][2]);
setData3.push(cpudata[row][3]);
setData4.push(cpudata[row][4]);
setData5.push(cpudata[row][5]);
setData6.push(cpudata[row][6]);
setData7.push(cpudata[row][7]);
};
let dateLabels = setLabels.map(function(n) {
return n.substr(-4, 2) + ':' + n.substr(-2);
});
let cpuPieChartData = cpudata.filter(function(x) {
let setTime = document.getElementById('set-time').innerHTML;
return x[0].substr(-4) === setTime.replace(':', '');
});
let ctx1 = document.getElementById("chart1").getContext("2d");
ctx1.canvas.height = 360;
if (cpuPieChartData.length === 0) {
ctx1.font = 'bold 20px sans-serif';
ctx1.textAlign = 'center';
ctx1.fillText('No Data', 170, 200);
} else {
if (chart1) { chart1.destroy(); }
chart1 = new Chart(ctx1, {
type: 'doughnut',
data: {
labels: ["cs","sy","id","wa","st"],
datasets: [{
backgroundColor: [
"#FF6384",
"#FF9F40",
"#FFCD56",
"#4BC0C0",
"#36A2EB"
],
data: [
cpuPieChartData[0][3],
cpuPieChartData[0][4],
cpuPieChartData[0][5],
cpuPieChartData[0][6],
cpuPieChartData[0][7]
]
}]
},
options: {
events: [],
responsive: true,
maintainAspectRatio: false,
title: {
display: true,
fontSize: 18,
text: 'CPU使用率(サーバ単位)'
},
layout: {
padding: {
bottom: 50,
}
}
}
});
}
}
chart1 = new Chart~ の部分でChartオブジェクトをインスタンス化し、引数にデータやグラフのカタチ等を定義することで、様々なグラフを描画できるんです!
注意点として、グラフのデータ更新時(日時変更や時間帯変更時など)if (chart1) { chart1.destroy(); }の部分で既存のオブジェクトがあった場合に明示的に破棄しているところです。
この対応を入れておかないとメモリリークを起こしてしまいます。
(メモリリークが原因でこれを作ったのにこれでメモリリークが起きたら本末転倒。。)
このほかにも、データだけを更新する方法もあるみたいです。
このほうがいいと思われます。
chart.jsのデータ更新でメモリリークが発生する問題と解決方法
③XHRでcsvデータをGETする
function createCpuPieGraph() {
const req = new XMLHttpRequest();
const cpuGraphPieFilePath = './csv/'+ year + '/' + year + '_' + setMonth + '/' + setMonth + setDay + '/' + targetServer + '/cpu.csv';
req.open("GET", cpuGraphPieFilePath, true);
req.onload = function() {
cpudata = csv2Array(req.responseText);
drawCpuPieChart(cpudata);
}
req.send(null);
}
createCpuPieGraph();
あとはcsvファイルをGETし、①で作ったcsvを配列に変換する関数を通して、その結果を②のグラフ描画関数に渡すだけ!
※パス内の変数はセレクトボックスなどでサーバや日付を変更するためあります。
結果、以下のような円グラフが表示されます。
リアルタイムボタン設置
リアルタイムとは名ばかりの、一分毎に最新情報に更新するボタンを設置しました。
実際に使っているシステムでは当日の現在時刻のグラフを表示してくれますが、デモサイトは2月20日~22日の3日分のcsvのみですので、そのcsv内のボタンを押した環境の時刻時点が表示されます
ソースコードは以下です。
// Real time button
const rtbtn = document.getElementById('rtbtn');
const statusTxt = document.querySelector('.rtstatus_btn');
function realTimeJob() {
let date = new Date();
let hour = date.getHours();
let minute = date.getMinutes();
if(hour.toString().length == 1) hour = '0' + hour;
if(minute.toString().length == 1) minute = '0' + minute;
eTime.value = hour + ':' + minute;
createCpuPieGraph();
createCpuGraph();
createProcessGraph();
createMemoryGraph();
createSessionGraph();
timeMater.value = (hour*60) + minute-1;
materSet.innerHTML = hour + ':' + minute;
chart1TimeSet.innerHTML = hour + ':' + minute;
processTimeSet.innerHTML = hour + ':' + minute;
}
let timer;
function timerStart() {
timer = setInterval(realTimeJob, 60000);
}
function timerEnd() {
clearInterval(timer);
}
rtbtn.addEventListener('click', function(){
rtbtn.classList.toggle('on');
if (rtbtn.classList.contains('on')) {
statusTxt.textContent = 'ON';
timerStart();
} else {
statusTxt.textContent = 'OFF';
timerEnd();
}
});
7.まとめ
今回、Chart.jsを初めて使ってみましたがとても分かりやすく見やすいグラフをさくっと作ることができました。
今回は単純なリソース情報をcsvから表示しただけですが、もっといろんなグラフ形式があるしアニメーションもたくさんあるので、データを可視化したいときは積極的に使っていきたいと思います。