= 1; $i--) { $source = $i === 1 ? $file : $file . "." . ($i - 1); $target = $file . "." . $i; if (!is_file($source)) { continue; } if ($i === $keepFiles && is_file($target)) { @unlink($target); } @rename($source, $target); } manageDeleteExpiredLogFiles($file); } function manageDeleteExpiredLogFiles(string $file): void { $maxAge = (int) MANAGE_LOG_MAX_AGE_SECONDS; if ($maxAge < 1) { return; } foreach (glob($file . ".*") ?: [] as $rotated) { $mtime = @filemtime($rotated); if ($mtime !== false && time() - $mtime > $maxAge) { @unlink($rotated); } } } function manageWriteLogEntry(string $file, array $entry): void { try { manageEnsureDirectory(dirname($file)); } catch (Throwable $exception) { return; } manageRotateLogFileIfNeeded($file); $line = json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($line === false) { return; } @file_put_contents($file, $line . PHP_EOL, FILE_APPEND | LOCK_EX); } function manageLogError(string $message, array $context = [], string $level = "ERROR"): void { manageWriteLogEntry(manageErrorLogFile(), [ "timestamp" => date("Y-m-d H:i:s.u"), "level" => $level, "message" => $message, "context" => $context, "ip" => $_SERVER["REMOTE_ADDR"] ?? "unknown", "user_agent" => substr((string) ($_SERVER["HTTP_USER_AGENT"] ?? "unknown"), 0, 300), "request_uri" => $_SERVER["REQUEST_URI"] ?? "unknown", ]); } function manageLogAccess(string $message, array $context = []): void { manageWriteLogEntry(manageAccessLogFile(), [ "timestamp" => date("Y-m-d H:i:s.u"), "message" => $message, "context" => $context, "ip" => $_SERVER["REMOTE_ADDR"] ?? "unknown", "request_method" => $_SERVER["REQUEST_METHOD"] ?? "unknown", "request_uri" => $_SERVER["REQUEST_URI"] ?? "unknown", ]); } // Plain-text append-only log for S3 diagnostics. Kept separate from the JSONL // logs so the raw provider error stays readable in the management UI. function manageLogS3(string $message, array $context = []): void { try { manageEnsureDirectory(dirname(manageS3LogFile())); } catch (Throwable $exception) { return; } $line = date(DATE_ATOM) . " " . $message; $encoded = @json_encode($context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if (is_string($encoded) && $encoded !== "[]") { $line .= " " . $encoded; } @file_put_contents(manageS3LogFile(), $line . PHP_EOL, FILE_APPEND | LOCK_EX); } function manageLogS3Tail(int $lines): array { $file = manageS3LogFile(); if (!is_file($file) || $lines < 1) { return []; } $content = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if (!is_array($content)) { return []; } return array_slice($content, -$lines); }