本文目录导读:

我来为你介绍 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 异常监控上报体系,你可以根据项目需求选择性地使用各个组件,建议从基础的异常处理和日志记录开始,逐步增加监控上报功能。