diff --git a/api/poller_online.php b/api/poller_online.php index 3787b14..837fd9f 100644 --- a/api/poller_online.php +++ b/api/poller_online.php @@ -45,6 +45,11 @@ function debugLog($msg, $ctx = []) { if ($ctx) $entry .= ' | Context: ' . json_encode($ctx, JSON_UNESCAPED_UNICODE); $entry .= "\n"; @file_put_contents($logFile, $entry, FILE_APPEND | LOCK_EX); + // ★ 保留策略(2026-10-05):只留最近3天 + ≤5MB,任一超标即清理到两条件同时满足 + if (is_file(__DIR__ . '/../log_maintain.php')) { + @require_once __DIR__ . '/../log_maintain.php'; + if (function_exists('logMaintain')) { @logMaintain($logFile); } + } } } // end if (!function_exists('debugLog')) diff --git a/captcha_guard.php b/captcha_guard.php index f98b372..ec84c24 100644 --- a/captcha_guard.php +++ b/captcha_guard.php @@ -119,7 +119,13 @@ function cgLog($msg) { } $dir = __DIR__ . '/db'; if (!is_dir($dir)) { @mkdir($dir, 0755, true); } - @file_put_contents($dir . '/debug.log', $line, FILE_APPEND | LOCK_EX); + $f = $dir . '/debug.log'; + @file_put_contents($f, $line, FILE_APPEND | LOCK_EX); + // ★ 保留策略(2026-10-05):只留最近3天 + ≤5MB,任一超标即清理到两条件同时满足 + if (is_file(__DIR__ . '/log_maintain.php')) { + @require_once __DIR__ . '/log_maintain.php'; + if (function_exists('logMaintain')) { @logMaintain($f); } + } } // ==================================================================== diff --git a/core.php b/core.php index 6b56831..e49923f 100644 --- a/core.php +++ b/core.php @@ -26,6 +26,12 @@ function debugLog($message, $context = []) { } file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX); + + // ★ 保留策略(2026-10-05):只留最近3天 + ≤5MB,任一超标即清理到两条件同时满足 + if (is_file(__DIR__ . '/log_maintain.php')) { + @require_once __DIR__ . '/log_maintain.php'; + if (function_exists('logMaintain')) { @logMaintain($logFile); } + } } // ===== 数据库初始化 ===== diff --git a/log_maintain.php b/log_maintain.php new file mode 100644 index 0000000..230f3a4 --- /dev/null +++ b/log_maintain.php @@ -0,0 +1,165 @@ + 0) return $ts; + } + return 0; +} + +/** + * 重建日志:过滤掉 cutoff 之前的行;若剩余仍 > maxBytes,再从最旧端按行丢弃到 ≤ maxBytes。 + * 调用方必须已持有 $fh 的排他锁。 + * @param resource $fh 日志文件句柄(读写) + * @param string $logFile 日志路径(临时文件建在同目录) + * @return bool 成功 true + */ +function logMaintainRebuild($fh, $logFile, $maxBytes, $cutoff) { + $tmp = $logFile . '.tmp'; + + // ===== 第一遍:读原文件,过滤超龄行,写入临时文件 ===== + $out = @fopen($tmp, 'wb'); + if (!$out) return false; + if (rewind($fh) === false) { fclose($out); @unlink($tmp); return false; } + + $keepPrev = true; // 无时间戳的续行跟随上一行;首行无时间戳时保守保留 + $kept = 0; + while (($line = fgets($fh)) !== false) { + $ts = logMaintainLineTs($line); + if ($ts > 0) { + $keepPrev = ($ts >= $cutoff); + } + if ($keepPrev) { + fwrite($out, $line); + $kept += strlen($line); + } + } + fflush($out); + fclose($out); + clearstatcache(true, $tmp); + + // ===== 第二步:剩余仍超 5MB → 从最旧端按整行丢弃到 ≤ maxBytes ===== + $skipBytes = ($kept > $maxBytes) ? ($kept - $maxBytes) : 0; + + $in = @fopen($tmp, 'rb'); + if (!$in) { @unlink($tmp); return false; } + if ($skipBytes > 0) { + $acc = 0; + // 逐行累计丢弃;按整行丢,最终大小 kept-acc 必 ≤ maxBytes + while ($acc < $skipBytes && ($line = fgets($in)) !== false) { + $acc += strlen($line); + } + } + + // ===== 写回日志本体(仍持锁,写入方在锁外等待,不会丢)===== + if (ftruncate($fh, 0) === false) { fclose($in); @unlink($tmp); return false; } + rewind($fh); + while (!feof($in)) { + $chunk = fread($in, 1048576); + if ($chunk === false) break; + if ($chunk !== '') fwrite($fh, $chunk); + } + fflush($fh); + fclose($in); + @unlink($tmp); + return true; +} + +/** + * 入口:日志写入后调用。任一条件超标(>5MB 或 含 3 天前的行)即清理, + * 清理到两个条件同时满足为止(最多 3 轮,正常第 1 轮后复查即通过)。 + * + * @param string $logFile 日志文件绝对路径 + * @param int $maxBytes 大小上限(默认 5MB) + * @param int $keepDays 保留天数(默认 3 天) + * @return bool true=干净(无需清理或已清理完成);false=清理失败 + */ +function logMaintain($logFile, $maxBytes = 5242880, $keepDays = 3) { + if (!is_string($logFile) || $logFile === '') return true; + clearstatcache(true, $logFile); + if (!is_file($logFile)) return true; + $size = @filesize($logFile); + if ($size === false || $size <= 0) return true; + + $cutoff = time() - $keepDays * 86400; + + // ---- 廉价预检:两条都没超标就直接返回(绝大多数调用在这里结束)---- + $need = ($size > $maxBytes); + if (!$need) { + $fh = @fopen($logFile, 'rb'); + if ($fh) { + $oldest = logMaintainOldestTs($fh); + fclose($fh); + $need = ($oldest > 0 && $oldest < $cutoff); + } + } + if (!$need) return true; + + // ---- 清理:排他锁内执行(写入方 file_put_contents(LOCK_EX) 短暂等待)---- + $fh = @fopen($logFile, 'c+'); + if (!$fh) return false; + if (!flock($fh, LOCK_EX)) { fclose($fh); return false; } + + $ok = false; + for ($round = 0; $round < 3; $round++) { + // 锁内复查:可能别的进程刚清理完 + $stat = fstat($fh); + $size = $stat ? (int) $stat['size'] : 0; + $oldest = logMaintainOldestTs($fh); + if ($size <= $maxBytes && !($oldest > 0 && $oldest < $cutoff)) { + $ok = true; + break; + } + if (!logMaintainRebuild($fh, $logFile, $maxBytes, $cutoff)) { + break; // 重建失败(如目录不可写)→ 解锁退出,下次写入重试 + } + } + + flock($fh, LOCK_UN); + fclose($fh); + clearstatcache(true, $logFile); + return $ok; +} + +} // end if (!function_exists('logMaintain')) diff --git a/orders.php b/orders.php index 9dbbdd5..fd96344 100644 --- a/orders.php +++ b/orders.php @@ -15,7 +15,13 @@ set_error_handler(function($errno, $errstr, $errfile, $errline) { // core.php may not be loaded yet, define minimal debugLog if (!function_exists('debugLog')) { function debugLog($msg) { - @file_put_contents(__DIR__ . '/db/debug.log', "[" . date('Y-m-d H:i:s') . "] [orders.php] $msg\n", FILE_APPEND | LOCK_EX); + $lf = __DIR__ . '/db/debug.log'; + @file_put_contents($lf, "[" . date('Y-m-d H:i:s') . "] [orders.php] $msg\n", FILE_APPEND | LOCK_EX); + // ★ 保留策略(2026-10-05):只留最近3天 + ≤5MB,任一超标即清理到两条件同时满足 + if (is_file(__DIR__ . '/log_maintain.php')) { + @require_once __DIR__ . '/log_maintain.php'; + if (function_exists('logMaintain')) { @logMaintain($lf); } + } } } debugLog("[orders.php] PHP {$type}: {$errstr} in {$errfile}:{$errline}");