php-fpm: capture & logs exceptions
All checks were successful
php-fpm-build / build (7.4) (push) Successful in 4m53s
All checks were successful
php-fpm-build / build (7.4) (push) Successful in 4m53s
This commit is contained in:
@@ -1,23 +1,80 @@
|
||||
<?php
|
||||
// force-debug.php
|
||||
// Optional debug bootstrap that can be toggled with FORCE_DEBUG_ERRORS=1.
|
||||
// Automatically prepended; guarantees fatals reach container logs and optionally surfaces verbose output.
|
||||
|
||||
$forceDebug = getenv('FORCE_DEBUG_ERRORS');
|
||||
if ($forceDebug === false) {
|
||||
return;
|
||||
$forceDebugRaw = getenv('FORCE_DEBUG_ERRORS');
|
||||
$forceDebugEnabled = false;
|
||||
if ($forceDebugRaw !== false) {
|
||||
$normalized = strtolower(trim($forceDebugRaw));
|
||||
$forceDebugEnabled = in_array($normalized, ['1', 'true', 'yes', 'on'], true);
|
||||
}
|
||||
|
||||
$normalized = strtolower(trim($forceDebug));
|
||||
$enabled = in_array($normalized, ['1', 'true', 'yes', 'on'], true);
|
||||
|
||||
if (!$enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
// Ensure PHP always logs to stderr even if application code tampers with settings.
|
||||
ini_set('log_errors', '1');
|
||||
ini_set('error_log', '/proc/self/fd/2');
|
||||
|
||||
error_log('[force-debug] Verbose error reporting enabled via FORCE_DEBUG_ERRORS');
|
||||
// Helper to safely write to container stderr without relying on php.ini.
|
||||
$forceDebugLog = static function (string $message): void {
|
||||
static $stderr = null;
|
||||
if ($stderr === null) {
|
||||
$stderr = @fopen('php://stderr', 'ab');
|
||||
}
|
||||
if ($stderr) {
|
||||
@fwrite($stderr, $message . PHP_EOL);
|
||||
} else {
|
||||
// Fallback to PHP's error_log if stderr is unavailable for any reason.
|
||||
error_log($message);
|
||||
}
|
||||
};
|
||||
|
||||
// Capture uncaught exceptions so they can never disappear silently.
|
||||
set_exception_handler(static function (Throwable $throwable) use ($forceDebugLog, $forceDebugEnabled): void {
|
||||
$message = sprintf(
|
||||
'[force-debug] Uncaught %s: %s in %s:%d',
|
||||
get_class($throwable),
|
||||
$throwable->getMessage(),
|
||||
$throwable->getFile(),
|
||||
$throwable->getLine()
|
||||
);
|
||||
$forceDebugLog($message);
|
||||
$forceDebugLog($throwable->getTraceAsString());
|
||||
|
||||
if ($forceDebugEnabled) {
|
||||
http_response_code(500);
|
||||
echo '<pre>' . htmlspecialchars($message . "\n" . $throwable->getTraceAsString(), ENT_QUOTES, 'UTF-8') . '</pre>';
|
||||
}
|
||||
});
|
||||
|
||||
// Capture fatal shutdown errors (E_ERROR, E_PARSE, etc.) and surface them to stderr.
|
||||
register_shutdown_function(static function () use ($forceDebugLog, $forceDebugEnabled): void {
|
||||
$error = error_get_last();
|
||||
if ($error === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR];
|
||||
if (!in_array($error['type'], $fatalTypes, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
'[force-debug] Fatal error (%s): %s in %s:%d',
|
||||
$error['type'],
|
||||
$error['message'],
|
||||
$error['file'],
|
||||
$error['line']
|
||||
);
|
||||
$forceDebugLog($message);
|
||||
|
||||
if ($forceDebugEnabled) {
|
||||
http_response_code(500);
|
||||
echo '<pre>' . htmlspecialchars($message, ENT_QUOTES, 'UTF-8') . '</pre>';
|
||||
}
|
||||
});
|
||||
|
||||
if ($forceDebugEnabled) {
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
$forceDebugLog('[force-debug] Verbose error reporting enabled via FORCE_DEBUG_ERRORS');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user