Add extended diagnostics logging
- Electron crash handlers (render-process-gone, child-process-gone, unresponsive/responsive, process warnings) with a circuit-breaker auto-reload for renderer crashes - Renderer error capture (window.onerror, unhandledrejection, React ErrorBoundary) forwarded to the main log via a one-way IPC channel - Memory-pressure heartbeat measured against the V8 heap_size_limit - Gated DEBUG log level (RD_DEBUG) and an in-memory ring of recent WARN/ERROR lines, exposed via the /errors endpoint and support bundle - Disk-error classification (ENOSPC etc.) on download failures and integrity-check pass/fail logging
This commit is contained in:
+29
-2
@@ -1,7 +1,24 @@
|
||||
import fs from "node:fs";
|
||||
import { logTimestamp } from "./log-timestamp";
|
||||
import { recordRecentError } from "./error-ring";
|
||||
import path from "node:path";
|
||||
|
||||
export function isDebugFlagEnabled(value: string | undefined): boolean {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
return /^(1|true|yes|on)$/i.test(value.trim());
|
||||
}
|
||||
|
||||
// Read once at startup. Enabling verbose DEBUG logging on the (unattended) server
|
||||
// is a deliberate support action that requires a restart — the runtime-toggleable
|
||||
// channel is the trace log, not this.
|
||||
const DEBUG_ENABLED = isDebugFlagEnabled(process.env.RD_DEBUG);
|
||||
|
||||
export function isDebugLoggingEnabled(): boolean {
|
||||
return DEBUG_ENABLED;
|
||||
}
|
||||
|
||||
let logFilePath = path.resolve(process.cwd(), "rd_downloader.log");
|
||||
let fallbackLogFilePath: string | null = null;
|
||||
const LOG_FLUSH_INTERVAL_MS = 120;
|
||||
@@ -204,12 +221,19 @@ function ensureExitHook(): void {
|
||||
process.once("exit", flushSyncPending);
|
||||
}
|
||||
|
||||
function write(level: "INFO" | "WARN" | "ERROR", message: string): void {
|
||||
function write(level: "DEBUG" | "INFO" | "WARN" | "ERROR", message: string): void {
|
||||
ensureExitHook();
|
||||
const line = `${logTimestamp()} [${level}] ${message}\n`;
|
||||
const ts = logTimestamp();
|
||||
const line = `${ts} [${level}] ${message}\n`;
|
||||
pendingLines.push(line);
|
||||
pendingChars += line.length;
|
||||
|
||||
// Single chokepoint: every WARN/ERROR also lands in the in-memory ring so
|
||||
// "what failed recently" is answerable even after the file rotates.
|
||||
if (level === "ERROR" || level === "WARN") {
|
||||
recordRecentError(level, message, ts);
|
||||
}
|
||||
|
||||
for (const listener of logListeners) {
|
||||
try { listener(line); } catch { }
|
||||
}
|
||||
@@ -230,6 +254,9 @@ function write(level: "INFO" | "WARN" | "ERROR", message: string): void {
|
||||
}
|
||||
|
||||
export const logger = {
|
||||
// Gated to a no-op when RD_DEBUG is unset so verbose call sites cost nothing
|
||||
// (no formatting, no allocation) in the normal/production path.
|
||||
debug: DEBUG_ENABLED ? (msg: string): void => write("DEBUG", msg) : (_msg: string): void => {},
|
||||
info: (msg: string): void => write("INFO", msg),
|
||||
warn: (msg: string): void => write("WARN", msg),
|
||||
error: (msg: string): void => write("ERROR", msg)
|
||||
|
||||
Reference in New Issue
Block a user