fix(diag): record bridge and audio interruption diagnostics (#18)

* fix(diag): record bridge events in diagnostics page

Connection lost/reconnecting/disconnected and iOS audio interruption
events now appear in the diagnostics dialog alongside existing error
snackbar entries.

* fix(diag): reduce audio callback sample verbosity to debug

The render callback diagnostic sample logged every 100 callbacks
(~2s) at INFO level, flooding the 256-entry release log buffer and
pushing out useful events. Changed to DEBUG so it only appears in
debug builds with the larger 4096-entry buffer.

* fix(diag): add timestamps to Rust diagnostic log entries
This commit is contained in:
Edison Jwa
2026-06-03 18:09:25 +09:00
committed by GitHub
parent d886e285d1
commit 2c3c3873dc
3 changed files with 12 additions and 2 deletions
+5
View File
@@ -624,6 +624,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
}); });
unawaited(_refreshSnapshot(recordActivity: false, reportErrors: true)); unawaited(_refreshSnapshot(recordActivity: false, reportErrors: true));
case rust.BridgeEvent_Lost(:final reason): case rust.BridgeEvent_Lost(:final reason):
_recordUiDiagnostic('connection', 'lost: $reason');
setState(() { setState(() {
_phase = ConnectionPhase.reconnecting; _phase = ConnectionPhase.reconnecting;
_lostReason = reason; _lostReason = reason;
@@ -631,12 +632,14 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
_reconnectDelay = null; _reconnectDelay = null;
}); });
case rust.BridgeEvent_Reconnecting(:final attempt, :final delaySecs): case rust.BridgeEvent_Reconnecting(:final attempt, :final delaySecs):
_recordUiDiagnostic('connection', 'reconnecting attempt=$attempt delay=${delaySecs}s');
setState(() { setState(() {
_phase = ConnectionPhase.reconnecting; _phase = ConnectionPhase.reconnecting;
_reconnectAttempt = attempt; _reconnectAttempt = attempt;
_reconnectDelay = delaySecs; _reconnectDelay = delaySecs;
}); });
case rust.BridgeEvent_Disconnected(): case rust.BridgeEvent_Disconnected():
_recordUiDiagnostic('connection', 'disconnected');
setState(() { setState(() {
_resetConnectionUiState(phase: ConnectionPhase.disconnected); _resetConnectionUiState(phase: ConnectionPhase.disconnected);
}); });
@@ -708,6 +711,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
if (!mounted) return; if (!mounted) return;
final messenger = ScaffoldMessenger.of(context); final messenger = ScaffoldMessenger.of(context);
if (began) { if (began) {
_recordUiDiagnostic('audio', 'interruption began');
messenger.showSnackBar( messenger.showSnackBar(
SnackBar( SnackBar(
content: Text(AppL10n.of(context).iosAudioInterrupted), content: Text(AppL10n.of(context).iosAudioInterrupted),
@@ -716,6 +720,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
), ),
); );
} else if (shouldResume) { } else if (shouldResume) {
_recordUiDiagnostic('audio', 'interruption ended, resuming');
messenger.showSnackBar( messenger.showSnackBar(
SnackBar( SnackBar(
content: Text(AppL10n.of(context).iosAudioResuming), content: Text(AppL10n.of(context).iosAudioResuming),
+1 -1
View File
@@ -877,7 +877,7 @@ impl IosVoiceUnit {
last_num_frames = num_frames; last_num_frames = num_frames;
cb_count = cb_count.wrapping_add(1); cb_count = cb_count.wrapping_add(1);
if cb_count.is_multiple_of(100) { if cb_count.is_multiple_of(100) {
info!( debug!(
target: "chanora_audio", target: "chanora_audio",
cb = cb_count, cb = cb_count,
num_frames, num_frames,
+6 -1
View File
@@ -471,9 +471,14 @@ where
let mut visitor = FormatVisitor::default(); let mut visitor = FormatVisitor::default();
event.record(&mut visitor); event.record(&mut visitor);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default();
let secs = now.as_secs();
let line = format!( let line = format!(
"[{lvl}] {target}: {msg}{fields}", "[{lvl}] {ts} {target}: {msg}{fields}",
lvl = event.metadata().level(), lvl = event.metadata().level(),
ts = secs,
target = event.metadata().target(), target = event.metadata().target(),
msg = visitor.message, msg = visitor.message,
fields = visitor.rest fields = visitor.rest