PHP 怎么PHP 错误收集

wen PHP项目 2

本文目录导读:

PHP 怎么PHP 错误收集

  1. 基础错误处理函数
  2. 完整的错误监控方案
  3. 使用第三方库
  4. 生产环境最佳实践
  5. 错误收集工具集成
  6. 自定义错误格式
  7. 建议

我来详细介绍 PHP 错误收集的几种方法:

基础错误处理函数

使用 set_error_handler()

// 设置自定义错误处理函数
set_error_handler(function($errno, $errstr, $errfile, $errline) {
    $error = [
        'type' => $errno,
        'message' => $errstr,
        'file' => $errfile,
        'line' => $errline,
        'time' => date('Y-m-d H:i:s')
    ];
    // 记录到文件
    file_put_contents('error.log', json_encode($error) . PHP_EOL, FILE_APPEND);
    // 返回 true 阻止 PHP 内置错误处理
    return true;
});

使用 set_exception_handler()

// 处理未捕获的异常
set_exception_handler(function($exception) {
    $error = [
        'type' => 'Exception',
        'message' => $exception->getMessage(),
        'file' => $exception->getFile(),
        'line' => $exception->getLine(),
        'trace' => $exception->getTraceAsString(),
        'time' => date('Y-m-d H:i:s')
    ];
    // 记录到文件或发送到监控系统
    file_put_contents('exception.log', json_encode($error) . PHP_EOL, FILE_APPEND);
});

完整的错误监控方案

类封装示例

class ErrorCollector {
    private static $instance = null;
    private $logDir;
    private $isRegistered = false;
    private function __construct($logDir = null) {
        $this->logDir = $logDir ?: __DIR__ . '/logs';
        if (!is_dir($this->logDir)) {
            mkdir($this->logDir, 0777, true);
        }
    }
    public static function getInstance($logDir = null) {
        if (self::$instance === null) {
            self::$instance = new self($logDir);
        }
        return self::$instance;
    }
    public function register() {
        if ($this->isRegistered) return;
        set_error_handler([$this, 'handleError']);
        set_exception_handler([$this, 'handleException']);
        register_shutdown_function([$this, 'handleShutdown']);
        $this->isRegistered = true;
    }
    public function handleError($errno, $errstr, $errfile, $errline) {
        $error = [
            'type' => $this->getErrorType($errno),
            'code' => $errno,
            'message' => $errstr,
            'file' => $errfile,
            'line' => $errline,
            'time' => date('Y-m-d H:i:s'),
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
            'url' => $_SERVER['REQUEST_URI'] ?? 'CLI'
        ];
        $this->logError($error);
        // 不阻止 PHP 内置错误处理
        return false;
    }
    public function handleException($exception) {
        $error = [
            'type' => 'Exception',
            'code' => $exception->getCode(),
            'message' => $exception->getMessage(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTraceAsString(),
            'time' => date('Y-m-d H:i:s'),
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
            'url' => $_SERVER['REQUEST_URI'] ?? 'CLI'
        ];
        $this->logError($error);
    }
    public function handleShutdown() {
        $error = error_get_last();
        if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $this->handleError($error['type'], $error['message'], $error['file'], $error['line']);
        }
    }
    private function logError($error) {
        // 按日期分类存储
        $filename = $this->logDir . '/error_' . date('Y-m-d') . '.log';
        $logEntry = json_encode($error, JSON_UNESCAPED_UNICODE) . PHP_EOL;
        file_put_contents($filename, $logEntry, FILE_APPEND);
        // 错误级别较高时发送通知
        if ($this->isCriticalError($error['code'])) {
            $this->sendAlert($error);
        }
    }
    private function getErrorType($errno) {
        $types = [
            E_ERROR => 'Fatal Error',
            E_WARNING => 'Warning',
            E_PARSE => 'Parse Error',
            E_NOTICE => 'Notice',
            E_CORE_ERROR => 'Core Error',
            E_COMPILE_ERROR => 'Compile Error',
            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[$errno] ?? 'Unknown';
    }
    private function isCriticalError($code) {
        return in_array($code, [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR]);
    }
    private function sendAlert($error) {
        // 实现发送警报的逻辑(邮件、短信、Webhook等)
        // mail('admin@example.com', 'Critical Error', print_r($error, true));
    }
}
// 使用示例
$collector = ErrorCollector::getInstance(__DIR__ . '/logs');
$collector->register();

使用第三方库

Monolog (推荐)

composer require monolog/monolog
// 使用示例
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Processor\WebProcessor;
$log = new Logger('app');
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::WARNING));
$log->pushHandler(new NativeMailerHandler('admin@example.com', 'Error Alert', 'from@example.com', Logger::CRITICAL));
$log->pushProcessor(new WebProcessor());
// 记录错误
try {
    // 你的代码
} catch (\Exception $e) {
    $log->error('Error occurred: ' . $e->getMessage(), [
        'trace' => $e->getTraceAsString(),
        'file' => $e->getFile(),
        'line' => $e->getLine()
    ]);
}

Whoops (开发环境)

composer require filp/whoops
// 使用示例
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();

生产环境最佳实践

// 配置文件 config.php
return [
    'error_handler' => [
        'enabled' => true,
        'log_dir' => __DIR__ . '/../logs',
        'log_level' => E_ALL & ~E_DEPRECATED & ~E_STRICT,
        'display_errors' => false,
        'send_alerts' => true,
        'alert_email' => 'admin@example.com',
        'alert_level' => E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR,
    ]
];
// 初始化
$config = require 'config.php';
$collector = ErrorCollector::getInstance($config['error_handler']['log_dir']);
$collector->register();
// 设置 PHP 配置
ini_set('display_errors', $config['error_handler']['display_errors'] ? '1' : '0');
ini_set('log_errors', '1');
ini_set('error_log', $config['error_handler']['log_dir'] . '/php_errors.log');
error_reporting($config['error_handler']['log_level']);

错误收集工具集成

// Sentry 集成
// composer require sentry/sdk
\Sentry\init(['dsn' => 'your-sentry-dsn' ]);
// 自动捕获所有错误
\Sentry\configureScope(function (\Sentry\State\Scope $scope): void {
    $scope->setUser(['email' => 'user@example.com']);
});
set_error_handler(function($severity, $message, $file, $line) {
    throw new \ErrorException($message, 0, $severity, $file, $line);
});
// 或者使用框架集成(Laravel)
if (app()->bound('sentry')) {
    $this->app->make('sentry');
}

自定义错误格式

// 支持多格式输出
class ErrorFormatter {
    public static function toJson($error) {
        return json_encode($error, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    }
    public static function toText($error) {
        return sprintf(
            "[%s] %s: %s in %s on line %d\nURL: %s\nIP: %s\n",
            $error['time'],
            $error['type'],
            $error['message'],
            $error['file'],
            $error['line'],
            $error['url'],
            $error['ip']
        );
    }
    public static function toHtml($error) {
        return sprintf(
            '<div class="error-entry">
                <h3>%s</h3>
                <p><strong>Time:</strong> %s</p>
                <p><strong>Message:</strong> %s</p>
                <p><strong>File:</strong> %s:%d</p>
            </div>',
            $error['type'],
            $error['time'],
            htmlspecialchars($error['message']),
            htmlspecialchars($error['file']),
            $error['line']
        );
    }
}

建议

  1. 开发环境:显示详细错误信息
  2. 生产环境:记录错误但不显示,使用日志和监控系统
  3. 使用成熟的库:Monolog, Sentry, Bugsnag等
  4. 定期检查日志:设置日志轮转和清理策略
  5. 错误分级:根据严重程度发送不同级别的通知

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