feat(admin): add transparent debug error-log mode

Add an admin-controlled debug mode that captures PHP errors of all
players into var/log/debug-players.log, to hunt remaining PHP 8.3 bugs
from real play sessions. Fully transparent to players: no redirect, no
gameplay change, errors are never displayed.

- DB: new debug_log table (one row), mirroring the maintenance pattern.
- Database: getDebugMode()/setDebugMode()/setDebugSettings(), defensive
  when the table is absent (no blank page).
- Session: register a custom error + shutdown handler when enabled; the
  handler runs even when php.ini error_reporting masks warnings/notices,
  so capture is complete without a Docker rebuild. Auto-disables after a
  configurable window.
- DebugErrorLogger: size-capped file with a single .log.1 rotation,
  honours the @ operator, never throws.
- Admin: new "Debug Error Log" page (levels, size cap, auto-off, on-page
  viewer, clear, download) + debugLog action mod.
- Menu: admin-only quick on/off widget (TZ_DEBUG_ON/OFF, EN/FR/RO).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ferywir
2026-06-12 15:25:57 +02:00
committed by Catalin Novgorodschi
parent 63c94fa961
commit 827354a622
12 changed files with 563 additions and 3 deletions
+61 -3
View File
@@ -8878,12 +8878,70 @@ public function setMaintenance($active, $uid=0) {
$active = (int)$active;
$uid = (int)$uid;
// REPLACE creează rândul dacă nu există
return $this->query("REPLACE INTO ".TB_PREFIX."maintenance
(id, active, started_by, started_at)
return $this->query("REPLACE INTO ".TB_PREFIX."maintenance
(id, active, started_by, started_at)
VALUES (1, $active, $uid, $time)");
}
/**
* Debug error-log mode (admin-controlled, transparent to players).
* Returns the single config row, falling back to safe defaults when the
* table does not exist yet (so deploying the code before creating the table
* never produces a blank page).
*/
public function getDebugMode() {
$default = [
'active' => 0,
'lvl_warning' => 1,
'lvl_notice' => 1,
'lvl_deprecated' => 1,
'lvl_fatal' => 1,
'max_size_mb' => 5,
'auto_off_hours' => 6,
'started_by' => null,
'started_at' => null,
];
try {
$res = @mysqli_query($this->dblink, "SELECT * FROM ".TB_PREFIX."debug_log WHERE id=1 LIMIT 1");
if (!$res) {
return $default;
}
$row = mysqli_fetch_assoc($res);
return $row ?: $default;
} catch (\Throwable $e) {
return $default;
}
}
/**
* Toggle the debug mode on/off (stamps who/when on activation).
*/
public function setDebugMode($active, $uid = 0) {
$active = (int)$active;
$uid = (int)$uid;
$time = time();
return $this->query("UPDATE ".TB_PREFIX."debug_log
SET active = $active, started_by = $uid, started_at = $time
WHERE id = 1");
}
/**
* Persist the debug capture parameters (levels, size cap, auto-off window).
*/
public function setDebugSettings($warning, $notice, $deprecated, $fatal, $maxSizeMb, $autoOffHours) {
$warning = $warning ? 1 : 0;
$notice = $notice ? 1 : 0;
$deprecated = $deprecated ? 1 : 0;
$fatal = $fatal ? 1 : 0;
$maxSizeMb = max(1, (int)$maxSizeMb);
$autoOffHours = max(0, (int)$autoOffHours);
return $this->query("UPDATE ".TB_PREFIX."debug_log
SET lvl_warning = $warning, lvl_notice = $notice, lvl_deprecated = $deprecated,
lvl_fatal = $fatal, max_size_mb = $maxSizeMb, auto_off_hours = $autoOffHours
WHERE id = 1");
}
/**
* Changed the actual capital with a new one
*