mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-25 11:03:14 +01:00
311 lines
9.8 KiB
PHP
311 lines
9.8 KiB
PHP
<?php
|
|
|
|
/**
|
|
* A centralized logger class for handling PHP errors, exceptions, and fatal errors.
|
|
* Logs all events to a file in base64-encoded JSON format and optionally displays them on screen.
|
|
*
|
|
* @package XC_VM_Includes_Libs
|
|
* @author Divarion_D <https://github.com/Divarion-D>
|
|
* @copyright 2025-2026 Vateron Media
|
|
* @link https://github.com/Vateron-Media/XC_VM
|
|
* @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html
|
|
*/
|
|
|
|
final class Logger {
|
|
/** @var bool Whether errors should be displayed on screen */
|
|
private static bool $showErrors = false;
|
|
|
|
/** @var string Full path to the log file */
|
|
private static string $logFile;
|
|
|
|
/**
|
|
* Initializes error, exception, and fatal error handlers.
|
|
*
|
|
* @param bool $showErrors true to display errors, false to hide UI output
|
|
* @param string $logFile Full path to the file where logs will be written
|
|
*/
|
|
public static function init(bool $showErrors, string $logFile): void {
|
|
self::$showErrors = $showErrors;
|
|
self::$logFile = $logFile;
|
|
|
|
// Set custom handlers
|
|
set_error_handler([self::class, 'handleError']);
|
|
set_exception_handler([self::class, 'handleException']);
|
|
register_shutdown_function([self::class, 'handleFatal']);
|
|
|
|
// Always collect all errors for logging; UI visibility is controlled separately.
|
|
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
|
|
|
|
if ($showErrors) {
|
|
ini_set('display_errors', '1');
|
|
ini_set('display_startup_errors', '1');
|
|
} else {
|
|
ini_set('display_errors', '0');
|
|
ini_set('display_startup_errors', '0');
|
|
}
|
|
}
|
|
|
|
/* ================= ERRORS ================= */
|
|
|
|
/**
|
|
* Handler for regular PHP errors (trigger_error, warnings, notices, etc.).
|
|
*
|
|
* @param int $errno Error level (E_WARNING, E_NOTICE, etc.)
|
|
* @param string $message Error message
|
|
* @param string $file File where the error occurred
|
|
* @param int $line Line number in the file
|
|
*
|
|
* @return bool true if the error was handled (prevents default PHP handler)
|
|
*/
|
|
public static function handleError(
|
|
int $errno,
|
|
string $message,
|
|
string $file,
|
|
int $line
|
|
): bool {
|
|
// Ignore suppressed errors (using @ operator)
|
|
if (!(error_reporting() & $errno)) {
|
|
return false;
|
|
}
|
|
|
|
self::log(
|
|
self::mapErrorLevel($errno),
|
|
$message,
|
|
self::buildTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)),
|
|
$file,
|
|
$line
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ================= EXCEPTIONS ================= */
|
|
|
|
/**
|
|
* Handler for uncaught exceptions.
|
|
*
|
|
* @param Throwable $e The uncaught exception
|
|
*/
|
|
public static function handleException(Throwable $e): void {
|
|
self::log(
|
|
'EXCEPTION',
|
|
$e->getMessage(),
|
|
self::buildExceptionTrace($e),
|
|
$e->getFile(),
|
|
$e->getLine()
|
|
);
|
|
}
|
|
|
|
/* ================= FATAL ================= */
|
|
|
|
/**
|
|
* Handler for fatal errors (E_ERROR, E_PARSE, etc.) that terminate script execution.
|
|
* Called via register_shutdown_function.
|
|
*/
|
|
public static function handleFatal(): void {
|
|
$error = error_get_last();
|
|
|
|
// Process only true fatal errors
|
|
if ($error && in_array($error['type'], [
|
|
E_ERROR,
|
|
E_PARSE,
|
|
E_CORE_ERROR,
|
|
E_COMPILE_ERROR
|
|
], true)) {
|
|
self::log(
|
|
'FATAL',
|
|
$error['message'],
|
|
'', // Stack trace is not available for fatal errors
|
|
$error['file'],
|
|
$error['line']
|
|
);
|
|
}
|
|
}
|
|
|
|
/* ================= CORE LOG ================= */
|
|
|
|
/**
|
|
* Core logging method. Writes the event to the log file and optionally outputs it on screen.
|
|
*
|
|
* @param string $type Event type (ERROR, WARNING, NOTICE, EXCEPTION, FATAL, etc.)
|
|
* @param string $message Message text
|
|
* @param string $trace Stack trace (optional)
|
|
* @param string $file File where the event occurred
|
|
* @param int $line Line number
|
|
*/
|
|
public static function log(
|
|
string $type,
|
|
string $message,
|
|
string $trace = '',
|
|
string $file = '',
|
|
int $line = 0
|
|
): void {
|
|
$data = [
|
|
'type' => $type,
|
|
'log_message' => $message,
|
|
'file' => $file,
|
|
'line' => $line,
|
|
'log_extra' => $trace, // Process ID
|
|
'time' => time(), // Unix timestamp
|
|
'env' => php_sapi_name() // SAPI name (cli, fpm-fcgi, etc.)
|
|
];
|
|
|
|
// Ensure log directory exists
|
|
$logDir = dirname(self::$logFile);
|
|
if (!is_dir($logDir)) {
|
|
@mkdir($logDir, 0775, true);
|
|
}
|
|
|
|
// Write to file as base64-encoded JSON (easy to parse and prevents line corruption)
|
|
file_put_contents(
|
|
self::$logFile,
|
|
base64_encode(json_encode($data, JSON_UNESCAPED_UNICODE)) . "\n",
|
|
FILE_APPEND | LOCK_EX
|
|
);
|
|
|
|
// Set log file permissions if running as root (common in containers)
|
|
if (function_exists('posix_geteuid') && posix_geteuid() === 0) {
|
|
@chown(self::$logFile, 'xc_vm');
|
|
@chgrp(self::$logFile, 'xc_vm');
|
|
@chmod(self::$logFile, 0664);
|
|
}
|
|
|
|
// If UI output is enabled, display a readable message on screen
|
|
if (self::$showErrors) {
|
|
self::output($data);
|
|
}
|
|
}
|
|
|
|
/* ================= TRACE ================= */
|
|
|
|
/**
|
|
* Builds a stack trace for an exception chain (including previous exceptions).
|
|
*
|
|
* @param Throwable $e The exception
|
|
*
|
|
* @return string Formatted trace string
|
|
*/
|
|
private static function buildExceptionTrace(Throwable $e): string {
|
|
$out = [];
|
|
|
|
do {
|
|
// Main exception info
|
|
$out[] = sprintf(
|
|
"%s: %s in %s:%d",
|
|
get_class($e),
|
|
$e->getMessage(),
|
|
$e->getFile(),
|
|
$e->getLine()
|
|
);
|
|
|
|
// Call stack for the current exception
|
|
foreach ($e->getTrace() as $i => $t) {
|
|
$out[] = sprintf(
|
|
"#%d %s(%s): %s()",
|
|
$i,
|
|
$t['file'] ?? '[internal]',
|
|
$t['line'] ?? '?',
|
|
$t['function']
|
|
);
|
|
}
|
|
|
|
$e = $e->getPrevious();
|
|
if ($e) {
|
|
$out[] = "---- CAUSED BY ----";
|
|
}
|
|
} while ($e);
|
|
|
|
return implode("\n", $out);
|
|
}
|
|
|
|
/**
|
|
* Builds a stack trace from the array returned by debug_backtrace().
|
|
*
|
|
* @param array $trace Backtrace array
|
|
*
|
|
* @return string Formatted trace string
|
|
*/
|
|
private static function buildTrace(array $trace): string {
|
|
$out = [];
|
|
|
|
foreach ($trace as $i => $t) {
|
|
$out[] = sprintf(
|
|
"#%d %s(%s): %s()",
|
|
$i,
|
|
$t['file'] ?? '[internal]',
|
|
$t['line'] ?? '?',
|
|
$t['function']
|
|
);
|
|
}
|
|
|
|
return implode("\n", $out);
|
|
}
|
|
|
|
/* ================= HELPERS ================= */
|
|
|
|
/**
|
|
* Maps a PHP error constant to a human-readable string.
|
|
*
|
|
* @param int $errno Error level constant
|
|
*
|
|
* @return string Error type (ERROR, WARNING, NOTICE, INFO)
|
|
*/
|
|
private static function mapErrorLevel(int $errno): string {
|
|
return match ($errno) {
|
|
E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR => 'ERROR',
|
|
E_WARNING, E_USER_WARNING => 'WARNING',
|
|
E_NOTICE, E_USER_NOTICE => 'NOTICE',
|
|
default => 'INFO',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Outputs error/exception information to the screen in a readable format.
|
|
* Format depends on the environment (CLI or Web).
|
|
*
|
|
* @param array $d Event data (from the log method)
|
|
*/
|
|
private static function output(array $d): void {
|
|
if (php_sapi_name() === 'cli') {
|
|
// Color scheme for terminal
|
|
$color = match ($d['type']) {
|
|
'FATAL', 'ERROR' => "\033[41m\033[97m", // red background, white text
|
|
'WARNING' => "\033[43m\033[30m", // yellow background, black text
|
|
'NOTICE' => "\033[44m\033[97m", // blue background, white text
|
|
default => "\033[45m\033[97m", // magenta background, white text
|
|
};
|
|
|
|
echo "\n{$color} {$d['type']} \033[0m ";
|
|
echo date('Y-m-d H:i:s', $d['time']) . "\n";
|
|
echo "{$d['log_message']}\n";
|
|
echo "{$d['file']}:{$d['line']}\n";
|
|
|
|
if (!empty($d['log_extra'])) {
|
|
echo str_repeat('-', 60) . "\n";
|
|
echo $d['log_extra'] . "\n";
|
|
}
|
|
} else {
|
|
// Web environment output
|
|
echo "<div style='
|
|
border:2px solid #f00;
|
|
background:#fff0f0;
|
|
padding:10px;
|
|
margin:10px;
|
|
font-family:monospace;
|
|
color:#000;
|
|
'>";
|
|
echo "<b>{$d['type']}</b> ";
|
|
echo "<small>" . date('Y-m-d H:i:s', $d['time']) . "</small><br>";
|
|
echo htmlspecialchars($d['log_message'], ENT_SUBSTITUTE) . "<br>";
|
|
echo "<small>{$d['file']}:{$d['line']}</small>";
|
|
|
|
if (!empty($d['log_extra'])) {
|
|
echo "<pre style='margin-top:10px;background:#fff;padding:10px;border:1px solid #ccc;'>";
|
|
echo htmlspecialchars($d['log_extra'], ENT_SUBSTITUTE);
|
|
echo "</pre>";
|
|
}
|
|
echo "</div>";
|
|
}
|
|
}
|
|
}
|