
В предыдущей статье мы создали базовую структуру нашего плагина. Теперь пришло время реализовать класс рендеринга для каждого из наших виджетов.
Напомним, что все провайдеры виджетов должны реализовывать интерфейс 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 -iKernel Interface tableIface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flgeth0 1500 0 5727 0 0 0 3400 0 0 0 BMRUeth1 1500 0 5004 0 0 0 2797 0 0 0 BMRUlo 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 BMRUeth1 1500 0 5004 0 0 0 2797 0 0 0 BMRUlo 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,3420eth1,5004,2797lo,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_wait100/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]$ iostatLinux 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.66Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtnsda 0.18 7.62 1.04 157826 21584 |
Четвертая строка содержит данные о состоянии ввода-вывода. Мы заинтересованы в iowaitстоимости в четвертом столбце. Данные из седьмой строки в будущем содержат блок чтения / записи в секунду жесткого диска.
|
1
2
3
|
[vagrant@vagrant-centos64 ~]$ cat /sys/block/sda/queue/physical_block_size512[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, чтобы получить эти метрики. Оставайтесь с нами для следующей части.
Как всегда, обязательно оставьте комментарий, чтобы сообщить нам, что вы думаете о плагине, и если вы настроите его (а также как!).