Give the PHP layer somewhere to record what it swallowed
This commit is contained in:
@@ -170,7 +170,7 @@ if (is_dir(LOG_DIR)) {
|
||||
if ($lastErr !== null)
|
||||
$errors[] = ['script' => $script, 'line' => $lastErr, 'ts' => (int)$lf->getMTime()];
|
||||
}
|
||||
} catch (Exception $e) {}
|
||||
} catch (Exception $e) { vv_log_error('api/board.php', 'log walk failed: ' . $e->getMessage()); }
|
||||
usort($errors, fn($a, $b) => $b['ts'] - $a['ts']);
|
||||
}
|
||||
$out['errors'] = array_slice($errors, 0, 20);
|
||||
|
||||
@@ -76,7 +76,7 @@ try {
|
||||
'dur' => isset($d['end']) ? max(0, (int)$d['end'] - (int)$d['start']) : 0,
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {}
|
||||
} catch (Exception $e) { vv_log_error('api/recent.php', 'run-log walk failed: ' . $e->getMessage()); }
|
||||
|
||||
usort($runs, fn($a, $b) => $b['start'] - $a['start']);
|
||||
echo json_encode(['ok' => true, 'runs' => array_slice($runs, 0, 24)]);
|
||||
|
||||
@@ -123,7 +123,7 @@ if ($action === 'list_scripts') {
|
||||
$label = str_replace('_', ' ', basename($rel, '.sh'));
|
||||
$groups[$folder][] = ['id' => $rel, 'label' => $label];
|
||||
}
|
||||
} catch (Exception $e) {}
|
||||
} catch (Exception $e) { vv_log_error('api/rsync_win_arrays.php', 'script tree walk failed: ' . $e->getMessage()); }
|
||||
ksort($groups);
|
||||
foreach ($groups as &$g) usort($g, fn($a, $b) => strcmp($a['id'], $b['id']));
|
||||
echo json_encode(['ok' => true, 'groups' => $groups]);
|
||||
|
||||
@@ -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 ───────────────────────────────────────────────────
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ try {
|
||||
if (in_array($_parts[0], $_repoSkip)) continue;
|
||||
$_repoFiles[] = $_rel;
|
||||
}
|
||||
} catch (Exception $_re) {}
|
||||
} catch (Exception $_re) { vv_log_error('pages/scheduler.php', 'repo file walk failed: ' . $_re->getMessage()); }
|
||||
sort($_repoFiles);
|
||||
|
||||
// Docs tree: README and Manual with their per-module children
|
||||
|
||||
Reference in New Issue
Block a user