feat: persistentes Auth-Log für Logout-Diagnose (M1)
Neuer AuthLog schreibt Login-Status-Wechsel und SDK-Warnungen/-Fehler
in eine Datei im App-Support-Verzeichnis, die einen Neustart übersteht.
Zusätzlich werden alle drei bewusst ausgelösten Logout-Buttons geloggt,
damit sich künftig unterscheiden lässt: Nutzer-Logout vs. vom SDK
automatisch ausgelöster Logout (Soft-Logout/M_UNKNOWN_TOKEN).
Reiner Logging-Zusatz, keine Verhaltensänderung – erster Schritt laut
ROADMAP M1 ("Erst Logging an allen Logout-Pfaden einbauen").
This commit is contained in:
+10
@@ -1123,3 +1123,13 @@
|
|||||||
2026-07-03 09:44 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
2026-07-03 09:44 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
||||||
2026-07-03 09:44 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
2026-07-03 09:44 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
||||||
2026-07-03 09:45 [Write] /home/steggi/pyramid/README.md
|
2026-07-03 09:45 [Write] /home/steggi/pyramid/README.md
|
||||||
|
2026-07-03 09:46 [Edit] /home/steggi/pyramid/ROADMAP.md
|
||||||
|
2026-07-03 09:46 [Edit] /home/steggi/pyramid/PROGRESS.md
|
||||||
|
2026-07-03 09:49 [Write] /home/steggi/pyramid/lib/core/auth_log.dart
|
||||||
|
2026-07-03 09:49 [Edit] /home/steggi/pyramid/lib/core/matrix_client.dart
|
||||||
|
2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/core/matrix_client.dart
|
||||||
|
2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/core/matrix_client.dart
|
||||||
|
2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
||||||
|
2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
||||||
|
2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
||||||
|
2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
/// Schreibt Login-/Logout-relevante Ereignisse in eine Datei, die einen
|
||||||
|
/// App-Neustart übersteht. Zweck: den Uta-Random-Logout-Bug nachvollziehen
|
||||||
|
/// können, auch wenn die App zwischen dem Logout und dem nächsten Blick ins
|
||||||
|
/// Log neu gestartet wurde (siehe ROADMAP M1).
|
||||||
|
class AuthLog {
|
||||||
|
static const _maxLines = 500;
|
||||||
|
static File? _file;
|
||||||
|
|
||||||
|
static Future<File> _ensureFile() async {
|
||||||
|
final existing = _file;
|
||||||
|
if (existing != null) return existing;
|
||||||
|
final dir = await getApplicationSupportDirectory();
|
||||||
|
final file = File(p.join(dir.path, 'auth_log.txt'));
|
||||||
|
return _file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> write(String message) async {
|
||||||
|
try {
|
||||||
|
final file = await _ensureFile();
|
||||||
|
await file.writeAsString(
|
||||||
|
'${DateTime.now().toIso8601String()} $message\n',
|
||||||
|
mode: FileMode.append,
|
||||||
|
flush: true,
|
||||||
|
);
|
||||||
|
final lines = await file.readAsLines();
|
||||||
|
if (lines.length > _maxLines) {
|
||||||
|
await file.writeAsString(
|
||||||
|
'${lines.sublist(lines.length - _maxLines).join('\n')}\n',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Logging darf selbst nie einen Fehlerpfad (oder gar einen Logout) auslösen.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> read() async {
|
||||||
|
try {
|
||||||
|
final file = await _ensureFile();
|
||||||
|
if (!await file.exists()) return '(noch keine Einträge)';
|
||||||
|
return await file.readAsString();
|
||||||
|
} catch (e) {
|
||||||
|
return 'Log konnte nicht gelesen werden: $e';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -10,6 +11,7 @@ import 'package:path/path.dart' as p;
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sqflite/sqflite.dart' as sqflite_native;
|
import 'package:sqflite/sqflite.dart' as sqflite_native;
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
|
import 'package:pyramid/core/auth_log.dart';
|
||||||
import 'package:pyramid/core/voip_manager.dart';
|
import 'package:pyramid/core/voip_manager.dart';
|
||||||
|
|
||||||
final matrixClientProvider = FutureProvider<Client>((ref) async {
|
final matrixClientProvider = FutureProvider<Client>((ref) async {
|
||||||
@@ -63,6 +65,18 @@ final matrixClientProvider = FutureProvider<Client>((ref) async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Persistiert Warnungen/Fehler des SDK sowie jeden Login-Status-Wechsel,
|
||||||
|
// damit ein unerwarteter Logout (z. B. Soft-Logout vom Server) auch nach
|
||||||
|
// einem App-Neustart noch nachvollziehbar ist (ROADMAP M1, Uta-Logout-Bug).
|
||||||
|
Logs().onLog = (event) {
|
||||||
|
if (event.level.index <= Level.warning.index) {
|
||||||
|
unawaited(AuthLog.write('[SDK ${event.level.name}] ${event.toFormattedString()}'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
client.onLoginStateChanged.stream.listen((state) {
|
||||||
|
unawaited(AuthLog.write('LoginState -> ${state.name}'));
|
||||||
|
});
|
||||||
|
|
||||||
await client.init(
|
await client.init(
|
||||||
waitForFirstSync: false,
|
waitForFirstSync: false,
|
||||||
waitUntilLoadCompletedLoaded: false,
|
waitUntilLoadCompletedLoaded: false,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'package:pretty_qr_code/pretty_qr_code.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:pointycastle/export.dart' as pc;
|
import 'package:pointycastle/export.dart' as pc;
|
||||||
import 'package:pyramid/core/app_state.dart';
|
import 'package:pyramid/core/app_state.dart';
|
||||||
|
import 'package:pyramid/core/auth_log.dart';
|
||||||
import 'package:pyramid/core/e2ee_diagnostics.dart';
|
import 'package:pyramid/core/e2ee_diagnostics.dart';
|
||||||
import 'package:pyramid/core/livekit_call_manager.dart';
|
import 'package:pyramid/core/livekit_call_manager.dart';
|
||||||
import 'package:pyramid/core/matrix_client.dart';
|
import 'package:pyramid/core/matrix_client.dart';
|
||||||
@@ -596,6 +597,7 @@ class _LogoutNavButtonState extends ConsumerState<_LogoutNavButton> {
|
|||||||
try {
|
try {
|
||||||
widget.onClose();
|
widget.onClose();
|
||||||
final client = await ref.read(matrixClientProvider.future);
|
final client = await ref.read(matrixClientProvider.future);
|
||||||
|
unawaited(AuthLog.write('User-initiated logout (Settings-Sidebar)'));
|
||||||
await client.logout();
|
await client.logout();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
@@ -4839,6 +4841,7 @@ class _DeleteAccountDialogState extends ConsumerState<_DeleteAccountDialog> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Konto wurde gelöscht — ausloggen
|
// Konto wurde gelöscht — ausloggen
|
||||||
|
unawaited(AuthLog.write('User-initiated logout (Konto gelöscht)'));
|
||||||
try { await client.logout(); } catch (_) {}
|
try { await client.logout(); } catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5137,6 +5140,7 @@ class _AboutSection extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
if (confirm == true) {
|
if (confirm == true) {
|
||||||
final client = await ref.read(matrixClientProvider.future);
|
final client = await ref.read(matrixClientProvider.future);
|
||||||
|
unawaited(AuthLog.write('User-initiated logout (Settings)'));
|
||||||
await client.logout().catchError((_) {});
|
await client.logout().catchError((_) {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user