feat(logger): native App-Crashes ueber RVS erkennen (ohne adb)
Native Crashes/OOM schreiben keinen JS-Fehler → tauchten in app.log
nirgends auf (nur der Whisper-Reboot als Symptom). Jetzt: ein RUN_MARKER
bleibt gesetzt solange die App aktiv laeuft, wird bei sauberem Hintergrund-
Wechsel geloescht. Ist er beim naechsten Start noch da, ist der vorige Lauf
unsauber gestorben → Report via RVS ('app.crash-detected') mit dem letzten
Breadcrumb (was die App zuletzt tat) + Zeitabstand. Breadcrumbs kommen aus
reportAppError/reportAppDebug, throttled in AsyncStorage persistiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { Platform, DeviceEventEmitter } from 'react-native';
|
import { Platform, DeviceEventEmitter, AppState } from 'react-native';
|
||||||
import rvs from './rvs';
|
import rvs from './rvs';
|
||||||
|
|
||||||
// Lokales Event damit die SettingsScreen Live Logs / Events Tabs
|
// Lokales Event damit die SettingsScreen Live Logs / Events Tabs
|
||||||
@@ -38,6 +38,23 @@ const noop = () => {};
|
|||||||
let _verbose = true;
|
let _verbose = true;
|
||||||
let _debugLogsToBridge = false;
|
let _debugLogsToBridge = false;
|
||||||
|
|
||||||
|
// ─── Crash-Kontext ohne adb ─────────────────────────────────────────
|
||||||
|
// Ein RUN_MARKER bleibt gesetzt, solange die App AKTIV laeuft; bei sauberem
|
||||||
|
// Wechsel in den Hintergrund wird er geloescht. Ist er beim naechsten Start
|
||||||
|
// noch da, ist der vorige Lauf unsauber gestorben (nativer Crash/OOM — der
|
||||||
|
// schreibt KEINEN JS-Fehler, taucht also sonst nirgends auf). Wir melden das
|
||||||
|
// dann via RVS mit dem letzten Breadcrumb (was die App zuletzt tat).
|
||||||
|
const RUN_MARKER_KEY = 'aria_run_marker';
|
||||||
|
const BREADCRUMB_KEY = 'aria_last_breadcrumb';
|
||||||
|
let _breadcrumb: { ts: number; scope: string; message: string } = { ts: 0, scope: '', message: '' };
|
||||||
|
let _breadcrumbDirty = false;
|
||||||
|
|
||||||
|
/** Letzte App-Aktivitaet merken — Crash-Kontext fuer den naechsten Boot. */
|
||||||
|
export function noteBreadcrumb(scope: string, message: string): void {
|
||||||
|
_breadcrumb = { ts: Date.now(), scope: scope || '', message: String(message || '').slice(0, 120) };
|
||||||
|
_breadcrumbDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
function applyState(): void {
|
function applyState(): void {
|
||||||
console.log = _verbose ? originalLog : noop;
|
console.log = _verbose ? originalLog : noop;
|
||||||
}
|
}
|
||||||
@@ -53,6 +70,45 @@ export async function initLogger(): Promise<void> {
|
|||||||
_debugLogsToBridge = d === 'true'; // default: false
|
_debugLogsToBridge = d === 'true'; // default: false
|
||||||
} catch {}
|
} catch {}
|
||||||
applyState();
|
applyState();
|
||||||
|
await _initCrashDetection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native-Crash-Erkennung (ohne adb) — siehe RUN_MARKER-Kommentar oben.
|
||||||
|
async function _initCrashDetection(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const marker = await AsyncStorage.getItem(RUN_MARKER_KEY);
|
||||||
|
if (marker) {
|
||||||
|
let bc: any = {};
|
||||||
|
try { bc = JSON.parse((await AsyncStorage.getItem(BREADCRUMB_KEY)) || '{}'); } catch {}
|
||||||
|
const gap = bc && bc.ts ? Math.round((Date.now() - bc.ts) / 1000) : -1;
|
||||||
|
// Verzoegert melden — RVS ist beim Boot oft noch nicht verbunden.
|
||||||
|
setTimeout(() => {
|
||||||
|
reportAppError({
|
||||||
|
scope: 'app.crash-detected',
|
||||||
|
level: 'warn',
|
||||||
|
message: `Voriger Lauf ohne sauberes Shutdown beendet (nativer Crash/OOM?). `
|
||||||
|
+ `Letzte Aktivitaet: [${(bc && bc.scope) || '?'}] ${(bc && bc.message) || '?'}`
|
||||||
|
+ (gap >= 0 ? ` (vor ~${gap}s)` : ''),
|
||||||
|
});
|
||||||
|
}, 6000);
|
||||||
|
}
|
||||||
|
await AsyncStorage.setItem(RUN_MARKER_KEY, String(Date.now()));
|
||||||
|
} catch {}
|
||||||
|
// Breadcrumb throttled persistieren (alle 5s, nur wenn geaendert).
|
||||||
|
setInterval(() => {
|
||||||
|
if (_breadcrumbDirty) {
|
||||||
|
_breadcrumbDirty = false;
|
||||||
|
AsyncStorage.setItem(BREADCRUMB_KEY, JSON.stringify(_breadcrumb)).catch(() => {});
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
// Sauberer Hintergrund-Wechsel → Marker weg (kein Crash). Rueckkehr → wieder
|
||||||
|
// scharf. So melden nur echte Aktiv-Crashes, kein normales Backgrounden.
|
||||||
|
try {
|
||||||
|
AppState.addEventListener('change', (s) => {
|
||||||
|
if (s === 'background') AsyncStorage.removeItem(RUN_MARKER_KEY).catch(() => {});
|
||||||
|
else if (s === 'active') AsyncStorage.setItem(RUN_MARKER_KEY, String(Date.now())).catch(() => {});
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVerboseLogging(): boolean {
|
export function isVerboseLogging(): boolean {
|
||||||
@@ -94,6 +150,7 @@ let _reportingInstalled = false;
|
|||||||
/** Schickt einen App-Fehler via RVS an die Bridge. */
|
/** Schickt einen App-Fehler via RVS an die Bridge. */
|
||||||
export function reportAppError(ev: AppErrorEvent): void {
|
export function reportAppError(ev: AppErrorEvent): void {
|
||||||
const ts = Date.now();
|
const ts = Date.now();
|
||||||
|
noteBreadcrumb(ev.scope, ev.message);
|
||||||
try {
|
try {
|
||||||
rvs.send('app_log' as any, {
|
rvs.send('app_log' as any, {
|
||||||
ts,
|
ts,
|
||||||
@@ -128,6 +185,9 @@ export function reportAppError(ev: AppErrorEvent): void {
|
|||||||
* Default aus damit Mama-Modus keine Disk-Schreiblast hat. Error-Reports
|
* Default aus damit Mama-Modus keine Disk-Schreiblast hat. Error-Reports
|
||||||
* (reportAppError) gehen weiterhin IMMER durch. */
|
* (reportAppError) gehen weiterhin IMMER durch. */
|
||||||
export function reportAppDebug(scope: string, message: string): void {
|
export function reportAppDebug(scope: string, message: string): void {
|
||||||
|
// Breadcrumb IMMER aktualisieren (auch wenn Debug-Logs-an-Bridge aus ist) —
|
||||||
|
// fuer den Crash-Kontext beim naechsten Boot.
|
||||||
|
noteBreadcrumb(scope, message);
|
||||||
if (!_debugLogsToBridge) return;
|
if (!_debugLogsToBridge) return;
|
||||||
const ts = Date.now();
|
const ts = Date.now();
|
||||||
const trimmed = String(message).slice(0, 2000);
|
const trimmed = String(message).slice(0, 2000);
|
||||||
|
|||||||
Reference in New Issue
Block a user