PHP 项目异常监控上报

wen PHP项目 3

本文目录导读:

PHP 项目异常监控上报

  1. 基础异常处理架构
  2. 监控上报服务
  3. 完整的监控方案
  4. 最佳实践建议

我来为你介绍 PHP 项目异常监控上报的完整方案。

基础异常处理架构

1 自定义异常处理类

<?php
// src/Exception/ExceptionHandler.php
class ExceptionHandler
{
    protected $logger;
    protected $config;
    public function __construct(array $config = [])
    {
        $this->config = $config;
        $this->logger = new Logger($config['log_path'] ?? sys_get_temp_dir());
    }
    // 注册异常处理
    public function register()
    {
        set_exception_handler([$this, 'handleException']);
        set_error_handler([$this, 'handleError']);
        register_shutdown_function([$this, 'handleFatalError']);
    }
    // 处理未捕获异常
    public function handleException(Throwable $e)
    {
        $data = [
            'type' => 'exception',
            'message' => $e->getMessage(),
            'code' => $e->getCode(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString(),
            'url' => $this->getCurrentUrl(),
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'cli',
            'params' => $this->getRequestParams(),
            'timestamp' => date('Y-m-d H:i:s'),
            'ip' => $this->getClientIp(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        ];
        // 记录日志
        $this->logger->error($data['message'], $data);
        // 上报监控系统
        $this->report($data);
        // 处理响应
        $this->renderResponse($e);
    }
    // 处理普通错误
    public function handleError($level, $message, $file = '', $line = 0)
    {
        if (error_reporting() & $level) {
            $data = [
                'type' => 'error',
                'level' => $level,
                'message' => $message,
                'file' => $file,
                'line' => $line,
                'url' => $this->getCurrentUrl(),
                'method' => $_SERVER['REQUEST_METHOD'] ?? 'cli',
                'params' => $this->getRequestParams(),
                'timestamp' => date('Y-m-d H:i:s'),
                'ip' => $this->getClientIp(),
            ];
            // 记录日志
            $this->logger->error($message, $data);
            // 上报监控系统
            $this->report($data);
        }
        return false;
    }
    // 处理致命错误
    public function handleFatalError()
    {
        $error = error_get_last();
        if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $data = [
                'type' => 'fatal_error',
                'level' => $error['type'],
                'message' => $error['message'],
                'file' => $error['file'],
                'line' => $error['line'],
                'url' => $this->getCurrentUrl(),
                'method' => $_SERVER['REQUEST_METHOD'] ?? 'cli',
                'params' => $this->getRequestParams(),
                'timestamp' => date('Y-m-d H:i:s'),
                'ip' => $this->getClientIp(),
            ];
            $this->logger->error($error['message'], $data);
            $this->report($data);
        }
    }
    // 上报监控系统
    protected function report(array $data)
    {
        // 配置上报
        if (isset($this->config['report_enable']) && $this->config['report_enable']) {
            $reporter = new ErrorReporter($this->config);
            $reporter->send($data);
        }
    }
    // 渲染响应
    protected function renderResponse(Throwable $e)
    {
        if ($this->isApiRequest()) {
            header('Content-Type: application/json');
            echo json_encode([
                'code' => $e->getCode() ?: 500,
                'message' => $this->isDebug() ? $e->getMessage() : 'Internal Server Error',
                'data' => null
            ]);
        } else {
            // 显示错误页面或跳转
            http_response_code(500);
            echo "An error occurred. Please try again later.";
        }
        exit;
    }
    // 辅助方法
    protected function getCurrentUrl()
    {
        return $_SERVER['REQUEST_URI'] ?? 'cli';
    }
    protected function getRequestParams()
    {
        return [
            'GET' => $_GET,
            'POST' => $_POST,
            'FILES' => $_FILES
        ];
    }
    protected function getClientIp()
    {
        return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    }
    protected function isApiRequest()
    {
        return isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false;
    }
    protected function isDebug()
    {
        return !empty($this->config['debug']);
    }
}

2 日志记录类

<?php
// src/Log/Logger.php
class Logger
{
    protected $logPath;
    protected $levels = [
        'DEBUG' => 100,
        'INFO' => 200,
        'WARNING' => 300,
        'ERROR' => 400,
        'CRITICAL' => 500
    ];
    public function __construct($logPath)
    {
        $this->logPath = $logPath;
        if (!is_dir($logPath)) {
            mkdir($logPath, 0755, true);
        }
    }
    public function error($message, array $context = [])
    {
        $this->write('ERROR', $message, $context);
    }
    public function info($message, array $context = [])
    {
        $this->write('INFO', $message, $context);
    }
    public function debug($message, array $context = [])
    {
        if (!empty($this->config['debug'])) {
            $this->write('DEBUG', $message, $context);
        }
    }
    protected function write($level, $message, array $context = [])
    {
        $date = date('Y-m-d');
        $time = date('Y-m-d H:i:s');
        $logFile = $this->logPath . "/{$date}.log";
        $formattedMessage = sprintf(
            "[%s] %s: %s %s\n",
            $time,
            $level,
            $message,
            $context ? json_encode($context, JSON_UNESCAPED_UNICODE) : ''
        );
        file_put_contents($logFile, $formattedMessage, FILE_APPEND | LOCK_EX);
    }
}

监控上报服务

1 错误上报服务

<?php
// src/Monitoring/ErrorReporter.php
class ErrorReporter
{
    protected $config;
    protected $chunks = [];
    public function __construct(array $config)
    {
        $this->config = $config;
        $this->initChunks();
    }
    // 上报错误
    public function send(array $data)
    {
        try {
            // 根据配置选择上报方式
            $transport = $this->config['transport'] ?? 'http';
            switch ($transport) {
                case 'http':
                    $this->sendHttp($data);
                    break;
                case 'rabbitmq':
                    $this->sendRabbitMQ($data);
                    break;
                case 'kafka':
                    $this->sendKafka($data);
                    break;
                default:
                    $this->sendHttp($data);
            }
        } catch (\Exception $e) {
            // 上报失败时记录到本地文件
            $this->saveToFile($data);
        }
    }
    // HTTP 上报
    protected function sendHttp(array $data)
    {
        $url = $this->config['report_url'] ?? '';
        if (empty($url)) {
            return;
        }
        $payload = [
            'project' => $this->config['project_name'] ?? 'php_project',
            'environment' => $this->config['environment'] ?? 'production',
            'version' => $this->config['version'] ?? '1.0.0',
            'data' => $data
        ];
        // 使用 cURL 异步上报(不阻塞主流程)
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'X-API-Key: ' . ($this->config['api_key'] ?? '')
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 1,
            CURLOPT_CONNECTTIMEOUT => 1
        ]);
        // 异步发送
        if (function_exists('curl_multi_init')) {
            $mh = curl_multi_init();
            curl_multi_add_handle($mh, $ch);
            curl_multi_exec($mh, $running);
            curl_multi_remove_handle($mh, $ch);
            curl_multi_close($mh);
        } else {
            curl_exec($ch);
            curl_close($ch);
        }
    }
    // 保存到本地文件(上报失败时的备份)
    protected function saveToFile(array $data)
    {
        $backupPath = $this->config['backup_path'] ?? sys_get_temp_dir() . '/error_reports';
        if (!is_dir($backupPath)) {
            mkdir($backupPath, 0755, true);
        }
        $filename = $backupPath . '/error_' . date('Y-m-d_H-i-s') . '_' . uniqid() . '.json';
        file_put_contents($filename, json_encode($data, JSON_UNESCAPED_UNICODE), LOCK_EX);
    }
    protected function initChunks()
    {
        // 初始化批处理
        register_shutdown_function(function() {
            $this->flushChunks();
        });
    }
    protected function flushChunks()
    {
        // 批量发送错误
    }
}

