value < self::$minLevel->value) { return; } if (self::$logPath === null) { self::Initialize(); } self::CheckRotation(); foreach ($context as $key => $value) { if (is_array($value) || is_object($value)) { $value = json_encode($value); } $message = str_replace("{{$key}}", $value, $message); } $timestamp = date("Y-m-d H:i:s"); $levelName = $level->name; $file = basename($file); $formattedMessage = "[$timestamp] <$file:$line> [$levelName] $message" . PHP_EOL; file_put_contents(self::$logPath, $formattedMessage, FILE_APPEND); } /** * Check if log rotation is needed and rotate if necessary */ private static function CheckRotation(): void { if (!file_exists(self::$logPath)) { return; } $sizeKB = filesize(self::$logPath) / 1024; if ($sizeKB >= self::$maxSizeKB) { self::RotateLog(); } } /** * Rotate the log file */ public static function RotateLog(): bool { if (!file_exists(self::$logPath)) { return false; } $timestamp = date("Y-m-d_H-i-s"); $rotatedFileName = "old_logs_{$timestamp}.log.gz"; $rotatedFilePath = dirname(self::$logPath) . DIRECTORY_SEPARATOR . $rotatedFileName; $logContent = file_get_contents(self::$logPath); $compressedContent = gzencode($logContent, 9); if (file_put_contents($rotatedFilePath, $compressedContent) === false) { self::Error("Failed to create rotated log file: {$rotatedFilePath}"); return false; } if (file_put_contents(self::$logPath, "") === false) { self::Error("Failed to clear log file after rotation"); return false; } self::Info("Old log rotated to {$rotatedFileName}"); return true; } /** * Debug level message */ public static function Debug(string $message, array $context = []): void { $key = array_search(__FUNCTION__, array_column(debug_backtrace(), "function")); $file = debug_backtrace()[$key]["file"]; $line = debug_backtrace()[$key]["line"]; self::Write(LogLevel::DBUG, $message, $context, $file, $line); } /** * Info level message */ public static function Info(string $message, array $context = []): void { $key = array_search(__FUNCTION__, array_column(debug_backtrace(), "function")); $file = debug_backtrace()[$key]["file"]; $line = debug_backtrace()[$key]["line"]; self::Write(LogLevel::INFO, $message, $context, $file, $line); } /** * Warning level message */ public static function Warn(string $message, array $context = []): void { $key = array_search(__FUNCTION__, array_column(debug_backtrace(), "function")); $file = debug_backtrace()[$key]["file"]; $line = debug_backtrace()[$key]["line"]; self::Write(LogLevel::WARN, $message, $context, $file, $line); } /** * Error level message */ public static function Error(string $message, array $context = []): void { $key = array_search(__FUNCTION__, array_column(debug_backtrace(), "function")); $file = debug_backtrace()[$key]["file"]; $line = debug_backtrace()[$key]["line"]; self::Write(LogLevel::EROR, $message, $context, $file, $line); } /** * Fatal level message */ public static function Fatal(string $message, array $context = []): void { $key = array_search(__FUNCTION__, array_column(debug_backtrace(), "function")); $file = debug_backtrace()[$key]["file"]; $line = debug_backtrace()[$key]["line"]; self::Write(LogLevel::FTAL, $message, $context, $file, $line); } /** * Get the log file path */ public static function GetLogPath(): string { if (self::$logPath === null) { self::Initialize(); } return self::$logPath; } /** * Clear the log file */ public static function Clear(): bool { if (self::$logPath === null) { self::Initialize(); } return file_put_contents(self::$logPath, "") !== false; } } enum LogLevel: int { case DBUG = 0; case INFO = 1; case WARN = 2; case EROR = 3; case FTAL = 4; public static function tryFromName(string $name): ?LogLevel { return array_find(self::cases(), fn($case) => $case->name === $name); } }