PHP 抓取监控报警

wen PHP项目 3

本文目录导读:

PHP 抓取监控报警

  1. 基础系统监控类
  2. Web服务监控类
  3. 数据库监控类
  4. 应用日志监控
  5. 报警通知渠道
  6. 主监控程序
  7. 使用示例
  8. 配置文件

我来为您提供一个完整的PHP监控报警系统示例,包含多种监控场景:

基础系统监控类

<?php
/**
 * 系统资源监控类
 */
class SystemMonitor {
    private $thresholds;
    private $alertChannel;
    public function __construct($thresholds = [], $alertChannel = null) {
        $this->thresholds = array_merge([
            'cpu_usage' => 80,        // CPU使用率阈值
            'memory_usage' => 80,     // 内存使用率阈值
            'disk_usage' => 85,       // 磁盘使用率阈值
            'load_average' => 10      // 负载均衡阈值
        ], $thresholds);
        $this->alertChannel = $alertChannel ?? new AlertChannel();
    }
    /**
     * 检查CPU使用率
     */
    public function checkCpuUsage() {
        $cpuUsage = $this->getCpuUsage();
        $threshold = $this->thresholds['cpu_usage'];
        if ($cpuUsage > $threshold) {
            $this->alertChannel->send("CPU使用率过高: {$cpuUsage}%", 'critical');
        }
        return [
            'metric' => 'cpu',
            'value' => $cpuUsage,
            'threshold' => $threshold,
            'status' => $cpuUsage > $threshold ? 'ALERT' : 'OK'
        ];
    }
    /**
     * 获取CPU使用率
     */
    private function getCpuUsage() {
        $load = sys_getloadavg();
        if (function_exists('proc_open')) {
            $cmd = "top -b -n1 | grep 'Cpu(s)' | awk '{print $2}'";
            $result = exec($cmd);
            return floatval($result);
        }
        return $load[0] * 100;
    }
    /**
     * 检查内存使用率
     */
    public function checkMemoryUsage() {
        $memInfo = file('/proc/meminfo');
        $totalMem = 0;
        $freeMem = 0;
        foreach ($memInfo as $line) {
            if (strpos($line, 'MemTotal:') === 0) {
                $parts = explode(':', $line);
                $totalMem = (int)trim(str_replace('kB', '', $parts[1]));
            } elseif (strpos($line, 'MemAvailable:') === 0) {
                $parts = explode(':', $line);
                $freeMem = (int)trim(str_replace('kB', '', $parts[1]));
            }
        }
        if ($totalMem > 0) {
            $usagePercent = (($totalMem - $freeMem) / $totalMem) * 100;
            $threshold = $this->thresholds['memory_usage'];
            if ($usagePercent > $threshold) {
                $this->alertChannel->send("内存使用率过高: " . round($usagePercent, 2) . "%", 'warning');
            }
            return [
                'metric' => 'memory',
                'value' => round($usagePercent, 2),
                'threshold' => $threshold,
                'status' => $usagePercent > $threshold ? 'ALERT' : 'OK'
            ];
        }
        return null;
    }
    /**
     * 检查磁盘使用率
     */
    public function checkDiskUsage() {
        $diskPath = '/';
        $diskFree = disk_free_space($diskPath);
        $diskTotal = disk_total_space($diskPath);
        if ($diskTotal > 0) {
            $usagePercent = (($diskTotal - $diskFree) / $diskTotal) * 100;
            $threshold = $this->thresholds['disk_usage'];
            if ($usagePercent > $threshold) {
                $this->alertChannel->send("磁盘空间不足: " . round($usagePercent, 2) . "%", 'critical');
            }
            return [
                'metric' => 'disk',
                'value' => round($usagePercent, 2),
                'threshold' => $threshold,
                'status' => $usagePercent > $threshold ? 'ALERT' : 'OK'
            ];
        }
        return null;
    }
    /**
     * 检查负载均衡
     */
    public function checkLoadAverage() {
        $load = sys_getloadavg();
        if ($load[0] > $this->thresholds['load_average']) {
            $this->alertChannel->send("系统负载过高: {$load[0]}", 'warning');
        }
        return [
            'metric' => 'load_average',
            'value' => $load[0],
            'threshold' => $this->thresholds['load_average'],
            'status' => $load[0] > $this->thresholds['load_average'] ? 'ALERT' : 'OK'
        ];
    }
}

Web服务监控类

<?php
/**
 * Web服务监控类
 */
