Home  >  Article  >  CMS Tutorial  >  将Transforming a WordPress Server Dashboard into a Widget

将Transforming a WordPress Server Dashboard into a Widget

PHPz
PHPzOriginal
2023-09-03 21:13:12926browse

将Transforming a WordPress Server Dashboard into a Widget

在上一篇文章中,我们创建了插件的基本结构。现在是时候为每个小部件实现渲染类了。

回想一下,所有小部件提供程序都必须实现 Provider 接口。它们还必须位于名为 widget 的文件夹内,并位于名称空间 AX\StatBoard\Widget 下。如果我们想添加一种新的指标,只需创建一个相应的类,并创建一个对象并使用 add_provider 方法将其添加到 Widget 类中。

RAM 使用情况小部件

我们想要显示的第一条信息是当前正在使用的 RAM 量以及当前空闲的 RAM 量。

在本例中,free -m 是我们的朋友 - 它告诉我们 RAM 使用情况。 -m 开关是以兆字节为单位输出结果。

[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

<?php
namespace AX\StatBoard\Widget;

class Ram implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Ram Usage";
  }

?>
接下来,我们将实现 get_metric 来实际运行必要的 shell 命令并提取信息。稍后我将解释 get_metric 的更多细节。
<?php
function get_metric() {
    $df = `free -m | grep -E "(Mem|Swap)" | awk '{print $1, $2, $3, $4}'`;
    $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 "内存|交换" | awk '{print $1, $2, $3, $4}' .它的输出看起来与此类似。

[vagrant@vagrant-centos64 ~]$ free -m | grep -E "Mem|Swap" | awk '{print $1, $2, $3, $4}'
Mem: 589 541 47
Swap: 255 0 255
[vagrant@vagrant-centos64 ~]$
我们通过将行拆分为数组来使用 PHP 解析每一位信息。我们使用 array_map 循环遍历数组的所有元素,对于每一行,我们用空格分隔,然后返回一个包含元素的关联数组:

  • type:第一个字段<p> </p>
  • total:第二个字段<p> </p>
  • used:第三个字段
  • free:第四个字段<p> </p>
现在,是 get_content 的时候了。
 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;
  }
我们使用堆积条形图来显示 RAM 使用情况。

首先,我们调用 get_metric() 来获取必要的数据。然后,我们只需循环它并格式化它以匹配 Google 图表数据要求。最后,我们使用 json_encode 将它们转换为 JavaScript 对象表示法(或 JSON)。然后,我们输出一个 HTML 代码,其中包含 div 元素来保存图表对象。

最后,我们调用相应的 Google Chart API 将图表渲染到 div 元素中。

将Transforming a WordPress Server Dashboard into a Widget

安装的软件

我们将介绍的第二个小部件是显示已安装软件的小部件。它是一个小部件,旨在显示我们在服务器上有哪些常见软件包以及哪个版本。

例如,我们是否安装了 NodeJS,是否安装了 Ruby?我们使用的是哪个版本的 PHP?等等。

让我们使用以下初始内容创建 widget/software.php
<?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>' . $version;
    }
    return $cmds;
  }
因此,与往常一样,我们有 get_title ,它只返回一个简单的字符串。对于 get_metric(),我们想知道是否安装了特定的软件。如果有,则获取其版本信息。

为此,我们使用显示软件版本的开关创建一个命令数组。以PHP为例,php -v 显示版本信息,mysql --version 显示MySQL信息。

如果命令返回且错误或未找到命令,则 shell_exec 将返回 false。此时,我们可以判断该软件没有安装;否则,我们可以解析结果以显示版本信息。然后,我们逐行分割结果,并检索第一行作为版本信息。这是因为我们需要的信息只能在第一行找到。

对于其他应用程序,某些命令过于冗长,包含许多信息。一旦我们有了数据,就可以创建 get_content 方法了。

  public function get_content() {
  
    $cmds = $this->get_metric();
    $content = '';
    
    foreach ( $cmds as $cmd => $info ) {
      $content .= "<p><strong>$cmd</strong>  $info</p>";
    }
    echo $content;
    
  }
我们只是展示此类数据的基本表格。这是显示时的仪表板:

将Transforming a WordPress Server Dashboard into a Widget

磁盘使用情况

现在我们将解决磁盘使用问题。我们将处理此任务的类命名为 Disk。让我们先制作基本骨架。

<?php
namespace AX\StatBoard\Widget;

