本文目录导读:

- 基础被动监控实现方式
- 使用 register_shutdown_function 实现结束监控
- 使用 set_error_handler 错误监控
- 使用 PHP 内置函数监控
- 完整的被动监控系统
- 数据库表结构
- Nginx 配置示例(用于被动日志记录)
- 使用建议
- 优势
- 注意事项
我来详细解释PHP中的被动监控实现方式,被动监控指的是不主动轮询,而是等待事件发生或被调用时进行监控记录。
基础被动监控实现方式
自动记录请求信息
<?php
// 在入口文件(如 index.php)开始处添加
class PassiveMonitor {
private static $instance = null;
private $startTime;
public function __construct() {
$this->startTime = microtime(true);
$this->recordRequest();
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function recordRequest() {
$data = [
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
'uri' => $_SERVER['REQUEST_URI'] ?? '/',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
'execution_time' => 0
];
// 保存到文件或数据库
$this->saveToFile($data);
}
private function saveToFile($data) {
$logFile = __DIR__ . '/monitor/log.txt';
$dir = dirname($logFile);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$line = json_encode($data) . PHP_EOL;
file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
}
}
// 初始化监控
$monitor = PassiveMonitor::getInstance();
?>
使用 register_shutdown_function 实现结束监控
<?php
class ExecutionMonitor {
private $startTime;
private $memoryStart;
public function __construct() {
$this->startTime = microtime(true);
$this->memoryStart = memory_get_usage();
// 注册关闭函数
register_shutdown_function([$this, 'onShutdown']);
// 捕获致命错误
register_shutdown_function([$this, 'handleFatalError']);
}
public function onShutdown() {
$executionTime = microtime(true) - $this->startTime;
$memoryUsed = memory_get_usage() - $this->memoryStart;
$peakMemory = memory_get_peak_usage(true);
$error = error_get_last();
$data = [
'time' => date('Y-m-d H:i:s'),
'url' => ($_SERVER['REQUEST_URI'] ?? 'CLI') . '?' . ($_SERVER['QUERY_STRING'] ?? ''),
'execution_time' => round($executionTime, 4),
'memory_used' => $this->formatBytes($memoryUsed),
'peak_memory' => $this->formatBytes($peakMemory),
'has_error' => $error !== null,
'error_message' => $error['message'] ?? '',
'php_version' => PHP_VERSION
];
$this->logToDatabase($data);
}
public function handleFatalError() {
$error = error_get_last();
if ($error !== null && $error['type'] === E_ERROR) {
// 记录致命错误
$this->logCriticalError($error);
}
}
private function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
return round($bytes / pow(1024, $pow), $precision) . ' ' . $units[$pow];
}
private function logToDatabase($data) {
// 使用PDO记录到数据库
try {
$pdo = new PDO('mysql:host=localhost;dbname=monitor', 'user', 'pass');
$sql = "INSERT INTO request_logs (timestamp, url, execution_time, memory_used, peak_memory, has_error, error_message)
VALUES (:time, :url, :exec_time, :memory, :peak_memory, :has_error, :error_msg)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':time' => $data['time'],
':url' => $data['url'],
':exec_time' => $data['execution_time'],
':memory' => $data['memory_used'],
':peak_memory' => $data['peak_memory'],
':has_error' => $data['has_error'],
':error_msg' => $data['error_message']
]);
} catch (PDOException $e) {
error_log("Monitor DB Error: " . $e->getMessage());
}
}
}
// 初始化
$monitor = new ExecutionMonitor();
?>
使用 set_error_handler 错误监控
<?php
class ErrorMonitor {
private static $errors = [];
public static function init() {
set_error_handler([__CLASS__, 'handleError']);
set_exception_handler([__CLASS__, 'handleException']);
}
public static function handleError($severity, $message, $file, $line) {
$error = [
'type' => self::getErrorType($severity),
'message' => $message,
'file' => $file,
'line' => $line,
'time' => date('Y-m-d H:i:s'),
'url' => $_SERVER['REQUEST_URI'] ?? 'CLI'
];
self::$errors[] = $error;
self::logError($error);
// 根据错误级别决定是否继续执行
if ($severity & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR)) {
return false; // 停止脚本
}
return true;
}
public static function handleException($exception) {
$data = [
'type' => 'Exception',
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
'time' => date('Y-m-d H:i:s')
];
self::logCriticalError($data);
// 显示友好的错误页面
if (!ini_get('display_errors')) {
http_response_code(500);
include 'error_page.php';
}
}
private static function getErrorType($severity) {
$types = [
E_ERROR => 'Fatal Error',
E_WARNING => 'Warning',
E_PARSE => 'Parse Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Strict',
E_RECOVERABLE_ERROR => 'Recoverable Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated'
];
return $types[$severity] ?? 'Unknown Error';
}
private static function logError($error) {
$logFile = __DIR__ . '/errors/error.log';
$dir = dirname($logFile);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$logLine = sprintf(
"[%s] %s: %s in %s:%d\n",
$error['time'],
$error['type'],
$error['message'],
$error['file'],
$error['line']
);
file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);
}
}
// 在脚本开始处调用
ErrorMonitor::init();
?>
使用 PHP 内置函数监控
<?php
class PerformanceMonitor {
private static $marks = [];
public static function mark($name) {
self::$marks[$name] = [
'time' => microtime(true),
'memory' => memory_get_usage()
];
}
public static function getReport() {
$report = [];
$previous = null;
foreach (self::$marks as $name => $data) {
$row = [
'name' => $name,
'time' => date('H:i:s', (int)$data['time']),
'memory' => self::formatBytes($data['memory'])
];
if ($previous) {
$row['time_diff'] = round($data['time'] - $previous['time'], 4) . 's';
$row['memory_diff'] = self::formatBytes($data['memory'] - $previous['memory']);
} else {
$row['time_diff'] = '0s';
$row['memory_diff'] = '0B';
}
$report[] = $row;
$previous = $data;
}
return $report;
}
private static function formatBytes($bytes) {
return round($bytes / 1024, 2) . ' KB';
}
}
// 使用示例
PerformanceMonitor::mark('start');
// ... 业务代码 ...
PerformanceMonitor::mark('after_db_query');
// ... 更多业务代码 ...
PerformanceMonitor::mark('end');
print_r(PerformanceMonitor::getReport());
?>
完整的被动监控系统
<?php
class PassiveMonitorSystem {
private $config;
private $startTime;
public function __construct($config = []) {
$this->config = array_merge([
'log_db' => false,
'log_file' => true,
'log_errors' => true,
'slow_request_threshold' => 2.0, // 秒
'log_memory' => true
], $config);
$this->startTime = microtime(true);
$this->initialize();
}
private function initialize() {
// 注册各种钩子
register_shutdown_function([$this, 'finalize']);
if ($this->config['log_errors']) {
set_error_handler([$this, 'handleError']);
set_exception_handler([$this, 'handleException']);
}
}
public function finalize() {
$executionTime = microtime(true) - $this->startTime;
$data = [
'request_id' => uniqid('req_', true),
'timestamp' => date('Y-m-d H:i:s'),
'method' => $_SERVER['REQUEST_METHOD'] ?? 'CLI',
'url' => $this->getCurrentUrl(),
'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'execution_time' => $executionTime,
'memory_usage' => memory_get_usage(true),
'peak_memory' => memory_get_peak_usage(true),
'is_slow' => $executionTime > $this->config['slow_request_threshold'],
'php_version' => PHP_VERSION,
'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? ''
];
$this->save($data);
}
private function getCurrentUrl() {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
return "$protocol://$host$uri";
}
private function save($data) {
if ($this->config['log_file']) {
$this->saveToFile($data);
}
if ($this->config['log_db']) {
$this->saveToDatabase($data);
}
// 慢请求告警
if ($data['is_slow']) {
$this->slowRequestAlert($data);
}
}
private function saveToFile($data) {
$logFile = __DIR__ . '/monitor/' . date('Y-m-d') . '.log';
$dir = dirname($logFile);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$line = json_encode($data) . PHP_EOL;
file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
}
private function slowRequestAlert($data) {
// 发送告警,例如邮件或短信
$message = sprintf(
"Slow Request Alert!\nURL: %s\nTime: %.2fs\nMemory: %s\nTime: %s",
$data['url'],
$data['execution_time'],
$this->formatBytes($data['memory_usage']),
$data['timestamp']
);
// 记录到单独的慢请求日志
$slowLogFile = __DIR__ . '/monitor/slow_requests.log';
file_put_contents($slowLogFile, $message . PHP_EOL, FILE_APPEND | LOCK_EX);
}
private function formatBytes($bytes) {
return round($bytes / 1024 / 1024, 2) . ' MB';
}
}
// 使用
$monitor = new PassiveMonitorSystem([
'log_db' => false,
'log_file' => true,
'slow_request_threshold' => 1.0
]);
?>
数据库表结构
CREATE TABLE request_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
request_id VARCHAR(50) UNIQUE,
timestamp DATETIME,
method VARCHAR(10),
url TEXT,
ip VARCHAR(45),
user_agent TEXT,
execution_time DECIMAL(10,4),
memory_usage BIGINT,
peak_memory BIGINT,
is_slow BOOLEAN DEFAULT FALSE,
php_version VARCHAR(10),
server_software VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_timestamp (timestamp),
INDEX idx_is_slow (is_slow),
INDEX idx_execution_time (execution_time)
);
CREATE TABLE error_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME,
type VARCHAR(50),
message TEXT,
file VARCHAR(255),
line INT,
url TEXT,
trace TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Nginx 配置示例(用于被动日志记录)
server {
listen 80;
server_name example.com;
# 访问日志(被动记录)
access_log /var/log/nginx/example_access.log;
error_log /var/log/nginx/example_error.log;
# 记录 POST 数据
location / {
try_files $uri $uri/ /index.php?$query_string;
# 记录请求体大小
client_body_buffer_size 1k;
client_max_body_size 10m;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 传递额外信息给 PHP
fastcgi_param REQUEST_TIME $msec;
fastcgi_param SERVER_ADDR $server_addr;
}
}
使用建议
<?php
// 1. 在框架入口文件引入
require_once 'PassiveMonitor.php';
$monitor = new PassiveMonitor();
// 2. 或者在 composer.json 中添加自动加载
// "autoload": {
// "files": [
// "app/Monitor.php"
// ]
// }
// 3. 数据库日志清理策略
// 定期清理旧数据
$cleanupDays = 30;
$pdo->exec("DELETE FROM request_logs WHERE timestamp < DATE_SUB(NOW(), INTERVAL $cleanupDays DAY)");
// 4. 日志文件轮转
// 使用 logrotate 或 PHP 自带的轮转
ini_set('log_rotate_max_files', 10);
?>
优势
- 零侵入性 - 不需要修改业务代码
- 自动触发 - 每次请求自动记录
- 实时性 - 立即记录问题
- 全面覆盖 - 监控所有请求
注意事项
- 注意磁盘空间管理
- 合理设置日志级别
- 考虑性能影响(建议使用队列异步写入)
- 敏感信息脱敏处理
这样实现的被动监控系统可以在不修改业务代码的情况下,自动记录所有请求信息和异常情况。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播