class WebMonitor {
    private $sites = [];
    private $alertChannel;
    public function __construct($sites = [], $alertChannel = null) {
        $this->sites = $sites;
        $this->alertChannel = $alertChannel ?? new AlertChannel();
    }
    /**
     * 检查网站可用性
     */
    public function checkWebsite($url) {
        $startTime = microtime(true);
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => true,
            CURLOPT_NOBODY => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_CONNECTTIMEOUT => 5
        ]);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $responseTime = microtime(true) - $startTime;
        curl_close($ch);
        // 检查HTTP状态码
        if ($httpCode >= 400) {
            $this->alertChannel->send("网站 {$url} 返回错误状态码: {$httpCode}", 'critical');
        }
        // 检查响应时间
        if ($responseTime > 3.0) {
            $this->alertChannel->send("网站 {$url} 响应过慢: " . round($responseTime, 2) . "s", 'warning');
        }
        return [
            'url' => $url,
            'http_code' => $httpCode,
            'response_time' => round($responseTime, 2),
            'status' => $httpCode >= 400 ? 'ALERT' : ($responseTime > 3.0 ? 'WARNING' : 'OK')
        ];
    }
    /**
     * 监控API接口
     */
    public function checkApi($url, $method = 'GET', $data = null) {
        $ch = curl_init($url);
        $headers = [
            'Content-Type: application/json',
            'Accept: application/json'
        ];
        $options = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_CUSTOMREQUEST => $method
        ];
        if ($data && in_array($method, ['POST', 'PUT', 'PATCH'])) {
            $options[CURLOPT_POSTFIELDS] = json_encode($data);
        }
        curl_setopt_array($ch, $options);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        // 解析JSON响应
        $responseData = json_decode($response, true);
        // 检查是否包含错误信息
        if (isset($responseData['error']) || isset($responseData['code']) && $responseData['code'] > 0) {
            $this->alertChannel->send("API {$url} 返回错误: " . json_encode($responseData), 'critical');
        }
        return [
            'url' => $url,
            'http_code' => $httpCode,
            'response' => $responseData,
            'status' => $httpCode >= 400 ? 'ALERT' : 'OK'
        ];
    }
    /**
     * 监控网页内容变化
     */
    public function checkContent($url, $expectedContent) {
        $content = file_get_contents($url);
        if (strpos($content, $expectedContent) === false) {
            $this->alertChannel->send("网站 {$url} 内容异常,未找到预期内容", 'warning');
            return ['status' => 'ALERT', 'message' => 'Content mismatch'];
        }
        return ['status' => 'OK', 'message' => 'Content OK'];
    }
}

数据库监控类

<?php
/**
 * 数据库监控类
 */
class DatabaseMonitor {
    private $pdo;
    private $alertChannel;
    public function __construct($config, $alertChannel = null) {
        try {
            $dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset=utf8mb4";
            $this->pdo = new PDO($dsn, $config['user'], $config['password'], [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_TIMEOUT => 3
            ]);
            $this->alertChannel = $alertChannel ?? new AlertChannel();
        } catch (Exception $e) {
            $this->alertChannel->send("数据库连接失败: " . $e->getMessage(), 'critical');
        }
    }
    /**
     * 检查数据库连接
     */
    public function checkConnection() {
        try {
            $this->pdo->query('SELECT 1');
            return ['status' => 'OK', 'message' => 'Connection successful'];
        } catch (Exception $e) {
            $this->alertChannel->send("数据库连接失败: " . $e->getMessage(), 'critical');
            return ['status' => 'ALERT', 'error' => $e->getMessage()];
        }
    }
    /**
     * 检查表空间使用率
     */
    public function checkTableSpace($tableName) {
        $sql = "SELECT DATA_LENGTH + INDEX_LENGTH as table_size 
                FROM information_schema.TABLES 
                WHERE TABLE_SCHEMA = DATABASE() 
                AND TABLE_NAME = :table";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute(['table' => $tableName]);
        $size = $stmt->fetchColumn();
        return [
            'table' => $tableName,
            'size_mb' => round($size / 1024 / 1024, 2),
            'status' => 'OK'
        ];
    }
    /**
     * 检查慢查询
     */
    public function checkSlowQueries($threshold = 2.0) {
        $sql = "SHOW FULL PROCESSLIST";
        $queries = $this->pdo->query($sql)->fetchAll();
        $slowQueries = array_filter($queries, function($query) use ($threshold) {
            return $query['Time'] > $threshold && !empty($query['Info']);
        });
        if (count($slowQueries) > 0) {
            foreach ($slowQueries as $query) {
                $this->alertChannel->send(
                    "慢查询检测到: SQL='{$query['Info']}' 耗时={$query['Time']}s",
                    'warning'
                );
            }
        }
        return [
            'total_queries' => count($queries),
            'slow_queries' => count($slowQueries),
            'status' => count($slowQueries) > 0 ? 'ALERT' : 'OK'
        ];
    }
    /**
     * 监控连接池使用率
     */
    public function checkConnections() {
        $maxConnections = 100;
        $sql = "SHOW STATUS WHERE Variable_name = 'Threads_connected'";
        $current = $this->pdo->query($sql)->fetchColumn();
        $usagePercent = ($current / $maxConnections) * 100;
        if ($usagePercent > 80) {
            $this->alertChannel->send("数据库连接数过高: {$current}/{$maxConnections}", 'warning');
        }
        return [
            'current_connections' => $current,
            'max_connections' => $maxConnections,
            'usage_percent' => round($usagePercent, 2),
            'status' => $usagePercent > 80 ? 'ALERT' : 'OK'
        ];
    }
}