2 集成 Sentry 上报

<?php
// src/Monitoring/SentryReporter.php
class SentryReporter
{
    protected $dsn;
    protected $client;
    public function __construct($dsn)
    {
        $this->dsn = $dsn;
        $this->client = \Sentry\init([
            'dsn' => $dsn,
            'traces_sample_rate' => 0.2,
            'environment' => 'production',
            'release' => '1.0.0',
        ]);
    }
    // 手动上报错误
    public function captureException(Throwable $e, array $context = [])
    {
        \Sentry\withScope(function (\Sentry\State\Scope $scope) use ($context) {
            if (!empty($context)) {
                $scope->setExtra('parameters', $context);
            }
            $scope->setTag('app', 'php_project');
            $scope->setUser([
                'id' => $context['user_id'] ?? null,
                'ip' => $_SERVER['REMOTE_ADDR'] ?? null
            ]);
        });
        \Sentry\captureException($e);
    }
    // 上报消息
    public function captureMessage($message, array $context = [])
    {
        \Sentry\withScope(function (\Sentry\State\Scope $scope) use ($context) {
            if (!empty($context)) {
                $scope->setExtras($context);
            }
        });
        \Sentry\captureMessage($message);
    }
    // 上报自定义事件
    public function captureEvent(array $event)
    {
        \Sentry\captureEvent($event);
    }
}