class Disk implements Provider {
  function __construct() {
  }
  public function get_title() {
    return "Disk Usage";
  }
}
与往常一样,我们必须实现 Provider 接口。我们在这里为我们的小部件设置一个标题。接下来是我们课程的核心:获取磁盘使用情况的方法。

<?php  
  function get_metric() {
    $df = `df -h`;
    $df = explode("\n", $df);
    if (is_array($df) && count($df)>=2) {
      array_shift($df); //Get rid the first line
      $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 输出:

[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.

  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);
    }
  }

我们遍历度量数组并尝试将 MB 空间转换为 GB 空间。我们构建一个数组来匹配图表数据格式要求。数据数组应如下所示:

[  ['File System', 'Free', 'Used',
  ['/dev/sda1', 10, 24],
  ['/dev/sda2', 28, 19]]
一旦我们有了数据,我们就开始渲染图表。我们将制作两个图表:

  1. 第一个图表显示了每个已安装文件系统的总空间。对于此数据,我们将使用饼图。<p> </p>
  2. 第二个图表用于显示每个已安装文件系统的磁盘使用情况。为此,我们将使用条形图。<p> </p>

为此,我们将方法修改为以下内容:

  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 元素来包含信息
      <div id="widget_disk_usage"></div>
      <div id="widget_disk_partion"></div>
然后,使用图表 API 的绘制方法将图表渲染在这些元素内。这里最令人困惑的事情可能是我们图表的数据格式。

结果如下:

将Transforming a WordPress Server Dashboard into a Widget

服务器信息

这个小部件向我们显示信息:Linux 内核、CPU 架构、正常运行时间、IP 地址。我们这里不需要图表,一个简单的数据表就可以完成这项工作。调用该类是Server。这是 widget/server.php

的第一个内容
<?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. -f1 /proc/uptime`;
    
    $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 | grep Mem | awk '{print $2}'`;
    $server['cpu'] =`cat /proc/cpuinfo | grep "model name" | awk '{print $4,$5,$6,$7}'`;

    return $server;
  }

}
至此,您应该熟悉 get_title()。我们只是返回这个小部件的标题。<p> </p>

首先,我们使用语句 use DateTime 因为我们位于名称空间 AX\StatBoard\Widget 中,并且 DateTime 类来自全局命名空间。每次我们想要使用 DateTime 时,我们都必须输入 \DateTime。因此,我们使用命名空间导入来使 DateTime 名称在我们当前的命名空间中可用。

将其视为符号链接。在 get_metric 中,我们运行 shell 命令来获取结果并将其分配回来。

主机名

显示您的服务器主机名。

名称-sr

显示Linux内核信息:

[vagrant@vagrant-centos64 ~]$ uname -sr
Linux 2.6.32-358.23.2.el6.x86_64

grep -c ^处理器/proc/cpuinfo

-c 开关打印输入字符串中匹配行的计数。 /proc/cpuinfo 包含处理器信息。我们 grep 它并计算文字处理器的出现次数。这是我的 32 核结果。

$ grep -c ^processor /proc/cpuinfo
32

剪切-d。 -f1 /proc/uptime

此命令显示服务器已启动并运行的秒数。我们将秒数转换为“x 天 y 小时 z 分钟”的格式,以使其更加用户友好。

使用 DateTime::diff 我们可以轻松实现这一点。我们创建一个带有当前时间戳的 DateTime 对象,另一个带有时间戳的对象是当前时间戳减去正常运行时间的秒数。然后使用 format 方法将其格式化为人类友好的字符串。

这是我的结果,正常运行时间为 26194091 秒。

$ cut -d. -f1 /proc/uptime
26194091

卷曲 ifconfig.me

ifconfig.me 是一项在浏览器内直接访问时显示您的 IP 地址的服务。如果您使用 curl 向其发送请求,它将以单个字符串形式返回您的 IP 地址。

[vagrant@vagrant-centos64 ~]$ curl ifconfig.me
76.102.253.237

CPU型号