应用日志监控

<?php
/**
 * 日志监控类
 */
class LogMonitor {
    private $logFile;
    private $alertChannel;
    private $tailCount = 100;
    public function __construct($logFile = '/var/log/php_errors.log', $alertChannel = null) {
        $this->logFile = $logFile;
        $this->alertChannel = $alertChannel ?? new AlertChannel();
    }
    /**
     * 监控错误日志
     */
    public function checkErrorLog($keywords = ['ERROR', 'FATAL', 'CRITICAL']) {
        if (!file_exists($this->logFile)) {
            return ['status' => 'OK', 'message' => 'Log file not found'];
        }
        $lines = $this->tailFile($this->tailCount);
        $matches = [];
        foreach ($lines as $line) {
            foreach ($keywords as $keyword) {
                if (stripos($line, $keyword) !== false) {
                    $matches[] = $line;
                    break;
                }
            }
        }
        if (count($matches) > 0) {
            $this->alertChannel->send(
                "日志中发现错误: " . count($matches) . "条\n" . implode("\n", array_slice($matches, 0, 5)),
                'critical'
            );
        }
        return [
            'total_lines' => count($lines),
            'error_lines' => count($matches),
            'status' => count($matches) > 0 ? 'ALERT' : 'OK'
        ];
    }
    /**
     * 读取文件末尾内容
     */
    private function tailFile($lines) {
        $file = new SplFileObject($this->logFile);
        $file->seek(PHP_INT_MAX);
        $totalLines = $file->key();
        $startLine = max(0, $totalLines - $lines);
        $file->seek($startLine);
        $result = [];
        while (!$file->eof()) {
            $result[] = $file->fgets();
        }
        return $result;
    }
}

报警通知渠道

<?php
/**
 * 报警通知渠道
 */
class AlertChannel {
    private $webhook;
    private $emailConfig;
    public function __construct() {
        // 可以配置多个通知渠道
        $this->webhook = 'https://example.com/webhook'; // Slack/DingTalk/企业微信
        $this->emailConfig = [
            'host' => 'smtp.example.com',
            'port' => 465,
            'username' => 'alert@example.com',
            'password' => 'your_password',
            'to' => 'admin@example.com'
        ];
    }
    /**
     * 发送报警通知
     */
    public function send($message, $level = 'warning') {
        // 1. 发送到Webhook
        $this->sendToWebhook($message, $level);
        // 2. 发送邮件
        $this->sendEmail($message, $level);
        // 3. 发送短信(可选)
        // $this->sendSMS($message, $level);
        // 4. 记录到数据库
        $this->logToDatabase($message, $level);
    }
    /**
     * 发送Webhook通知
     */
    private function sendToWebhook($message, $level) {
        $data = [
            'message' => $message,
            'level' => $level,
            'timestamp' => date('Y-m-d H:i:s'),
            'host' => gethostname()
        ];
        $ch = curl_init($this->webhook);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_HTTPHEADER => ['Content-Type: application/json']
        ]);
        curl_exec($ch);
        curl_close($ch);
    }
    /**
     * 发送邮件通知
     */
    private function sendEmail($message, $level) {
        // 使用PHP内置mail函数或其他邮件库
        // 这里使用简单的示例
        $subject = "[服务器报警] {$level} - " . date('Y-m-d H:i');
        // mail($this->emailConfig['to'], $subject, $message);
    }
    /**
     * 记录到数据库
     */
    private function logToDatabase($message, $level) {
        // 使用PDO连接数据库并记录报警信息
        // 这里简化处理
        $logEntry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'level' => $level,
            'message' => $message,
            'host' => gethostname()
        ];
        // 可以写入数据库或文件
        $logFile = '/var/log/monitor_alerts.log';
        file_put_contents($logFile, json_encode($logEntry) . "\n", FILE_APPEND);
    }
}

