В предыдущей статье мы создали базовую структуру нашего плагина. Теперь пришло время реализовать класс рендеринга для каждого из наших виджетов.
Напомним, что все провайдеры виджетов должны реализовывать интерфейс Provider
. Они также должны находиться внутри папки с именем widget
и под пространством имен AX\StatBoard\Widget
. Если мы хотим добавить метрику нового типа, просто создайте соответствующий класс, создайте объект и добавьте его в класс Widget
с add_provider
метода add_provider
.
Виджет использования ОЗУ
Одной из первых частей информации, которую мы хотим отобразить, является объем оперативной памяти, используемой в настоящее время, и объем свободной памяти.
В этом случае free -m
— наш друг — он говорит нам об использовании оперативной памяти. ключ -m
для вывода результата в мегабайтах.
1
2
3
4
5
|
[vagrant@vagrant-centos64 ~]$ free -m
total used free shared buffers cached
Mem: 589 366 223 0 9 57
-/+ buffers/cache: 299 290
Swap: 0 0 0
|
Мы назовем класс Ram
. Соответствующий файл будет widget/ram.php
. Мы просто составляем основную часть и реализуем здесь get_title
.
01
02
03
04
05
06
07
08
09
10
11
12
|
<?php
namespace AX\StatBoard\Widget;
class Ram implements Provider {
function __construct() {
}
public function get_title() {
return «Ram Usage»;
}
?>
|
Далее мы реализуем get_metric
для фактического запуска необходимой команды оболочки и извлечения информации. Я объясню более подробно get_metric
позже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?php
function get_metric() {
$df = `free -m |
$df = explode(«\n», $df);
if ( is_array( $df ) && 2 <= count( $df ) ) {
$df = array_map( function ( $line ) {
if ( empty( $line ) ) {
return;
}
$segment = preg_split( ‘/\s+/’, $line );
return array(
‘type’ => trim( $segment[0],» :» ),
‘total’ => (int)$segment[1],
‘used’ => (int)$segment[2],
‘free’ => (int)$segment[3],
);
}, $df );
return $df;
}
return false;
}
?>
|
Выполняем команду free -m | grep -E "Mem|Swap" | awk '{print $1, $2, $3, $4}'
free -m | grep -E "Mem|Swap" | awk '{print $1, $2, $3, $4}'
free -m | grep -E "Mem|Swap" | awk '{print $1, $2, $3, $4}'
. Его вывод выглядит примерно так.
1
2
3
4
|
[vagrant@vagrant-centos64 ~]$ free -m |
Mem: 589 541 47
Swap: 255 0 255
[vagrant@vagrant-centos64 ~]$
|
Мы разбираем каждый бит информации с помощью PHP, разбивая строки в массив. Мы используем array_map
для циклического array_map
по всему элементу массива и для каждой строки разделяем пробелы, а затем возвращаем ассоциативный массив с элементами:
-
type
: первое поле -
total
: второе поле -
used
: третье поле -
free
: четвертое поле
Теперь пришло время для get_content
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public function get_content() {
$metric = $this->get_metric();
$data = array(
array(‘Type’, ‘Used(MB)’, ‘Free(MB)’)
);
foreach ($metric as $item) {
if (empty($item)) {
continue;
}
if ($item[‘type’] !== ‘Mem’ && $item[‘type’] !== ‘Swap’) {
continue;
}
if ( 0 == ($item[‘free’] + $item[‘used’])) {
continue;
}
$data[] = array(
$item[‘type’],$item[‘used’], $item[‘free’]
);
}
$data = json_encode($data);
echo <<<EOD
<div id=»widget_ram_usage»></div>
<script type=»text/javascript»>
google.setOnLoadCallback(function () {
var data = google.visualization.arrayToDataTable({$data});
var options = {
isStacked: true
};
var chart = new google.visualization.ColumnChart(document.getElementById(‘widget_ram_usage’));
chart.draw(data, options);
})
</script>
EOD;
}
|
Мы использовали гистограмму с накоплением для отображения использования оперативной памяти.
get_metric()
чтобы получить необходимые данные. Затем мы просто зациклим его и отформатируем в соответствии с требованиями к данным диаграммы Google. Наконец, мы используем json_encode
для преобразования их в нотацию объектов JavaScript (или JSON). Затем мы выводим HTML-код, который содержит элемент div
для хранения объекта диаграммы.
div
. Установленное программное обеспечение
Второй виджет, который мы рассмотрим, будет отображать установленное программное обеспечение. Это виджет, который предназначен для того, чтобы показать, какие общие пакеты у нас на сервере и какая версия.
widget/software.php
с этим начальным содержимым:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<?php
namespace AX\StatBoard\Widget;
class Software implements Provider {
function __construct() {
}
public function get_title() {
return «Installed Software»;
}
function get_metric() {
$cmds = array();
$package = array(
‘php’ => ‘-v’,
‘node’ => ‘-v’,
‘mysql’ => ‘-V’,
‘vim’ => ‘—version’,
‘python’ => ‘-V’,
‘ruby’ => ‘-v’,
‘java’ => ‘-version’,
‘curl’ => ‘-V’);
foreach ( $package as $cmd=>$version_query ) {
if ( NULL == $cmds[$cmd] = shell_exec( «which $cmd» ) ) {
$cmds[ $cmd ] = ‘Not installed’;
continue;
}
$version = shell_exec( «$cmd $version_query» );
$version = explode( «\n», $version );
if ( is_array( $version ) ) {
$version = array_shift( $version );
}
$cmds[ $cmd ] .= ‘<br>’ .
}
return $cmds;
}
|
Итак, как всегда, у нас есть get_title
и он просто возвращает простую строку. Для get_metric()
мы хотим знать, установлена ли конкретная часть программного обеспечения или нет. Если так, то получите информацию о его версии.
php -v
показывает информацию о версии, mysql --version
показывает информацию MySQL. shell_exec
возвращает false
если команда возвращает ошибку и команда не найдена. В этом случае мы можем определить, что программное обеспечение не установлено; в противном случае мы можем проанализировать результат, чтобы показать информацию о версии. Затем мы разбиваем результат по строкам и извлекаем первую строку как информацию о версии. Это потому, что нужная нам информация находится только в первой строке. get_content
.
01
02
03
04
05
06
07
08
09
10
11
|
public function get_content() {
$cmds = $this->get_metric();
$content = »;
foreach ( $cmds as $cmd => $info ) {
$content .= «<p><strong>$cmd</strong> $info</p>»;
}
echo $content;
}
|
Мы просто показываем базовую таблицу для такого рода данных. Вот панель инструментов при появлении:
Использование диска
Теперь мы рассмотрим использование диска. Назовем класс, который выполняет эту задачу Disk
. Давайте сначала создадим базовый скелет.
01
02
03
04
05
06
07
08
09
10
|
<?php
namespace AX\StatBoard\Widget;
class Disk implements Provider {
function __construct() {
}
public function get_title() {
return «Disk Usage»;
}
}
|
Как всегда, мы должны реализовать интерфейс Provider
. Мы устанавливаем заголовок для нашего виджета здесь. Следующее является сердцем нашего класса: метод получения использования диска.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?php
function get_metric() {
$df = `df -h`;
$df = explode(«\n», $df);
if (is_array($df) && count($df)>=2) {
array_shift($df);
$df = array_map(function ($line) {
if (empty($line)) {
return NULL;
}
$segment=preg_split(‘/\s+/’, $line);
return array(
‘filesystem’ => $segment[0],
‘size’ => $segment[1],
‘used’ => $segment[2],
‘available’ => $segment[3],
‘use_percent’ => $segment[4],
);
}, $df);
return $df;
}
return false;
}
|
В первой части этой серии мы получили некоторое представление о команде df
поэтому понимание следующей команды должно быть простым:
df
:
1
2
3
4
5
6
7
8
|
[vagrant@vagrant-centos64 ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 7.3G 1.4G 5.6G 20% /
tmpfs 295M 0 295M 0% /dev/shm
/vagrant 60G 55G 4.9G 92% /vagrant
/data/GeoIP 60G 55G 4.9G 92% /data/GeoIP
/var/webapps 60G 55G 4.9G 92% /var/webapps
/var/www/html 60G 55G 4.9G 92% /var/www/html
|
Мы разбиваем его по строкам, чтобы превратить в массив. Мы перебираем каждую строку, разделяем целую строку пробелами, чтобы снова превратить ее в массив. Затем мы просто отображаем значение, чтобы иметь более дружественный, понятный человеку ассоциативный массив. Когда у нас есть эти данные, мы можем поместить их в get_content
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public function get_content() {
$metric = $this->get_metric();
$data = array(
array( ‘Disk’, ‘Space’ )
);
$disk_container = array();
$data_partition = array(
array(‘Filesystem’, ‘Free(GB)’, ‘Used(GB)’)
);
foreach ( $metric as $disk ) {
$size = intval( $disk[‘size’] );
if ( ‘M’ == substr( $disk[‘size’], -1 ) ) {
$size = round( $size / 1024, 2 );
}
$used = intval( $disk[‘used’] );
if (‘M’ == substr( $disk[‘used’], -1 ) ) {
$used = round( $used / 1024, 2 );
}
if ( empty( $size ) ) {
continue;
}
$data[] = array( $disk[‘filesystem’], $size );
$data_partition[] = array($disk[‘filesystem’], $size — $used, $used);
}
}
|
Мы перебираем метрический массив и пытаемся преобразовать пространство в МБ в ГБ. Мы строим массив в соответствии с требованиями формата данных диаграммы. Массив данных должен выглядеть так:
[
['Файловая система', 'Свободно', 'Использовано', ['/ dev / sda1', 10, 24], ['/ dev / sda2', 28, 19]
]
Как только у нас есть данные, мы начинаем отображать графики. Мы сделаем две диаграммы:
- Первая диаграмма показывает пространство каждой смонтированной файловой системы в целом. Для этих данных мы будем использовать круговую диаграмму.
- Вторая диаграмма предназначена для отображения использования диска каждой отдельной смонтированной файловой системой. Для этого мы будем использовать гистограмму.
Для этого давайте изменим наш метод следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
public function get_content() {
$metric = $this->get_metric();
$data = array(
array(‘Disk’, ‘Space’)
);
$disk_container = array();
$data_partition = array(
array(‘Filesystem’, ‘Free(GB)’, ‘Used(GB)’)
);
foreach ($metric as $disk) {
$size = intval($disk[‘size’]);
if (‘M’ == substr($disk[‘size’], -1)) {
$size = round($size / 1024, 2);
}
$used = intval($disk[‘used’]);
if (‘M’ == substr($disk[‘used’], -1)) {
$used = round($used / 1024, 2);
}
if (empty($size)) {
continue;
}
$data[] = array($disk[‘filesystem’], $size);
$data_partition[] = array($disk[‘filesystem’], $size — $used, $used);
}
$data = json_encode($data);
$data_partition = json_encode($data_partition);
echo <<<EOD
<div id=»widget_disk_usage»></div>
<div id=»widget_disk_partion»></div>
<script type=»text/javascript»>
google.load(«visualization», «1», {packages:[«corechart»]});
google.setOnLoadCallback(function () {
var data = google.visualization.arrayToDataTable({$data});
var options = {
is3D: true,
};
var chart = new google.visualization.PieChart(document.getElementById(‘widget_disk_usage’));
chart.draw(data, options);
var data2 = google.visualization.arrayToDataTable({$data_partition});
var options2 = {
isStacked: true
};
var chart2 = new google.visualization.ColumnChart(document.getElementById(‘widget_disk_partion’));
chart2.draw(data2, options2);
})
</script>
EOD;
}
|
Мы создали два элемента div
для хранения информации
1
2
|
<div id=»widget_disk_usage»></div>
<div id=»widget_disk_partion»></div>
|
Затем диаграмма отображается внутри этого элемента с помощью метода draw API диаграммы. Самым запутанным здесь может быть формат данных для нашего графика.
Информация о сервере
Этот виджет показывает нам информацию: ядро Linux, архитектура процессора, время работы, IP-адрес. Нам не нужна диаграмма здесь, простая таблица данных делает работу. Вызов класса является Server
. Вот первый контент для widget/server.php
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<?php
namespace AX\StatBoard\Widget;
use DateTime;
class Server implements Provider {
function __construct() {
}
public function get_title() {
return «Server Info»;
}
/**
* Return server info: OS, Kernel, Uptime, and hostname
* @return array with 3 metric:
* * hostname
* * os
* * uptime
*/
function get_metric() {
$server = array();
$server[‘hostname’] = `hostname`;
$server[‘os’] = `uname -sr`;
$server[‘core’] = `grep -c ^processor /proc/cpuinfo`;
$total_uptime_sec = time() — `cut -d.
$now = new DateTime(«now»);
$server[‘uptime’] = $now->diff(new DateTime(«@$total_uptime_sec»))->format(‘%a days, %h hours, %i minutes and %s seconds’);
// Get the external ip with ifconfig.me, a website that show you ip address in plaintext
// when sending request with curl header
$server[‘ip’] = `curl ifconfig.me`;
$server[‘ram’] = `free -m |
$server[‘cpu’] =`cat /proc/cpuinfo |
return $server;
}
}
|
К этому моменту вы должны быть знакомы с get_title()
. Мы просто возвращаем название этого виджета.
use DateTime
потому что мы находимся внутри пространства имен AX\StatBoard\Widget
а класс DateTime
— из глобального пространства имен. Каждый раз, когда мы хотим использовать DateTime
мы должны набирать \DateTime
. Поэтому мы используем импорт пространства имен, чтобы сделать имя DateTime
доступным внутри нашего текущего пространства имен. get_metric
мы запускаем команду оболочки, чтобы получить результат и просто присвоить его обратно. имя хоста
Покажите имя вашего сервера.
uname -sr
Показать информацию о ядре Linux:
1
2
|
[vagrant@vagrant-centos64 ~]$ uname -sr
Linux 2.6.32-358.23.2.el6.x86_64
|
grep -c ^ процессор / proc / cpuinfo
Ключ -c выводит количество совпадающих строк во входной строке. /proc/cpuinfo
содержит информацию о процессоре. Мы grep это и подсчитать возникновение текстового процессора. Вот мой результат с 32 ядрами.
1
2
|
$ grep -c ^processor /proc/cpuinfo
32
|
вырезать -f1 / proc / uptime
Эта команда показывает, сколько секунд сервер был запущен и работает. Мы конвертируем эти числа секунд в формат «x день y час z минута», чтобы сделать его более удобным для пользователя.
Используя DateTime :: diff, мы можем легко добиться этого. Мы создаем объект DateTime
с текущей отметкой времени, а другой объект с отметкой времени — это текущая отметка времени минус число секунд времени безотказной работы. Затем с помощью метода форматирования отформатируйте его в удобную для человека строку.
Вот мой результат с временем безотказной работы 26194091 секунд.
1
2
|
$ cut -d.
26194091
|
curl ifconfig.me
ifconfig.me
— это сервис, который показывает ваш IP-адрес при посещении прямо в браузере. Если вы отправляете запрос с помощью curl
, он возвращает ваш IP-адрес в виде одной строки.
1
2
|
[vagrant@vagrant-centos64 ~]$ curl ifconfig.me
76.102.253.237
|
Модель процессора
Как уже упоминалось выше, /proc/cpuinfo
хранит информацию о процессоре. Мы можем извлечь модель процессора из него. Например:
1
2
|
[vagrant@vagrant-centos64 ~]$ cat /proc/cpuinfo |
Intel(R) Core(TM) i5-4250U CPU
|
Как только у нас есть все данные, доступные в массиве, мы возвращаем их и get_content
методу get_content
эти данные. Вот get_content
, просто показывающий данные:
01
02
03
04
05
06
07
08
09
10
11
12
|
public function get_content() {
$server = $this->get_metric();
echo <<<EOD
<strong>Ip Address</strong> {$server[‘ip’]}<br>
<strong>CPU</strong>
<strong>Number of Core</strong>
<strong>Ram</strong>
<strong>Hostname</strong> {$server[‘hostname’]}<br>
<strong>OS</strong> {$server[‘os’]}<br>
<strong>Uptime</strong> {$server[‘uptime’]}<br>
EOD;
}
|
Вот наш виджет на приборной панели.
процессор
Мониторинг нашего процессора — одна из самых важных вещей, которые мы можем отобразить. Мы хотим знать, сколько ЦП используется и / или объем памяти, потребляемый конкретным процессом. Мы вызываем наш класс Process
, начиная с get_title
и get_metric
. Я объясню более подробно get_metric
после кода:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<?php
namespace AX\StatBoard\Widget;
class Process implements Provider {
public function get_title() {
return «Processes»;
}
/**
* Return server info: OS, Kernel, Uptime, and hostname
* @return array with 3 metric:
* * hostname
* * os
* * uptime
*/
function get_metric() {
$processes = array();
$output = `ps -eo pcpu,pmem,pid,user,args,time,start |
$output = explode(«\n», $output);
if (!is_array($output) || count($output)<2) {
return false;
}
array_shift($output);
foreach ($output as $line) {
//$line = preg_split(‘/\s+/’, $line);
$line = explode(‘ ‘, $line);
if (count($line)<6) {
continue;
}
//var_dump($line);
//echo count($line);
if (empty($processes[$line[6]])) {
$processes[$line[6]] = array_combine(array(‘user’, ‘pid’, ‘%cpu’, ‘%mem’,’start’,’time’, ‘command’), $line);
} else {
$processes[$line[6]][‘%cpu’] += $line[2];
$processes[$line[6]][‘%mem’] += $line[3];
}
}
return $processes;
}
}
|
Команда, которая показывает нам, что процесс запущен — это ps . Он дает обширную информацию с ключом -e
так как позволяет нам видеть каждый процесс. Для нашего виджета нам нужно только вытащить наш COU, память, PID, пользователей, аргументы, время и старт.
-o
средним определяемым пользователем форматом, таким как: ps -eo pcpu,pmem,pid,user,args,time,start
. Если вы попробуете эту команду, вы получите странный процесс, такой как:
1
2
3
4
5
|
[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start
%CPU %MEM PID USER COMMAND TIME STARTED
0.0 0.2 1 root /sbin/init 00:00:00 06:50:39
0.0 0.0 2 root [kthreadd] 00:00:00 06:50:39
0.0 0.0 3 root [migration/0] 00:00:00 06:50:39
|
Обратите внимание на [kthread], [igration / 0]. По сути, это означает, что команда не может быть расположена в файловой системе. Это может быть какой-то внутренний системный процесс или поток ядра, и нам, возможно, никогда не захочется об этом заботиться. Поэтому мы должны исключить этот процесс с помощью grep
. grep имеет ключ -v
позволяющий нам инвертировать соответствие. Он возвращает результат, который не содержит строку, которую мы передаем ему.
01
02
03
04
05
06
07
08
09
10
|
[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start |
%CPU %MEM PID USER COMMAND TIME STARTED
0.0 0.2 1 root /sbin/init 00:00:00 06:50:39
0.0 0.1 292 root /sbin/udevd -d 00:00:00 06:50:41
0.0 0.1 811 root /sbin/dhclient -H vagrant-c 00:00:00 06:50:48
0.0 0.2 948 root /sbin/rsyslogd -i /var/run/ 00:00:00 06:50:50
0.0 0.1 966 rpc rpcbind 00:00:00 06:50:50
0.0 0.2 984 rpcuser rpc.statd 00:00:00 06:50:50
0.0 0.0 1011 root rpc.idmapd 00:00:00 06:50:51
0.0 0.2 1073 root /usr/sbin/VBoxService 00:00:00 06:50:51
|
Чтобы данные выглядели хорошо, мы должны отсортировать процесс по памяти или процессору. В нашем уроке давайте рассортируем по %MEM
. Мы можем сделать это с помощью команды сортировки Linux. И %MEM
— это второй столбец.
sort -k 1
. Сортирует от низшего к высшему. Мы на самом деле заботимся о процессе, который сначала потребляет много памяти. Для этого мы должны изменить порядок с помощью sort -k 1 -r
. Как только мы получим результат, нам могут понадобиться только первые 30 процессов. Конечно, это зависит от вас, поскольку вы можете включить все, но я хочу, чтобы это было коротким. 30 звучит как разумное число.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start |
root 1151 0.0 0.0 00:00:00 -d /sbin/udevd
root 1152 0.0 0.0 00:00:00 -d /sbin/udevd
root 292 0.0 0.0 00:00:00 -d /sbin/udevd
root 811 0.0 0.0 vagrant-c -H /sbin/dhclient
root 1 0.0 0.1 06:50:39 00:00:00 /sbin/init
root 2153 0.0 0.1 -q -1 /sbin/dhclient
root 3642 0.0 0.1 00:00:00 -s /usr/sbin/anacron
vagrant 3808 0.0 0.1 pcpu,pmem,pid,user,a -eo ps
vagrant 3810 0.0 0.1 1 -k sort
vagrant 3811 0.0 0.1 00:00:00 -30 head
vagrant 3812 0.0 0.1 $4,$3,$1,$2,$7,$ {print awk
root 948 0.0 0.1 /var/run/ -i /sbin/rsyslogd
rpc 966 0.0 0.1 06:50:50 00:00:00 rpcbind
root 1073 0.0 0.2 06:50:51 00:00:00 /usr/sbin/VBoxService
root 1105 0.0 0.2 06:50:51 00:00:00 /usr/sbin/sshd
root 1121 0.0 0.2 06:50:52 00:00:00 crond
rpcuser 984 0.0 0.2 06:50:50 00:00:00 rpc.statd
496 1088 0.0 0.3 -p -d memcached
vagrant 3544 0.0 0.3 00:00:00 vagrant@pts/0 sshd:
vagrant 3545 0.0 0.3 06:59:27 00:00:00 -bash
root 1113 0.0 1.7 06:50:52 00:00:00 /usr/sbin/httpd
apache 1157 0.0 4.2 06:50:53 00:00:01 /usr/sbin/httpd
apache 3438 0.0 4.2 06:55:39 00:00:01 /usr/sbin/httpd
|
Как только мы получим результат обратно, мы разделим его на массив и пройдемся по нему с помощью foreach
. Мы группируем одноименный процесс в элемент и складываем в него проценты процессора и память.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
<?php
//…
// inside get_content
foreach ( $output as $line ) {
//$line = preg_split( ‘/\s+/’, $line );
$line = explode( ‘ ‘, $line );
if ( 6 > count( $line ) ) {
continue;
}
if ( empty( $processes[ $line[6] ] ) ) {
$processes[ $line[6]] = array_combine( array( ‘user’, ‘pid’, ‘%cpu’, ‘%mem’,’start’,’time’, ‘command’ ), $line );
} else {
$processes[ $line[6] ][‘%cpu’] += $line[2];
$processes[ $line[6] ][‘%mem’] += $line[3];
}
}
//…
|
Мы используем array_combine
для создания ассоциативного массива из двух массивов: один для ключей и один для значений.
get_content
только реализовать метод get_content
. Напомню, что мы должны реализовать метод get_title
: get_title
, get_metric
и get_content
. Для процесса мы хотим показать только простую таблицу. get_content
прост.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public function get_content() {
$processes = $this->get_metric();
$html = ‘<table class=»wp-list-table widefat»><thead><tr>
<th>User</th>
<th>Pid</th>
<th>%CPU</th>
<th>%Mem</th>
<th>Command</th>
</tr></thead><tbody>’;
foreach ($processes as $process) {
$html .= «<tr>
<td>{$process[‘user’]}</td>
<td>{$process[‘pid’]}</td>
<td>{$process[‘%cpu’]}</td>
<td>{$process[‘%mem’]}</td>
<td>{$process[‘command’]}</td>
</tr>»;
}
$html .= ‘</tbody></table>’;
echo $html;
}
|
И вот что мы получаем:
Средняя нагрузка
В Linux есть команда, которая показывает среднюю загрузку процессора и ввода-вывода за последнюю минуту, пять минут и 15 минут. Пусть хрустят в виджет. Назовите его Cpuload
и создайте наш widget/cpuload.php
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<?php
namespace AX\StatBoard\Widget;
class Cpuload implements Provider {
function __construct() {
}
public function get_title() {
return «CPU Load»;
}
function get_metric() {
}
|
Первым делом мы подсчитываем количество ядер процессора, читая /proc/cpuinfo
и подсчитываем количество строк, содержащих слово «процессор». Мы рассмотрели это в разделе « Информация о сервере».
/proc/loadavg
содержит информацию о средней загрузке. Первые три столбца представляют собой нагрузку на одну минуту, пять минут и 15 минут соответственно. Здесь используется awk
, чтобы отфильтровать только те поля, которые нам нужны.
1
2
3
4
5
|
➜ ~ cat /proc/loadavg
0.01 0.04 0.05 1/217 16089
➜ ~ cat /proc/loadavg |
0.01 0.04 0.05
<br> |
Приведенный выше результат разделен пробелами для создания массива из трех элементов. Средняя нагрузка рассчитана для всех ядер. Таким образом, чтобы получить результат, мы $loadAvg
массив $loadAvg
с array_map
и делим на количество ядер, которое у нас есть. Обратите внимание, что мы создаем 2 дополнительных массива такой же длины, что и $loadAvg
, один для ключа, другой для хранения номера ядра, чтобы передать все по одному в обратный вызов array_map
.
get_content
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public function get_content() {
$metrics = $this->get_metric();
if ( ! $metrics ) {
return false;
}
// see https://google-developers.appspot.com/chart/interactive/docs/gallery/barchart#Data_Format for more detai of format
$data = array( array( ‘Duration’, ‘% Load’ ) );
foreach ( $metrics as $key=>$metric ) {
array_push( $data, array( $metric[0], $metric[1] ) );
}
$data = json_encode( $data );
echo <<<EOD
<div id=»avg_load»></div>
<script type=»text/javascript»>
google.load(«visualization», «1», {packages:[«corechart»]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable($data);
var options = {
hAxis: {
titleTextStyle: {color: ‘red’},
minValue:0,
maxValue:100
}
};
var chart = new google.visualization.BarChart(document.getElementById(‘avg_load’));
chart.draw(data, options);
}
</script>
EOD;
}
|
Мы используем гистограмму и создаем массив данных из нашего массива, затем используем json_encode
чтобы превратить его в массив нотации JavaScript, который соответствует формату данных гистограммы.
[
[«Длительность», «% загрузки»],
["1 мин", 20],
["5 минут", 11],
["15 минут", 3]
]
Вот наш результат, когда график отображается:
Интерфейс Ethernet
Следующий виджет, который мы рассмотрим, — для интерфейса Ethernet. Некоторые серверы могут иметь несколько интерфейсов Ethernet с разными IP-адресами.
Просмотр этой информации очень полезен. Давайте назовем этот класс Ethernet
, начнем с этой базовой вещи для widget/ethernet.php
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<?php
/**
* Adopt from https://github.com/afaqurk/linux-dash/blob/master/sh/ip.php
*
*/
namespace AX\StatBoard\Widget;
class Ethernet implements Provider {
function __construct() {
}
public function get_title() {
return «Ethernet»;
}
function get_metric() {
$ethernet = array();
$output = shell_exec(«ip -oneline link show | awk ‘{print $2}’ | sed ‘s/://’»);
if (!$output) { // It didn’t work with «ip» , so we do it with ifconfig
$output = shell_exec(
‘ifconfig |
‘{ if ( $1 == «inet» ) { print $2 }’ .
‘else if ( $2 == «Link» ) { printf «%s:»,$1 } }\’ |
‘ -F: \'{ print $1″,»$3 }\»
);
$output = trim($output, » \n»);
$output = `ifconfig |
$interfaces = explode(«\n», $output);
$output = `ifconfiga |
$addreses = explode(«\n», $output);
$output = trim($output, » \n»);
return array_combine($interfaces, $addreses);
}
$output = trim($output, » \n»);
$interfaces = explode(«\n», $output);
$addreses = array();
foreach ($interfaces as $interface) {
$output = shell_exec(«ip -oneline -family inet addr show $interface | awk ‘{print $4}’ | cut -d’/’ -f1»);
$addreses[] = $output;
}
return array_combine($interfaces, $addreses);
}
}
|
Таким образом, название виджета будет Ethernet . Для get_metric
мы попытаемся получить все имена интерфейсов Ethernet, затем мы получим IP-адрес каждого из них и скомбинируем имя устройства и IP-адрес для возврата.
ip
вместо ifconfig;
поэтому сначала нужно запустить ip, чтобы получить Ethernet-устройства.
1
|
$output = shell_exec(«ip -oneline link show | awk ‘{print $2}’ | sed ‘s/://’»);
|
Использование утилиты IP
Команда ip
с параметром -oneline
покажет вывод только в одной строке, где в виде link
и show
будут перечислены все устройства. Мы используем awk, чтобы получить второй столбец с именами устройств; однако он содержит: char. Мы использовали sed
для замены: пустой строкой.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether 08:00:27:08:c2:e4 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether 08:00:27:eb:11:e4 brd ff:ff:ff:ff:ff:ff
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show |
lo:
eth0:
eth1:
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show |
lo
eth0
eth1
[vagrant@vagrant-centos64 sbin]$
|
Если shell_exec
работает успешно, мы переходим к утилите IP. Приведенный выше вывод разбивается на массив строка за строкой
1
2
|
$output = trim($output, » \n»);
$interfaces = explode(«\n», $output);
|
Затем мы перебираем этот массив и снова используем команду ip
ip -oneline -family inet addr show device_name
чтобы получить IP-адрес, назначенный устройству.
1
2
3
4
5
|
$addreses = array();
foreach ($interfaces as $interface) {
$output = shell_exec(«ip -oneline -family inet addr show $interface | awk ‘{print $4}’ | cut -d’/’ -f1»);
$addreses[] = $output;
}
|
IP-адрес отображается в четвертом столбце, поэтому для вывода этого значения используется awk. Затем мы используем команду cut, чтобы разделить команду на / и получить первый элемент с swich -f1.
1
2
3
4
5
6
7
|
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1
3: eth1 inet 192.168.1.111/24 brd 192.168.1.255 scope global eth1
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 |
192.168.1.111/24
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 |
192.168.1.111
[vagrant@vagrant-centos64 sbin]$
|
Когда мы получаем имя устройства в массиве интерфейсов и IP-адрес в массиве адресов, мы создаем ассоциативный массив с именем интерфейса в качестве ключа и IP-адресом в качестве значения.
1
|
return array_combine($interfaces, $addreses);
|
Серверы, использующие ifconfig
В случае ifconfig shell_exec
ip
вернет false
. В этом случае мы запускаем ifconfig.
1
2
3
4
5
6
7
8
|
if (!$output) { // It didn’t work with «ip» , so we do it with ifconfig
$output = `ifconfig |
$interfaces = explode(«\n», $output);
$output = `ifconfig |
$addreses = explode(«\n», $output);
$output = trim($output, » \n»);
return array_combine($interfaces, $addreses);
}
|
Давайте запустим указанную выше команду в терминале, чтобы иметь представление о том, что происходит.
ifconfig
показывает IP-адрес непосредственно в выходных данных, поэтому мы можем просто получить их вместо запуска цикла по устройствам и получить IP-адрес каждого из них.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
[vagrant@vagrant-centos64 sbin]$ ifconfig
eth0 Link encap:Ethernet HWaddr 08:00:27:08:C2:E4
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe08:c2e4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4230 errors:0 dropped:0 overruns:0 frame:0
TX packets:2575 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:444488 (434.0 KiB) TX bytes:2288676 (2.1 MiB)
eth1 Link encap:Ethernet HWaddr 08:00:27:EB:11:E4
inet addr:192.168.1.111 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:feeb:11e4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4470 errors:0 dropped:0 overruns:0 frame:0
TX packets:2449 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1689803 (1.6 MiB) TX bytes:271675 (265.3 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:264 errors:0 dropped:0 overruns:0 frame:0
TX packets:264 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:15840 (15.4 KiB) TX bytes:15840 (15.4 KiB)
[vagrant@vagrant-centos64 sbin]$ ifconfig |
eth0 Link encap:Ethernet HWaddr 08:00:27:08:C2:E4
eth1 Link encap:Ethernet HWaddr 08:00:27:EB:11:E4
lo Link encap:Local Loopback
[vagrant@vagrant-centos64 sbin]$ ifconfig |
eth0
eth1
lo
[vagrant@vagrant-centos64 sbin]$ ifconfig |
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet addr:192.168.1.111 Bcast:192.168.1.255 Mask:255.255.255.0
inet addr:127.0.0.1 Mask:255.0.0.0
[vagrant@vagrant-centos64 sbin]$ ifconfig |
addr:10.0.2.15
addr:192.168.1.111
addr:127.0.0.1
[vagrant@vagrant-centos64 sbin]$ ifconfig |
10.0.2.15
192.168.1.111
127.0.0.1
[vagrant@vagrant-centos64 sbin]$
|
Когда у нас есть данные, поместить их в get_content
легко, потому что мы просто показываем здесь простую таблицу.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public function get_content() {
$interfaces = $this->get_metric();
$html = ‘<table class=»wp-list-table widefat»><thead><tr>
<th>Interface</th>
<th>IP</th>
</tr></thead><tbody>’;
foreach ( $interfaces as $interface => $ip ) {
$html .= «<tr>
<td>{$interface}</td>
<td>{$ip}</td>
</tr>»;
}
$html .= ‘</tbody></table>’;
echo $html;
}
|
Вот как это выглядит до администратора:
Сетевой трафик
Сетевой трафик, или Сетевой ввод-вывод, показывает состояние передачи пакетов по сети компьютеров. Информация извлечена из netstat .
1
2
3
4
5
6
|
[vagrant@vagrant-centos64 sbin]$ netstat -i
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 0 4828 0 0 0 2933 0 0 0 BMRU
eth1 1500 0 4806 0 0 0 2679 0 0 0 BMRU
lo 16436 0 276 0 0 0 276 0 0 0 LRU
|
Давайте возьмем наш базовый класс Networkio
в файле widget/networkio.php
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<?php
/**
* Adopt from https://github.com/afaqurk/linux-dash/blob/master/sh/ip.php
*
*/
namespace AX\StatBoard\Widget;
class Networkio implements Provider {
function __construct() {
}
public function get_title() {
return «Network IO»;
}
function get_metric() {
|
Я объясню их позже в статье. А сейчас давайте попробуем оценить команду, которую мы использовали в приведенном выше коде.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
[vagrant@vagrant-centos64 sbin]$ netstat -i Kernel Interface table Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg eth0 1500 0 5727 0 0 0 3400 0 0 0 BMRU eth1 1500 0 5004 0 0 0 2797 0 0 0 BMRU lo 16436 0 292 0 0 0 292 0 0 0 LRU [vagrant@vagrant-centos64 sbin]$ netstat -i | grep - v -E '(Iface|Interface)' eth0 1500 0 5736 0 0 0 3405 0 0 0 BMRU eth1 1500 0 5004 0 0 0 2797 0 0 0 BMRU lo 16436 0 292 0 0 0 292 0 0 0 LRU [vagrant@vagrant-centos64 sbin]$ netstat -i | grep - v -E '(Iface|Interface)' | awk '{print $1","$4","$8}' eth0,5760,3420 eth1,5004,2797 lo,292,292 [vagrant@vagrant-centos64 sbin]$ <br><br> |
netstat
возвращая многие вещи, мы использовали grep, чтобы исключить строку, содержащую слова Iface или Kernel (первые две строки), мы заботимся только о последних трех строках с нашими устройствами Ethernet и передачей их пакетов. awk используется для распечатки данных первого, четвертого и восьмого столбцов, означающих название интерфейса, RX-OK и TX-OK.
В нашем случае get_metric
мы разбиваем результат построчно на массив. Поскольку каждая строка содержит данные, разделенные запятой, они снова разделяются на массив.
get_content
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public function get_content() { $interfaces = $this ->get_metric(); $data = array_merge ( array ( array ( 'Interface' , 'Receive(package)' , 'Transfer(package)' )), $interfaces ); $data = json_encode( $data ); echo <<<EOD <div id= "nio_chart" ></div> <script type=»text/javascript»>
google.setOnLoadCallback( function () { var data = google.visualization.arrayToDataTable({ $data }); var options = {
};
var chart = new google.visualization.ColumnChart(document.getElementById( 'nio_chart' )); chart.draw(data, options); })
</script>
EOD; }
|
Ранее мы использовали гистограмму и попытались отформатировать массив метрических данных в формат данных гистограммы, а затем отобразить его.
Статистика ввода / вывода
Теперь мы решаем io stat
. IO означает ввод / вывод. Мы узнаем, сколько операций чтения / записи выполняется в секунду. Мы также занимаемся io_wait
. IO Wait — это время простоя процессора в ожидании результата, который он считывает с жесткого диска.
Например, вы читаете данные MySQL, и процессор будет бездействовать, чтобы дождаться результата. io wait
Рассчитан на 1 секунду или 1000 миллисекунд. Если вашему коду требуется 100 миллисекунд для чтения данных с жесткого диска, то значение io_wait
100/1000 = 10%. Чем меньше ожиданий ввода-вывода, тем выше производительность системы.
Чтобы продолжить, убедитесь, что в вашей системе есть пакет sysstat .
- Для Arch Linux установите
with pacman -S sysstat
- Для Debian / Ubuntu вы можете получить их с
apt-get install sysstat
- Для Fedora / Centos, вы можете использовать
yum install sysstat
- Для других дистрибутивов: используйте менеджер дистрибутива для его установки.
После установки давайте оценим некоторые команды, которые мы будем использовать. Первым делом первым:
1
2
3
4
5
6
7
8
|
[vagrant@vagrant-centos64 sbin]$ iostat Linux 2.6.32-358.23.2.el6.x86_64 (vagrant-centos64.vagrantup.com) 04 /27/2014 _x86_64_ (1 CPU) avg-cpu: %user % nice %system %iowait %steal %idle 0.05 0.00 0.25 0.04 0.00 99.66 Device: tps Blk_read /s Blk_wrtn /s Blk_read Blk_wrtn sda 0.18 7.62 1.04 157826 21584 |
Четвертая строка содержит данные о состоянии ввода-вывода. Мы заинтересованы в iowait
стоимости в четвертом столбце. Данные из седьмой строки в будущем содержат блок чтения / записи в секунду жесткого диска.
1
2
3
|
[vagrant@vagrant-centos64 ~]$ cat /sys/block/sda/queue/physical_block_size 512 [vagrant@vagrant-centos64 ~]$ |
Таким образом, размер блока моего sda равен 512. Мы умножаем число прочитанных блоков в секунду на размер блока, чтобы получить реальный размер данных для чтения / записи.
С базовыми знаниями, давайте создадим наш класс Iostat
в widget/iostat.php
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
<?php
namespace AX\StatBoard\Widget; class Iostat implements Provider { function __construct() {
}
public function get_title() { return "Disk IO"; }
/**
* Make sure we install package sysstat * yum install sysstat * or apt-get install sysstat *
* Return IO Stat information. CPU waiting time, disk read/write *
*/
function get_metric() { $metric = array(); $output = `iostat`; $number_of_core = intval(`/bin/grep -c processor /proc/cpuinfo`); $lines = explode("\n", $output); //We should have more than 4 lines if (!is_array($lines) || sizeof($lines)<4) { return false;
}
$avg_cpu = preg_split("/\s+/", $lines[3]); $metric['cpu'] = array( 'user' => floatval($avg_cpu[0]) * $number_of_core, 'system' => floatval($avg_cpu[2]) * $number_of_core, 'io_wait' => floatval($avg_cpu[3]) * $number_of_core, 'other' => 100 - ($avg_cpu[0] + $avg_cpu[2] + $avg_cpu[3]) );
if (sizeof($lines) >=7) { for ($i=6,$l = sizeof($lines);$i<$l; $i++) { $line = preg_split("/\s+/", $lines[$i]); if (!is_array($line) || sizeof($line)<5) { continue;
}
// Calculate block size $block_size = shell_exec("cat /sys/block/{$lines[1]}/queue/physical_block_size"); $metric['disk'][$line[0]] = array( 'read' => floatval($line[2]) * $block_size / 1024, 'write' => floatval($line[3]) * $block_size / 1024, );
}
}
return $metric; }
}
|
Мы просто пытались реализовать нашу теорию в PHP-коде. Получив вывод iostat
, превратить его в массив с каждой строкой является элементом.
get_content
и представим нашу диаграмму.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public function get_content() { $metric = $this ->get_metric(); $disk_io = array ( array ( 'Disk' , 'Read(MB)' , 'Write(MB)' ), );
foreach ( $metric [ 'disk' ] as $disk => $stat ) { $disk_io [] = array ( $disk , $stat [ 'read' ], $stat [ 'write' ]); }
$disk_io = json_encode( $disk_io ); $cpu_io = json_encode( array ( array ( 'CPU Time' , 'Percent' ), array ( 'IO Wait' , $metric [ 'cpu' ][ 'io_wait' ]), ));
echo <<<EOD <div id= "widget_disk_io" ></div> <div id= "widget_cpu_io_wait" ></div> <script type=»text/javascript»>
google.load( 'visualization' , '1' , {packages:[ 'gauge' ]}); google.setOnLoadCallback( function () { var data = google.visualization.arrayToDataTable({ $cpu_io }); var goptions = { redFrom: 80, redTo: 100, yellowFrom:50, yellowTo: 80, minorTicks: 5 };
var chart = new google.visualization.Gauge(document.getElementById( 'widget_cpu_io_wait' )); chart.draw(data, goptions); var data2 = google.visualization.arrayToDataTable({ $disk_io }); var chart2 = new google.visualization.ColumnChart(document.getElementById( 'widget_disk_io' )); chart2.draw(data2, {}); })
</script>
EOD; }
|
Для чтения и записи дискового ввода-вывода мы использовали гистограмму. Для ввода — вывода ожидания, мы используем калибровочную диаграмму , чтобы сделать его хорошо выглядеть. Мы считаем, что IOwait 80-100 является критическим инцидентом, и выделим его красным.
1
2
3
4
5
|
var goptions = { redFrom: 80, redTo: 100, yellowFrom:50, yellowTo: 80, minorTicks: 5 };
|
Давайте посмотрим на нашу тяжелую работу в реальной жизни:
Что дальше?
На данный момент мы закончили все виджеты для нашего плагина. Вы можете скачать плагин в этом руководстве, чтобы не вводить весь код руководства. Установка плагина, затем обновление панели инструментов и использование опции экрана, чтобы решить, какой виджет вы хотите показать.
Наш плагин прекрасно работает на этом этапе; тем не менее, мы можем пойти дальше, внедрив проверку пользователей и ролей, поскольку данные могут быть конфиденциальными.
Мы также должны использовать cronjob, чтобы получить эти метрики. Оставайтесь с нами для следующей части.
Как всегда, обязательно оставьте комментарий, чтобы сообщить нам, что вы думаете о плагине, и если вы настроите его (а также как!).