生產環境中完善的錯誤監控是系統穩定的基石。本文提供一個開箱即用的 PHP 錯誤處理器,統一捕獲致命錯誤、警告、解析錯誤等,自動記錄詳細上下文到日誌文件。
function error_reg() {
$errorTypes = [
E_ERROR => 'ERROR',
E_WARNING => 'WARNING',
E_PARSE => 'PARSE',
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',
];
// 1. 捕獲致命錯誤 (register_shutdown_function)
register_shutdown_function(function () use ($errorTypes) {
$error = error_get_last();
if ($error && !in_array($error['type'], [E_NOTICE, E_STRICT, E_DEPRECATED])) {
$msg = sprintf(
"%s [%d]: %s in %s on line %d [%s]\n",
$errorTypes[$error['type']] ?? 'UNKNOWN',
$error['type'],
$error['message'],
$error['file'],
$error['line'],
date('Y-m-d H:i:s')
);
error_log($msg, 3, '/var/log/php_errors.log');
}
});
// 2. 捕獲運行時錯誤 (set_error_handler)
set_error_handler(function ($type, $msg, $file, $line) use ($errorTypes) {
if (!in_array($type, [E_NOTICE, E_STRICT, E_DEPRECATED])) {
$logMsg = sprintf(
"%s [%d]: %s in %s on line %d [%s]\n",
$errorTypes[$type] ?? 'UNKNOWN',
$type,
$msg,
$file,
$line,
date('Y-m-d H:i:s')
);
error_log($logMsg, 3, '/var/log/php_errors.log');
}
return false; // 繼續執行 PHP 內置處理器
}, E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
}
// 入口文件調用
error_reg();
| 捕獲方式 | 覆蓋範圍 | 特點 |
|---|---|---|
| register_shutdown_function | E_ERROR, E_PARSE, E_CORE_ERROR 等致命錯誤 | 腳本結束前最後機會捕獲 |
| set_error_handler | E_WARNING, E_NOTICE, E_USER_ERROR 等非致命錯誤 | 實時攔截,可決定是否繼續執行 |
[ERROR] [1]: Call to undefined function foo() in /var/www/index.php on line 15 [2024-01-15 10:30:45] [WARNING] [2]: Division by zero in /var/www/calc.php on line 8 [2024-01-15 10:31:12]
包含:錯誤級別、錯誤碼、消息、文件、行號、時間戳,便於日誌分析工具解析。
// 使用 logrotate 配置
/var/log/php_errors.log {
daily
rotate 30
compress
missingok
notifempty
create 640 www-data adm
}
function error_reg() {
// ...
$criticalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
// 在記錄日誌後
if (in_array($type, $criticalTypes)) {
// 發送郵件/Slack/鉚釘告警
alert_oncall($msg);
}
}
$logData = [
'level' => $errorTypes[$type] ?? 'UNKNOWN',
'code' => $type,
'message' => $msg,
'file' => $file,
'line' => $line,
'timestamp' => date('c'),
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5),
'request' => [
'uri' => $_SERVER['REQUEST_URI'] ?? '',
'method' => $_SERVER['REQUEST_METHOD'] ?? '',
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
],
];
error_log(json_encode($logData, JSON_UNESCAPED_UNICODE) . "\n", 3, $logFile);
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;
$log = new Logger('app');
$handler = new RotatingFileHandler('/var/log/app.log', 30, Logger::WARNING);
$handler->setFormatter(new LineFormatter("%datetime% %level_name% %message% %context% %extra%\n"));
$log->pushHandler($handler);
// 統一異常處理
set_exception_handler(function ($e) use ($log) {
$log->critical($e->getMessage(), ['trace' => $e->getTraceAsString()]);
});
log_errors = On、error_log = /var/log/php_errors.logchown www-data:adm /var/log && chmod 750 /var/logdisplay_errors = Off建立完善的錯誤監控體系,能將故障發現時間從「用戶投訴」縮短到「秒級告警」。