主监控程序

<?php
/**
 * 主监控调度器
 */
class MonitorScheduler {
    private $monitors = [];
    private $alerts = [];
    private $runInterval = 60; // 运行间隔(秒)
    public function __construct() {
        $this->initializeMonitors();
    }
    /**
     * 初始化所有监控任务
     */
    private function initializeMonitors() {
        // 系统监控
        $this->monitors['system'] = new SystemMonitor([
            'cpu_usage' => 80,
            'memory_usage' => 80,
            'disk_usage' => 85
        ]);
        // Web监控
        $this->monitors['web'] = new WebMonitor([
            ['url' => 'https://example.com', 'name' => '主站'],
            ['url' => 'https://api.example.com', 'name' => 'API服务']
        ]);
        // 数据库监控
        $this->monitors['database'] = new DatabaseMonitor([
            'host' => 'localhost',
            'dbname' => 'monitor_db',
            'user' => 'monitor',
            'password' => 'monitor_password'
        ]);
        // 日志监控
        $this->monitors['logs'] = new LogMonitor('/var/log/app.log');
    }
    /**
     * 运行监控任务
     */
    public function run() {
        while (true) {
            echo "\n=========== 开始监控 " . date('Y-m-d H:i:s') . " ===========\n";
            // 执行所有监控
            $results = [];
            $results['system_cpu'] = $this->monitors['system']->checkCpuUsage();
            $results['system_memory'] = $this->monitors['system']->checkMemoryUsage();
            $results['system_disk'] = $this->monitors['system']->checkDiskUsage();
            $results['system_load'] = $this->monitors['system']->checkLoadAverage();
            // 打印结果
            $this->printResults($results);
            // 等待下一次运行
            echo "\n等待 {$this->runInterval} 秒后继续监控...\n";
            sleep($this->runInterval);
        }
    }
    /**
     * 打印监控结果
     */
    private function printResults($results) {
        foreach ($results as $name => $result) {
            if ($result && isset($result['status'])) {
                $status = $result['status'];
                $color = $status === 'OK' ? "\033[32m" : ($status === 'ALERT' ? "\033[31m" : "\033[33m");
                $reset = "\033[0m";
                echo "{$color}[{$status}]{$reset} {$name}: ";
                if (isset($result['value'])) {
                    echo "value = " . $result['value'];
                }
                echo "\n";
            }
        }
    }
    /**
     * 单次运行(用于cron定时任务)
     */
    public function runOnce() {
        $this->initializeMonitors();
        // 执行所有监控
        $this->monitors['system']->checkCpuUsage();
        $this->monitors['system']->checkMemoryUsage();
        $this->monitors['system']->checkDiskUsage();
        $this->monitors['system']->checkLoadAverage();
        // Web监控
        foreach ($this->monitors['web']->getSites() as $site) {
            $this->monitors['web']->checkWebsite($site['url']);
        }
        // 数据库监控
        $this->monitors['database']->checkConnection();
        $this->monitors['database']->checkSlowQueries(2);
        // 日志监控
        $this->monitors['logs']->checkErrorLog();
    }
}

使用示例

<?php
// 启动监控程序
require_once 'monitor.php';
// 创建调度器并启动
$scheduler = new MonitorScheduler();
// 方式1:连续运行(适合开发和测试)
// $scheduler->run();
// 方式2:单次运行(配合cron定时任务)
// 在crontab中添加:*/5 * * * * php /path/to/monitor.php
$scheduler->runOnce();

配置文件

<?php
// config.php
return [
    'system' => [
        'cpu_threshold' => 80,
        'memory_threshold' => 80,
        'disk_threshold' => 85
    ],
    'websites' => [
        ['url' => 'https://example.com', 'name' => '主页'],
        ['url' => 'https://api.example.com', 'name' => 'API']
    ],
    'database' => [
        'host' => 'localhost',
        'dbname' => 'monitor',
        'user' => 'monitor_user',
        'password' => 'monitor_password'
    ],
    'alert' => [
        'webhook' => 'https://hooks.slack.com/services/xxx',
        'email' => 'admin@example.com',
        'levels' => ['warning', 'critical']
    ]
];

这个监控系统包含:

  • 系统监控:CPU、内存、磁盘、负载
  • Web监控:网站可用性、响应时间、API状态
  • 数据库监控:连接状态、慢查询、表空间
  • 日志监控:错误日志检测
  • 多通道报警:Webhook、邮件、短信等
  • 定时调度:支持连续运行和cron定时方式

您可以根据实际需求进行调整和扩展。

抱歉,评论功能暂时关闭!