如上所述,/proc/cpuinfo存储了CPU信息。我们可以从中提取CPU型号。例如:
[vagrant@vagrant-centos64 ~]$ cat /proc/cpuinfo | grep "model name" | awk '{print $4,$5,$6,$7}'
Intel(R) Core(TM) i5-4250U CPU
一旦我们在数组中获得了所有可用数据,我们就会返回它并向 get_content 方法提供这些数据。这是 get_content,仅显示数据:

  public function get_content() {
    $server = $this->get_metric();
    echo <<<EOD
    <strong>Ip Address</strong> {$server['ip']}<br>
    <strong>CPU</strong>  {$server['cpu']}<br>
    <strong>Number of Core</strong>  {$server['core']}<br>
    <strong>Ram</strong>  {$server['ram']}<br>
    <strong>Hostname</strong> {$server['hostname']}<br>
    <strong>OS</strong> {$server['os']}<br>
    <strong>Uptime</strong> {$server['uptime']}<br>
EOD;
  }
这是仪表板上的小部件。

将Transforming a WordPress Server Dashboard into a Widget

处理器

监控我们的处理器是其中之一我们可以展示的最重要的东西。我们想知道某个特定进程正在使用多少 CPU 和/或消耗了多少内存。我们将我们的类称为 Process,首先从 get_titleget_metric 开始。我将在代码后面解释 get_metric 的更多细节:

<?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 | grep -v '\[' | sort -k 1 -r | head -30 | awk '{print $4,$3,$1,$2,$7,$6,$5}'`;
    $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。如果您尝试该命令,您将得到一些奇怪的过程,例如:

[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]、[migration/0]。基本上,这意味着该命令无法在文件系统中找到。它可能是一些内部系统进程或内核线程,我们可能永远不想关心它。因此,我们应该使用 grep 来消除这些进程。 grep 有 -v 开关使我们能够反转匹配。它返回的结果不包含我们传递给它的字符串。

[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\['
%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
为了使数据看起来不错,我们应该按内存或CPU对进程进行排序。在我们的教程中,我们按 %MEM 排序。我们可以使用Linux的sort命令来做到这一点。 %MEM 是第二列。

就像索引为零的数组一样,第二个元素通过索引键 1 访问。我们可以使用 sort -k 1。它按从最低到最高的顺序排序。我们实际上关心的是首先消耗大量内存的进程。为此,我们应该使用 sort -k 1 -r 反转顺序。一旦我们得到结果,我们可能只需要前30个过程。当然,这取决于您,因为您可以选择包含所有内容,但我想保持简短。 30 听起来是一个合理的数字。

最后,我们使用 awk 来格式化输出。这是我们的命令和示例输出:

[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\[' | sort -k 1 |  head -30 | awk '{print $4,$3,$1,$2,$7,$6,$5}'
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 循环它。我们将同名进程分组到一个元素中,并将 CPU 百分比和内存添加到其中。
<?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_title, get_metric get_content.对于这个过程,我们只想展示一个简单的表格。

我们的 get_content 方法很简单。

  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;
  }
这是我们得到的结果:

将Transforming a WordPress Server Dashboard into a Widget

平均负载

Linux 有一个命令可以显示过去一分钟、五分钟和 15 分钟内 CPU 和 IO 的平均负载。让我们把它压缩成一个小部件。称之为 Cpuload,并创建我们的 widget/cpuload.php

<?php
namespace AX\StatBoard\Widget;

class Cpuload implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "CPU Load";
  }
  function get_metric() {    $number_of_core = intval(`/bin/grep -c processor /proc/cpuinfo`);    $loadAvg = `cat /proc/loadavg | /usr/bin/awk '{print $1,$2,$3}'`;    $loadAvg = explode(' ', $loadAvg);    if ($loadAvg <3) {      return false;    }    $loadTimes = array('1 min', '5 mins', '15 mins');    return array_map(      function ($loadtime, $value, $number_of_core) {        return array($loadtime, round($value * 100 / $number_of_core, 2), $value);      },        $loadTimes,        $loadAvg,        array_fill(0, 3, $number_of_core)      );  }

}
首先,我们通过读取 /proc/cpuinfo 来统计 CPU 核心数,并统计包含“processor”一词的行数。我们在服务器信息

部分对此进行了介绍。

在Linux中,/proc/loadavg保存平均负载信息。前三列分别是1分钟、5分钟和15分钟的负载。 awk 在这里用于过滤掉我们需要的字段。
➜  ~  cat /proc/loadavg
0.01 0.04 0.05 1/217 16089
➜  ~  cat /proc/loadavg | awk '{print $1, $2, $3}'
0.01 0.04 0.05
将上面的结果用空格分割,构建一个包含三个元素的数组。计算所有核心的平均负载。因此,为了获得结果,我们使用 array_map 循环遍历 $loadAvg 数组,并除以我们拥有的核心数。请注意,我们创建了 2 个与 $loadAvg 长度相同的额外数组,一个用于键,另一个用于保存核心数,以便将所有这些一一传递给 array_map

get_content 的时间:

  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 表示法数组。

例如:

[ ["Duration","% Load"], ["1 min",20], ["5 mins",11], ["15 mins",3]]
这是渲染图表时的结果:

将Transforming a WordPress Server Dashboard into a Widget

以太网接口

我们要处理的下一个小部件是以太网接口。某些服务器可以有多个以太网接口,并为其分配不同的 IP 地址。

看到这些信息非常有用。我们将这个类称为 Ethernet,从 widget/ethernet.php 的基本内容开始。

<?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 | /bin/grep -B1 "inet addr" | /usr/bin/awk \'' .
        '{ if ( $1 == "inet" ) { print $2 }' .
        'else if ( $2 == "Link" ) { printf "%s:",$1 } }\' | /usr/bin/awk' .
        ' -F: \'{ print $1","$3 }\''
      );
      $output = trim($output, " \n");
      $output = `ifconfig | grep "Link encap" | awk '{ print $1 }'`;
      $interfaces = explode("\n", $output);
      $output = `ifconfiga | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'`;
      $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);
  }

}

