初始化-安装依赖
This commit is contained in:
253
server/plugin/saiadmin/utils/ServerMonitor.php
Normal file
253
server/plugin/saiadmin/utils/ServerMonitor.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils;
|
||||
|
||||
/**
|
||||
* 服务器监控信息
|
||||
*/
|
||||
class ServerMonitor
|
||||
{
|
||||
/**
|
||||
* 获取内存信息
|
||||
* @return array
|
||||
*/
|
||||
public function getMemoryInfo(): array
|
||||
{
|
||||
$totalMem = 0; // 总内存 (Bytes)
|
||||
$freeMem = 0; // 可用/剩余内存 (Bytes)
|
||||
|
||||
if (stristr(PHP_OS, 'WIN')) {
|
||||
// Windows 系统
|
||||
// 一次性获取 总可见内存 和 空闲物理内存 (单位都是 KB)
|
||||
// TotalVisibleMemorySize: 操作系统可识别的内存总数 (比物理内存条总数略少,更准确反映可用上限)
|
||||
// FreePhysicalMemory: 当前可用物理内存
|
||||
$cmd = 'wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /format:csv';
|
||||
$output = shell_exec($cmd);
|
||||
$output = mb_convert_encoding($output ?? '', 'UTF-8', 'GBK, UTF-8, ASCII');
|
||||
$lines = explode("\n", trim($output));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) continue;
|
||||
|
||||
// CSV 格式: Node,FreePhysicalMemory,TotalVisibleMemorySize
|
||||
$parts = str_getcsv($line);
|
||||
|
||||
// 确保解析正确且排除标题行 (通常索引1是Free, 2是Total)
|
||||
if (count($parts) >= 3 && is_numeric($parts[1])) {
|
||||
$freeMem = floatval($parts[1]) * 1024; // KB -> Bytes
|
||||
$totalMem = floatval($parts[2]) * 1024; // KB -> Bytes
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Linux 系统
|
||||
// 读取 /proc/meminfo,效率远高于 shell_exec('cat ...')
|
||||
$memInfo = @file_get_contents('/proc/meminfo');
|
||||
if ($memInfo) {
|
||||
// 使用正则提取 MemTotal 和 MemAvailable (单位 kB)
|
||||
// MemAvailable 是较新的内核指标,比单纯的 MemFree 更准确(包含可回收的缓存)
|
||||
if (preg_match('/^MemTotal:\s+(\d+)\s+kB/m', $memInfo, $matches)) {
|
||||
$totalMem = floatval($matches[1]) * 1024;
|
||||
}
|
||||
|
||||
if (preg_match('/^MemAvailable:\s+(\d+)\s+kB/m', $memInfo, $matches)) {
|
||||
$freeMem = floatval($matches[1]) * 1024;
|
||||
} else {
|
||||
// 如果内核太老没有 MemAvailable,退化使用 MemFree
|
||||
if (preg_match('/^MemFree:\s+(\d+)\s+kB/m', $memInfo, $matches)) {
|
||||
$freeMem = floatval($matches[1]) * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算已用内存
|
||||
$usedMem = $totalMem - $freeMem;
|
||||
|
||||
// 避免除以0
|
||||
$rate = ($totalMem > 0) ? ($usedMem / $totalMem) * 100 : 0;
|
||||
|
||||
// PHP 自身占用
|
||||
$phpMem = memory_get_usage(true);
|
||||
|
||||
return [
|
||||
// 人类可读格式 (String)
|
||||
'total' => $this->formatBytes($totalMem),
|
||||
'free' => $this->formatBytes($freeMem),
|
||||
'used' => $this->formatBytes($usedMem),
|
||||
'php' => $this->formatBytes($phpMem),
|
||||
'rate' => sprintf('%.2f', $rate) . '%',
|
||||
|
||||
// 原始数值 (Float/Int),方便前端图表使用或逻辑判断,统一单位 Bytes
|
||||
'raw' => [
|
||||
'total' => $totalMem,
|
||||
'free' => $freeMem,
|
||||
'used' => $usedMem,
|
||||
'php' => $phpMem,
|
||||
'rate' => round($rate, 2)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取PHP及环境信息
|
||||
* @return array
|
||||
*/
|
||||
public function getPhpAndEnvInfo(): array
|
||||
{
|
||||
return [
|
||||
'php_version' => PHP_VERSION,
|
||||
'os' => PHP_OS,
|
||||
'project_path' => BASE_PATH,
|
||||
'memory_limit' => ini_get('memory_limit'),
|
||||
'max_execution_time' => ini_get('max_execution_time'),
|
||||
'error_reporting' => ini_get('error_reporting'),
|
||||
'display_errors' => ini_get('display_errors'),
|
||||
'upload_max_filesize' => ini_get('upload_max_filesize'),
|
||||
'post_max_size' => ini_get('post_max_size'),
|
||||
'extension_dir' => ini_get('extension_dir'),
|
||||
'loaded_extensions' => implode(', ', get_loaded_extensions()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取磁盘信息
|
||||
* @return array
|
||||
*/
|
||||
public function getDiskInfo(): array
|
||||
{
|
||||
$disk = [];
|
||||
|
||||
if (stristr(PHP_OS, 'WIN')) {
|
||||
// Windows 系统
|
||||
// 使用 CSV 格式输出,避免空格解析错误;SkipTop=1 跳过空行
|
||||
// LogicalDisk 包含: Caption(盘符), FreeSpace(剩余字节), Size(总字节)
|
||||
$cmd = 'wmic logicaldisk get Caption,FreeSpace,Size /format:csv';
|
||||
$output = shell_exec($cmd);
|
||||
|
||||
// 转换编码,防止中文乱码(视服务器环境而定,通常 Windows CMD 输出为 GBK)
|
||||
$output = mb_convert_encoding($output ?? '', 'UTF-8', 'GBK, UTF-8, ASCII');
|
||||
$lines = explode("\n", trim($output));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) continue;
|
||||
|
||||
// CSV 格式: Node,Caption,FreeSpace,Size
|
||||
$parts = str_getcsv($line);
|
||||
|
||||
// 确保数据列足够且是一个盘符 (例如 "C:")
|
||||
// 索引通常是: 1=>Caption, 2=>FreeSpace, 3=>Size (索引0通常是计算机名)
|
||||
if (count($parts) >= 4 && preg_match('/^[A-Z]:$/', $parts[1])) {
|
||||
$caption = $parts[1];
|
||||
$freeSpace = floatval($parts[2]);
|
||||
$totalSize = floatval($parts[3]);
|
||||
|
||||
// 避免除以 0 错误(如光驱未放入光盘时 Size 可能为 0 或 null)
|
||||
if ($totalSize <= 0) continue;
|
||||
|
||||
$usedSpace = $totalSize - $freeSpace;
|
||||
|
||||
$disk[] = [
|
||||
'filesystem' => $caption,
|
||||
'mounted_on' => $caption,
|
||||
'size' => $this->formatBytes($totalSize),
|
||||
'available' => $this->formatBytes($freeSpace),
|
||||
'used' => $this->formatBytes($usedSpace),
|
||||
'use_percentage' => sprintf('%.2f', ($usedSpace / $totalSize) * 100) . '%',
|
||||
'raw' => [ // 保留原始数据以便前端或其他逻辑使用
|
||||
'size' => $totalSize,
|
||||
'available' => $freeSpace,
|
||||
'used' => $usedSpace
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Linux 系统
|
||||
// -P: POSIX 输出格式(强制在一行显示,防止长挂载点换行)
|
||||
// -T: 显示文件系统类型
|
||||
// 默认单位是 1K-blocks (1024字节)
|
||||
$output = shell_exec('df -TP 2>/dev/null');
|
||||
$lines = explode("\n", trim($output ?? ''));
|
||||
|
||||
// 过滤表头
|
||||
array_shift($lines);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) continue;
|
||||
|
||||
// 限制分割数量,防止挂载点名称中有空格导致解析错位(虽然 -P 很大程度避免了这个问题,但仍需谨慎)
|
||||
$parts = preg_split('/\s+/', $line);
|
||||
|
||||
// df -TP 输出列: Filesystem(0), Type(1), 1024-blocks(2), Used(3), Available(4), Capacity(5), Mounted on(6)
|
||||
if (count($parts) >= 7) {
|
||||
$filesystem = $parts[0];
|
||||
$type = $parts[1];
|
||||
$totalKB = floatval($parts[2]); // 单位是 KB
|
||||
$usedKB = floatval($parts[3]);
|
||||
$availKB = floatval($parts[4]);
|
||||
$mountedOn = $parts[6];
|
||||
|
||||
// 过滤逻辑:只显示物理硬盘或特定挂载点
|
||||
// 通常过滤掉 tmpfs, devtmpfs, overlay, squashfs(snap) 等
|
||||
// 如果你只想看 /dev/ 开头的物理盘,保留原来的正则即可
|
||||
if (!preg_match('/^\/dev\//', $filesystem)) {
|
||||
// continue; // 根据需求决定是否取消注释此行
|
||||
}
|
||||
// 过滤掉 Docker overlay 或 kubelet 等产生的繁杂挂载
|
||||
if (strpos($filesystem, 'overlay') !== false) continue;
|
||||
|
||||
// 转换为字节
|
||||
$totalSize = $totalKB * 1024;
|
||||
$usedSize = $usedKB * 1024;
|
||||
$freeSize = $availKB * 1024;
|
||||
|
||||
if ($totalSize <= 0) continue;
|
||||
|
||||
$disk[] = [
|
||||
'filesystem' => $filesystem,
|
||||
'type' => $type,
|
||||
'mounted_on' => $mountedOn,
|
||||
'size' => $this->formatBytes($totalSize),
|
||||
'available' => $this->formatBytes($freeSize),
|
||||
'used' => $this->formatBytes($usedSize),
|
||||
'use_percentage' => sprintf('%.2f', ($usedSize / $totalSize) * 100) . '%',
|
||||
'raw' => [
|
||||
'size' => $totalSize,
|
||||
'available' => $freeSize,
|
||||
'used' => $usedSize
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化字节为可读格式 (B, KB, MB, GB, TB...)
|
||||
* @param int|float $bytes 字节数
|
||||
* @param int $precision 小数点后保留位数
|
||||
* @return string
|
||||
*/
|
||||
private function formatBytes($bytes, int $precision = 2): string
|
||||
{
|
||||
if ($bytes <= 0) {
|
||||
return '0 B';
|
||||
}
|
||||
$base = log($bytes, 1024);
|
||||
$suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
|
||||
// 确保不会数组越界
|
||||
$class = min((int)floor($base), count($suffixes) - 1);
|
||||
return sprintf("%." . $precision . "f", $bytes / pow(1024, $class)) . ' ' . $suffixes[$class];
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user