完整的监控方案

1 项目配置

<?php
// config/monitoring.php
return [
    // 异常处理配置
    'exception' => [
        'debug' => false,           // 调试模式显示错误详情
        'log_path' => storage_path('logs'),
        'report_enable' => true,    // 是否启用上报
    ],
    // 日志配置
    'log' => [
        'path' => storage_path('logs'),
        'level' => 'debug',
        'max_files' => 30,
        'channels' => [
            'daily' => [
                'driver' => 'daily',
                'path' => storage_path('logs/laravel.log'),
                'level' => 'debug',
                'days' => 14,
            ],
            'error' => [
                'driver' => 'single',
                'path' => storage_path('logs/error.log'),
                'level' => 'error',
            ],
            'monitor' => [
                'driver' => 'single',
                'path' => storage_path('logs/monitor.log'),
                'level' => 'debug',
            ]
        ]
    ],
    // 上报配置
    'report' => [
        'transport' => 'http',      // http, rabbitmq, kafka
        'report_url' => env('REPORT_URL', 'https://your-monitor-server.com/api/errors'),
        'api_key' => env('REPORT_API_KEY', ''),
        'project_name' => 'my-php-project',
        'environment' => env('APP_ENV', 'production'),
        'version' => '1.0.0',
        'backup_path' => storage_path('app/report_backup'),
        'batch_size' => 100,        // 批量上报大小
        'interval' => 10,           // 上报间隔(秒)
    ],
    // Sentry 配置(可选)
    'sentry' => [
        'dsn' => env('SENTRY_DSN'),
        'traces_sample_rate' => 0.2,
        'environment' => env('APP_ENV', 'production'),
        'release' => '1.0.0',
    ],
    // 性能监控(可选)
    'performance' => [
        'slow_query_threshold' => 1000,  // 慢查询阈值(毫秒)
        'slow_request_threshold' => 2000, // 慢请求阈值(毫秒)
        'report_memory_usage' => true,
    ]
];

2 初始化入口

<?php
// bootstrap.php
// 加载配置
$config = require 'config/monitoring.php';
// 初始化异常处理器
$handler = new ExceptionHandler($config['exception']);
$handler->register();
// 初始化 Sentry(如果配置了)
if (!empty($config['sentry']['dsn'])) {
    $sentry = new SentryReporter($config['sentry']['dsn']);
    // 将 Sentry 集成到异常处理
    $handler->setLogger(function($data) use ($sentry) {
        if (isset($data['message'])) {
            $sentry->captureException(
                new \RuntimeException($data['message'])
            );
        }
    });
}
// 性能监控(可选)
$perfMonitor = new PerformanceMonitor($config['performance']);
$perfMonitor->start();

3 性能监控扩展

