feat(app): reportAppDebug — Live-Debug-Logs an Bridge ohne ADB

Stefan-Anforderung: Background-Wake-Word-Pipeline klappt noch nicht,
ADB nicht zur Hand → Debug via RVS-Log-Pipeline.

Logger:
- reportAppDebug(scope, message) analog zu reportAppError aber
  level=info, kein console.error, fuer Live-Diagnose

Strategische Log-Punkte:
- wakeword.ts: start() emits 'wake.start armed'
- wakeword.ts: onWakeDetected emits 'wake.detect state=X' beim
  Native-Trigger-Empfang
- ChatScreen.tsx wake-callback: 'wake.cb callback fired',
  'wake.cb startRecording=X', 'wake.cb gong played'
- backgroundAudio.ts: 'bg.start slot=X', 'bg.stop service stopped',
  'bg.start.fail msg' wenn Service nicht hochkommt

Abruf live via curl http://172.0.2.33:3001/api/app-log?lines=100

Damit kann Stefan nach APK-Build (mit allen Native-Fixes + Logger)
im Background-Test exakt sehen wo es klemmt:
- Kommt 'wake.detect' im Hintergrund an? (WakeLock-Frage)
- Kommt 'wake.cb callback fired'? (JS-Bridge-Frage)
- Geht 'bg.start slot=wake' durch? (Service-Start-Frage)

APK neu bauen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 20:27:29 +02:00
parent a9115699db
commit d78b668e31
4 changed files with 26 additions and 0 deletions
+3
View File
@@ -1270,9 +1270,11 @@ const ChatScreen: React.FC = () => {
useEffect(() => {
const unsubWake = wakeWordService.onWakeWord(async () => {
console.log('[Chat] Gespraechsmodus — starte Auto-Aufnahme');
import('../services/logger').then(m => m.reportAppDebug('wake.cb', 'callback fired, calling startRecording')).catch(()=>{});
// Conversation-Window: User hat X Sekunden um anzufangen, sonst Konversation aus
const windowMs = await loadConvWindowMs();
const started = await audioService.startRecording(true, windowMs);
import('../services/logger').then(m => m.reportAppDebug('wake.cb', `startRecording returned ${started}`)).catch(()=>{});
if (started) {
// Erst JETZT signalisieren dass das Mikro wirklich offen ist —
// vorher war's noch in der Init-Phase. So weiss der User exakt
@@ -1280,6 +1282,7 @@ const ChatScreen: React.FC = () => {
// ueber Settings → Wake-Word abschaltbar.
ToastAndroid.show('🎤 Mikro offen — sprich jetzt', ToastAndroid.SHORT);
playWakeReadySound().catch(() => {});
import('../services/logger').then(m => m.reportAppDebug('wake.cb', 'gong played + recording started')).catch(()=>{});
} else {
// Mikrofon nicht verfuegbar, naechsten Versuch
wakeWordService.resume();
+3
View File
@@ -48,6 +48,7 @@ async function applyState(): Promise<void> {
if (slots.size === 0) {
try { await BackgroundAudio.stop(); } catch {}
console.log('[BackgroundAudio] Service gestoppt (keine Slots)');
import('./logger').then(m => m.reportAppDebug('bg.stop', 'service stopped')).catch(()=>{});
return;
}
const reason = topReason();
@@ -55,8 +56,10 @@ async function applyState(): Promise<void> {
await BackgroundAudio.start(reason);
console.log('[BackgroundAudio] Service aktiv (slot=%s, slots=%s)',
reason, [...slots].join('+'));
import('./logger').then(m => m.reportAppDebug('bg.start', `slot=${reason} all=[${[...slots].join(',')}]`)).catch(()=>{});
} catch (err: any) {
console.warn('[BackgroundAudio] start fehlgeschlagen:', err?.message || err);
import('./logger').then(m => m.reportAppDebug('bg.start.fail', err?.message || String(err))).catch(()=>{});
}
}
+16
View File
@@ -78,6 +78,22 @@ export function reportAppError(ev: AppErrorEvent): void {
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. */
export function reportAppDebug(scope: string, message: string): void {
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 {
+4
View File
@@ -179,6 +179,8 @@ class WakeWordService {
try {
await OpenWakeWord.start();
console.log('[WakeWord] armed — warte auf "%s"', this.keyword);
// Debug-Log via RVS damit wir auch ohne ADB sehen wann es greift
import('./logger').then(m => m.reportAppDebug('wake.start', `armed, keyword=${this.keyword}`)).catch(()=>{});
ToastAndroid.show(`Lausche auf "${KEYWORD_LABELS[this.keyword]}"`, ToastAndroid.SHORT);
this.setState('armed');
return true;
@@ -236,6 +238,8 @@ class WakeWordService {
}
console.log('[WakeWord] Wake-Word "%s" erkannt! (state=%s, barge=%s)',
this.keyword, this.state, this.bargeListening);
import('./logger').then(m => m.reportAppDebug('wake.detect',
`keyword=${this.keyword} state=${this.state} barge=${this.bargeListening}`)).catch(()=>{});
this.lastTriggerAt = now;
if (this.nativeReady && OpenWakeWord) {
try { await OpenWakeWord.stop(); } catch {}