Stefan: "haben wir einen Menupunkt logging? sonst muellen wir uns dicht wenns funktioniert und wir das logging im moment nicht brauchen" Stimmt. reportAppDebug() schickt aktuell IMMER an Bridge, auch wenn gar nicht debuggt wird. Bei armed Wake-Word + Pipeline-Logs sind das schnell ein Dutzend Eintraege pro Wake-Trigger. Loesung: separater Settings-Toggle "Debug-Logs an Bridge" mit eigenem AsyncStorage-Key (aria_debug_logs_to_bridge), Default AUS. - logger.ts: _debugLogsToBridge flag + isDebugLogsToBridge() / setDebugLogsToBridge(). initLogger() laedt den Wert. reportAppDebug() prueft das Flag und schickt nur wenn AN. - SettingsScreen: neuer Toggle direkt unter Verbose-Logging, orange (#FF9500) damit er als "Power-User-Option" erkennbar ist, mit Erklaerungs-Hinweis dass nur Info-Logs gefiltert werden, Crash-Reports (Errors via reportAppError) gehen weiterhin IMMER. Workflow: - Default-User: Toggle aus, kein Traffic, kein Disk-Schreiben - Stefan beim Debuggen: Toggle an, testet die App, schaut Logs via curl /api/app-log?lines=N, schaltet wieder aus APK neu bauen erforderlich. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
158 lines
5.4 KiB
TypeScript
158 lines
5.4 KiB
TypeScript
/**
|
|
* Verbose-Logging-Toggle: console.log laesst sich global stummschalten.
|
|
* console.warn/console.error bleiben immer an — Fehler will man immer sehen.
|
|
*
|
|
* Default: an (true). Toggle ueber Settings → Protokoll → Verbose Logging.
|
|
* Beim Start wird der gespeicherte Wert geladen, vorher loggen wir normal.
|
|
*/
|
|
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import { Platform } from 'react-native';
|
|
import rvs from './rvs';
|
|
|
|
export const VERBOSE_LOGGING_KEY = 'aria_verbose_logging';
|
|
// Eigener Toggle fuer Debug-Logs die ueber RVS an die Bridge gehen
|
|
// (/shared/logs/app.log → Diagnostic /api/app-log). Damit der Default-User
|
|
// nicht stuendlich Traffic + Disk-Schreiben hat, dieser ist DEFAULT AUS.
|
|
// Stefan schaltet's nur ein wenn er ein konkretes Problem debuggen muss.
|
|
export const DEBUG_LOGS_TO_BRIDGE_KEY = 'aria_debug_logs_to_bridge';
|
|
|
|
// Original-console.log retten, damit wir die Wrapper jederzeit wieder
|
|
// "scharf" stellen koennen (sonst waere ein Toggle-an nach -aus tot).
|
|
const originalLog = console.log.bind(console);
|
|
const noop = () => {};
|
|
|
|
let _verbose = true;
|
|
let _debugLogsToBridge = false;
|
|
|
|
function applyState(): void {
|
|
console.log = _verbose ? originalLog : noop;
|
|
}
|
|
|
|
/** Wert aus AsyncStorage laden und anwenden. Beim App-Start aufrufen. */
|
|
export async function initLogger(): Promise<void> {
|
|
try {
|
|
const v = await AsyncStorage.getItem(VERBOSE_LOGGING_KEY);
|
|
_verbose = v !== 'false'; // default: true
|
|
} catch {}
|
|
try {
|
|
const d = await AsyncStorage.getItem(DEBUG_LOGS_TO_BRIDGE_KEY);
|
|
_debugLogsToBridge = d === 'true'; // default: false
|
|
} catch {}
|
|
applyState();
|
|
}
|
|
|
|
export function isVerboseLogging(): boolean {
|
|
return _verbose;
|
|
}
|
|
|
|
export function setVerboseLogging(verbose: boolean): void {
|
|
_verbose = verbose;
|
|
applyState();
|
|
AsyncStorage.setItem(VERBOSE_LOGGING_KEY, String(verbose)).catch(() => {});
|
|
}
|
|
|
|
export function isDebugLogsToBridge(): boolean {
|
|
return _debugLogsToBridge;
|
|
}
|
|
|
|
export function setDebugLogsToBridge(enabled: boolean): void {
|
|
_debugLogsToBridge = enabled;
|
|
AsyncStorage.setItem(DEBUG_LOGS_TO_BRIDGE_KEY, String(enabled)).catch(() => {});
|
|
}
|
|
|
|
// ─── App-Crash-Reporting via RVS ────────────────────────────────────
|
|
//
|
|
// Wenn die App crasht — egal ob React-Render-Fehler (ErrorBoundary) oder
|
|
// ungefangener JS-Error (ErrorUtils-Handler) — schicken wir den Crash
|
|
// als RVS-Message vom Typ "app_log" an die Bridge. Die schreibt in
|
|
// /shared/logs/app.log, sodass wir/Diagnostic die Crashes mitlesen
|
|
// koennen ohne ADB.
|
|
|
|
interface AppErrorEvent {
|
|
scope: string;
|
|
message: string;
|
|
stack?: string;
|
|
level?: 'error' | 'warn' | 'info';
|
|
}
|
|
|
|
let _reportingInstalled = false;
|
|
|
|
/** Schickt einen App-Fehler via RVS an die Bridge. */
|
|
export function reportAppError(ev: AppErrorEvent): void {
|
|
try {
|
|
rvs.send('app_log' as any, {
|
|
ts: Date.now(),
|
|
platform: Platform.OS,
|
|
level: ev.level || 'error',
|
|
scope: ev.scope,
|
|
message: ev.message,
|
|
stack: (ev.stack || '').slice(0, 8000),
|
|
});
|
|
} catch {
|
|
// RVS noch nicht connected — Fehler geht im console weiter.
|
|
}
|
|
// Plus lokal: console.error, damit Stefan's adb (wenn doch mal verfuegbar)
|
|
// den Crash sieht.
|
|
console.error(`[app-error scope=${ev.scope}]`, ev.message, '\n', ev.stack || '');
|
|
}
|
|
|
|
/** Schickt eine Debug-/Info-Message via RVS an die Bridge. Landet ebenfalls
|
|
* in /shared/logs/app.log — abrufbar via `curl /api/app-log?lines=N`.
|
|
* Im Gegensatz zu reportAppError: keine Stacktrace, level=info, kein
|
|
* console.error. Fuer Live-Diagnose im Hintergrund wenn ADB nicht da ist.
|
|
*
|
|
* Nur aktiv wenn Settings → Protokoll → Debug-Logs an Bridge AN ist.
|
|
* Default aus damit Mama-Modus keine Disk-Schreiblast hat. Error-Reports
|
|
* (reportAppError) gehen weiterhin IMMER durch. */
|
|
export function reportAppDebug(scope: string, message: string): void {
|
|
if (!_debugLogsToBridge) return;
|
|
try {
|
|
rvs.send('app_log' as any, {
|
|
ts: Date.now(),
|
|
platform: Platform.OS,
|
|
level: 'info',
|
|
scope,
|
|
message: String(message).slice(0, 2000),
|
|
});
|
|
} catch {}
|
|
}
|
|
|
|
/** Installiert einen globalen JS-Error-Handler der ungefangene Errors via
|
|
* RVS an die Bridge schickt. Beim App-Start aufrufen. */
|
|
export function installGlobalCrashReporter(): void {
|
|
if (_reportingInstalled) return;
|
|
_reportingInstalled = true;
|
|
try {
|
|
const g: any = global as any;
|
|
const prev = g.ErrorUtils?.getGlobalHandler?.();
|
|
g.ErrorUtils?.setGlobalHandler?.((err: any, isFatal: boolean) => {
|
|
reportAppError({
|
|
scope: isFatal ? 'global-fatal' : 'global-nonfatal',
|
|
message: (err && err.message) || String(err),
|
|
stack: err && err.stack,
|
|
});
|
|
// Original-Handler weiterhin aufrufen damit React-Native das System-
|
|
// Crash-Overlay zeigt (im Dev-Build) bzw. in Production sauber stirbt.
|
|
if (typeof prev === 'function') {
|
|
try { prev(err, isFatal); } catch {}
|
|
}
|
|
});
|
|
// unhandled Promise-Rejections — manche RN-Versionen haben das nicht
|
|
// automatisch im ErrorUtils.
|
|
g.HermesInternal?.enablePromiseRejectionTracker?.({
|
|
allRejections: true,
|
|
onUnhandled: (id: number, err: any) => {
|
|
reportAppError({
|
|
scope: 'promise-unhandled',
|
|
level: 'warn',
|
|
message: (err && err.message) || String(err),
|
|
stack: err && err.stack,
|
|
});
|
|
},
|
|
});
|
|
} catch {
|
|
// ErrorUtils nicht da → nix machen
|
|
}
|
|
}
|