<?php
// src/Monitoring/PerformanceMonitor.php
class PerformanceMonitor
{
    protected $startTime;
    protected $startMemory;
    protected $config;
    public function __construct(array $config)
    {
        $this->config = $config;
        $this->startTime = microtime(true);
        $this->startMemory = memory_get_usage();
        // 注册结束时的监控
        register_shutdown_function([$this, 'stop']);
    }
    public function start()
    {
        $this->startTime = microtime(true);
        $this->startMemory = memory_get_usage();
    }
    public function stop()
    {
        $duration = microtime(true) - $this->startTime;
        $memoryUsed = memory_get_peak_usage() - $this->startMemory;
        $data = [
            'type' => 'performance',
            'duration' => $duration * 1000, // 毫秒
            'memory' => $memoryUsed,
            'peak_memory' => memory_get_peak_usage(),
            'url' => $_SERVER['REQUEST_URI'] ?? 'cli',
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'cli',
            'timestamp' => date('Y-m-d H:i:s')
        ];
        // 检查是否超过阈值
        if ($data['duration'] > $this->config['slow_request_threshold']) {
            $this->reportSlowRequest($data);
        }
        // 检查内存使用
        if ($this->config['report_memory_usage'] && $data['memory'] > 100 * 1024 * 1024) {
            $this->reportHighMemory($data);
        }
    }
    protected function reportSlowRequest(array $data)
    {
        // 慢请求上报
        $reporter = new ErrorReporter($this->config);
        $reporter->send($data);
    }
    protected function reportHighMemory(array $data)
    {
        // 内存使用上报
        $reporter = new ErrorReporter($this->config);
        $reporter->send($data);
    }
}

4 使用示例

<?php
// index.php
// 异常处理配置
$config = [
    'debug' => false,
    'log_path' => __DIR__ . '/logs',
    'report_enable' => true,
    'report_url' => 'https://monitor.example.com/api/errors',
    'api_key' => 'your-api-key',
    'project_name' => 'my-project',
    'environment' => 'production'
];
// 注册异常处理
$handler = new ExceptionHandler($config);
$handler->register();
// 业务代码
try {
    // 你的业务逻辑
    $service = new UserService();
    $user = $service->getUser(1);
    // 手动异常捕获
    if (!$user) {
        throw new \Exception('User not found');
    }
} catch (\Exception $e) {
    // 可以手动处理或上报
    $handler->handleException($e);
}

最佳实践建议

1 上报数据规范

// 统一错误格式
$errorSchema = [
    'project' => 'string',
    'environment' => 'production|staging|development',
    'timestamp' => '2024-01-01 00:00:00',
    'level' => 'DEBUG|INFO|WARNING|ERROR|CRITICAL',
    'type' => 'exception|error|fatal_error',
    'message' => '错误信息',
    'class' => '异常类名',
    'file' => '文件路径',
    'line' => 123,
    'trace' => '堆栈信息',
    'url' => '请求URL',
    'method' => 'GET|POST|PUT|DELETE',
    'params' => [],
    'user_id' => 12345,
    'ip' => '192.168.1.1',
    'user_agent' => '浏览器信息',
    'request_id' => '唯一请求ID'
];

2 上报策略

  • 分级上报:根据严重级别决定上报方式
  • 去重处理:合并相同错误,避免重复上报
  • 频率限制:控制上报频率,防止过载
  • 失败重试:上报失败后自动重试
  • 本地缓存:离线时暂存错误,联网后上报

3 监控指标体系

$metrics = [
    'error_rate' => '错误率',
    'error_count' => '错误数量',
    'response_time' => '响应时间',
    'throughput' => '吞吐量',
    'memory_usage' => '内存使用',
    'cpu_usage' => 'CPU使用',
    'slow_queries' => '慢查询数量',
    'slow_requests' => '慢请求数量',
    'request_count' => '请求次数',
    'active_users' => '活跃用户数'
];

这个方案提供了完整的 PHP 异常监控上报体系,你可以根据项目需求选择性地使用各个组件,建议从基础的异常处理和日志记录开始,逐步增加监控上报功能。

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