Give the PHP layer somewhere to record what it swallowed

This commit is contained in:
Gmer4Lfe
2026-08-24 18:51:36 -04:00
parent 492757ce70
commit eda411c0be
6 changed files with 55 additions and 5 deletions
+50
View File
@@ -160,6 +160,56 @@ define('LOG_DIR', '/var/log/varaverk');
define('CUSTOM_SCRIPTS_DIR', $_vv_cfg['CUSTOM_SCRIPTS_DIR'] ?? '/boot/config/plugins/user.scripts/Varaverk/Scripts');
unset($_vv_cfg);
// ── Recording a failure this layer chose to survive ───────────────────────────────────────────
// The bash half writes a log for every run. The PHP half had nowhere to write at all — no
// error_log() call in 107 files — so an endpoint that caught an exception and carried on left no
// trace anywhere. Five directory walks do exactly that, and an unreadable directory renders as a
// legitimately empty result: the scheduler's script library shows "no scripts" whether you have
// none or the tree could not be read.
//
// This is the smallest thing that fixes it. It does not change any response contract; it records
// what was swallowed so the operator can find out why a page went empty.
//
// It lives in config.php because that is the one file every layer reaches — api/, include/ and
// pages/ all load it, several of them without ever loading common.php.
define('VV_PHP_LOG', LOG_DIR . '/php.log');
define('VV_PHP_LOG_MAX', 262144); // 256KB. /var/log is tmpfs on Unraid — this is RAM, so a
// repeating fault must not be able to grow without bound.
/**
* Record a failure that was caught and survived. Never throws, never echoes, never affects the
* response — a logger that can break a request is worse than no logger.
*/
function vv_log_error(string $where, string $message): void {
try {
if (!is_dir(LOG_DIR)) return;
// Timestamps are local, matching every bash log — see "The clock this layer runs on".
$line = date('Y-m-d H:i:s') . ' ' . $where . ' '
. preg_replace('/\s+/', ' ', trim($message)) . "\n";
$size = @filesize(VV_PHP_LOG);
if ($size !== false && $size > VV_PHP_LOG_MAX) {
// Drop the older half rather than the whole file, so a fault that repeats every poll
// still leaves recent context instead of a log that empties itself at random.
$keep = @file_get_contents(VV_PHP_LOG, false, null, (int)(VV_PHP_LOG_MAX / 2));
if ($keep !== false) {
// Resume on a line boundary — slicing at a byte offset lands mid-line, and a
// half-written first entry is worse than one fewer entry.
$nl = strpos($keep, "\n");
$keep = $nl === false ? '' : substr($keep, $nl + 1);
@file_put_contents(VV_PHP_LOG, $keep, LOCK_EX);
}
}
$isNew = !file_exists(VV_PHP_LOG);
@file_put_contents(VV_PHP_LOG, $line, FILE_APPEND | LOCK_EX);
// Do not inherit the ambient umask. php-fpm's is loose enough to create this 0666, and a
// world-writable file under /var/log is the kind of small thing that is never noticed.
if ($isNew) @chmod(VV_PHP_LOG, 0644);
} catch (Throwable $_) {
return; // Deliberately silent: this is the last place an error should propagate from.
}
}
define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');
// ── The two install layouts ───────────────────────────────────────────────────
+1 -1
View File
@@ -468,7 +468,7 @@ function vv_script_library(): array {
if (in_array($rel, $orchIds) || isset($confMap[$rel]) || isset($cardIds[$rel]) || isset($schedule[$rel])) continue;
$library[] = ['id' => $rel, 'label' => vv_pretty_label(basename($rel, '.sh'))];
}
} catch (Exception $e) {}
} catch (Exception $e) { vv_log_error('include/scheduler.php', 'script library walk failed: ' . $e->getMessage()); }
usort($library, fn($a, $b) => strcmp($a['id'], $b['id']));
return $library;
}