| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- <?php
- declare(strict_types=1);
- // JSONL logging with size-based rotation. Ported from the PSA order system
- // (includes/functions.php logError/logAccess/rotateLogFileIfNeeded).
- function manageErrorLogFile(): string
- {
- return manageLogsDir() . "error.log";
- }
- function manageAccessLogFile(): string
- {
- return manageLogsDir() . "access.log";
- }
- function manageRotateLogFileIfNeeded(string $file): void
- {
- $maxBytes = (int) MANAGE_LOG_MAX_BYTES;
- if ($maxBytes < 1 || !is_file($file)) {
- return;
- }
- $size = filesize($file);
- if ($size === false || $size < $maxBytes) {
- manageDeleteExpiredLogFiles($file);
- return;
- }
- $keepFiles = max(1, (int) MANAGE_LOG_KEEP_FILES);
- for ($i = $keepFiles; $i >= 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);
- }
|