所以小部件的标题将是以太网。对于 get_metric,我们将尝试获取所有以太网接口名称,然后获取每个接口的 IP 地址,并将设备名称和 IP 地址结合起来返回。

我们需要处理两种情况:如果服务器使用 ifconfig 或服务器使用 ip 实用程序。较新的服务器很可能有 ip 而不是 ifconfig; 因此,我们应该首先运行 ip 来获取以太网设备。
$output = shell_exec("ip -oneline link show | awk '{print $2}' | sed 's/://'");

使用 IP 实用程序

带有 ip 命令和 -oneline 将仅在一行中显示输出,其中 link show 将列出所有设备。我们使用 awk 获取第二列,其中包含设备名称;但是它包含 : 字符。我们使用 sed 将 : 替换为空字符串。

这是我们在命令行上运行它们时的结果:

[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 | awk '{print $2}'
lo:
eth0:
eth1:
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show | awk '{print $2}' | sed 's/://'
lo
eth0
eth1
[vagrant@vagrant-centos64 sbin]$
如果 shell_exec 成功运行,我们将继续使用 IP 实用程序。上面的输出被逐行分割成一个数组。
$output = trim($output, " \n");
$interfaces = explode("\n", $output);
然后我们循环遍历该数组,并再次使用 ip 命令 ip -oneline -family inet addr show device_name 来获取分配给设备的 IP 地址。
    $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 获取第一个元素。

查看输出,看看当我们在命令行上运行它们时,它们是如何工作的:

[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 | awk '{print $4}'
192.168.1.111/24
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 | awk '{print $4}' | cut -d'/' -f1
192.168.1.111
[vagrant@vagrant-centos64 sbin]$
当我们在接口数组中获取设备名称并在地址数组中获取 IP 地址时,我们创建一个关联数组,其中接口名称作为键,IP 地址作为值。
return array_combine($interfaces, $addreses);

使用 ifconfig 的服务器

在 ifconfig 的情况下,ipshell_exec 将返回 false。在这种情况下,我们改为运行 ifconfig。

我们上面介绍的逻辑完全相同 - 它只是获取信息的不同实用程序。
    if (!$output) { // It didn't work with "ip" , so we do it with ifconfig
      $output = `ifconfig | grep "Link encap" | awk '{ print $1 }'`;
      $interfaces = explode("\n", $output);
      $output = `ifconfig | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'`;
      $addreses = explode("\n", $output);
      $output = trim($output, " \n");
      return array_combine($interfaces, $addreses);
    }
让我们在终端中运行上面的命令,以便我们了解发生了什么。

请注意,ifconfig 直接在输出中显示 IP 地址,因此我们可以直接获取它们,而不是在设备数组上运行循环并获取每个设备的 IP 地址。

[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 | grep "Link encap"
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 | grep "Link encap" | awk '{ print $1 }'
eth0
eth1
lo

[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr"
          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 | grep "inet addr" | awk '{ print $2 }'
addr:10.0.2.15
addr:192.168.1.111
addr:127.0.0.1
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'
10.0.2.15
192.168.1.111
127.0.0.1
[vagrant@vagrant-centos64 sbin]$
一旦我们有了数据,将它们放入 get_content 中就很容易了,因为我们在这里只显示了一个简单的表格。

  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;
  }
以下是它向管理员显示的方式:

将Transforming a WordPress Server Dashboard into a Widget

网络流量

网络流量或网络 IO 显示通过计算机网络传输包的状态。该信息是从 netstat 中提取的。

[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
让我们在文件 widget/networkio.php 中获取我们的基本类 Networkio

<?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() {    $ethernet = array();       $output = `netstat -i | grep -v -E '(Iface|Interface)' | awk '{print $1","$4","$8}'`;    $lines = explode("\n", $output);    foreach ($lines as $line) {      $line = explode(',', $line);      if (count($line)<3) {        continue;      }      $ethernet[] = array($line[0], intval($line[1]), intval($line[2]));    }    return $ethernet;  }}

我将在本文后面解释它们。现在,让我们尝试评估我们在上面代码中使用的命令。

我将尝试运行多个命令,以便在我们将结果传递到另一个管道后您可以理解其中的差异。

[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]$
netstat 返回很多东西,我们使用 grep 来排除包含单词 IfaceKernel 的行(前两行),我们只关心最后三行是我们的以太网设备及其包传输。 awk 用于打印出第一、四、八列的数据,分别表示接口名称、RX-OK、TX-OK。

在我们的get_metric中,我们将结果逐行拆分到一个数组中。因为每一行都包含用逗号分隔的数据,所以它们被再次分割成一个数组。

我们确保只接受具有三个元素的数组。我们还尝试将数字字符串转换为整数值。数据将依次输入 get_content

  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;

  }
我们之前使用了条形图,并尝试将指标数据数组格式化为条形图数据格式,然后进行渲染。

数据数组的每一行代表一个组条形图,其中包含条形图名称及其相应的值。在我们的例子中,每一行都是接口名称以及 RX 栏和 TX 栏。

这是我们得到的:

将Transforming a WordPress Server Dashboard into a Widget

输入/输出统计

现在,我们处理 io stat。 IO 表示输入/输出。我们将了解每秒执行多少次读/写操作。我们还处理 io_wait。 IO等待是CPU空闲等待从硬盘读取结果的时间。

比如你正在读取MySQL数据,CPU会空闲下来等待结果。 io wait 按1秒或1000毫秒计算。如果您的代码需要 100 毫秒从硬盘读取数据,则 io_wait 为 100/1000 = 10%。 IO 等待越少,系统性能越好。

为了继续执行此操作,请确保系统上有 sysstat 软件包。

  1. 对于 Arch Linux,使用 pacman -S sysstat 安装 <p> </p>
  2. 对于 Debian/Ubuntu,您可以使用 apt-get install sysstat 获取它们<p> </p>
  3. 对于 Fedora/Centos,您可以使用 yum install sysstat
  4. 对于其他发行版,:请使用您的发行版包管理器进行安装

安装完成后,让我们评估一下我们将使用的一些命令。首先是第一件事:

[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
第四行包含IO状态数据。我们对第四列中的 iowait 值感兴趣。从第七行开始的数据包含硬盘驱动器每秒的读/写块。

如果您有许多硬盘连接到服务器,您将拥有多个设备:sdb、sdc 等。

数据是块的数量,而不是实际大小(以兆字节为单位)。我们必须找到块大小来计算总大小。

块大小存储在/sys/block/sda/queue/physical_block_size中。

[vagrant@vagrant-centos64 ~]$ cat /sys/block/sda/queue/physical_block_size
512
[vagrant@vagrant-centos64 ~]$
所以我的sda的块大小是512。我们将每秒读取的块数与块大小相乘,得到读/写数据的实际大小。

有了上面的基本知识,让我们在 widget/iostat.php 中创建我们的类 Iostat

<?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 的输出,将其转换为数组,每一行都是一个元素。

第四行被空格分割并映射到关联数组中。第七行之后的所有行都放入另一个关联数组中,其中键是设备名称(sda,sdb,sdc,..),如果以兆字节为单位读取和写入,则值是一个数组。

一旦我们获得了指标,将其输入 get_content 并渲染我们的图表。

  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;

  }
对于磁盘 IO 读写,我们使用了条形图。对于 IO 等待,我们使用仪表图来使其看起来更漂亮。我们认为 80-100 的 IOwait 是一个严重事件,并用红色突出显示。

将Transforming a WordPress Server Dashboard into a Widget

The above is the detailed content of 将Transforming a WordPress Server Dashboard into a Widget. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn