Added raw-failure logs to flutter app (to debug init errors)
This commit is contained in:
parent
05eb37bc80
commit
2f73a21a41
@ -59,6 +59,7 @@ void main() async {
|
||||
Hive.deleteBoxFromDisk('scn-logs');
|
||||
await Hive.openBox<SCNLog>('scn-logs');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-logs: ' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to open Hive-Box: scn-logs', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
|
||||
print('[INIT] Load Hive<scn-requests>...');
|
||||
@ -69,6 +70,7 @@ void main() async {
|
||||
Hive.deleteBoxFromDisk('scn-requests');
|
||||
await Hive.openBox<SCNRequest>('scn-requests');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-requests: ' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to open Hive-Box: scn-requests', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
|
||||
print('[INIT] Load Hive<scn-message-cache>...');
|
||||
@ -79,6 +81,7 @@ void main() async {
|
||||
Hive.deleteBoxFromDisk('scn-message-cache');
|
||||
await Hive.openBox<SCNMessage>('scn-message-cache');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-message-cache' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to open Hive-Box: scn-message-cache', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
|
||||
print('[INIT] Load Hive<scn-channel-cache>...');
|
||||
@ -89,6 +92,7 @@ void main() async {
|
||||
Hive.deleteBoxFromDisk('scn-channel-cache');
|
||||
await Hive.openBox<Channel>('scn-channel-cache');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-channel-cache' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to open Hive-Box: scn-channel-cache', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
|
||||
print('[INIT] Load Hive<scn-fb-messages>...');
|
||||
@ -99,6 +103,7 @@ void main() async {
|
||||
Hive.deleteBoxFromDisk('scn-fb-messages');
|
||||
await Hive.openBox<FBMessage>('scn-fb-messages');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-fb-messages' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to open Hive-Box: scn-fb-messages', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
|
||||
print('[INIT] Load AppAuth...');
|
||||
@ -112,11 +117,13 @@ void main() async {
|
||||
await appAuth.loadUser();
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.error('Failed to load user (background load on startup): ' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to load user (background load on startup)', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
try {
|
||||
await appAuth.loadClient();
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.error('Failed to load user (background load on startup): ' + exc.toString(), trace: trace);
|
||||
ApplicationLog.writeRawFailure('Failed to load user (background load on startup)', {'error': exc.toString(), 'trace': trace});
|
||||
}
|
||||
}();
|
||||
}
|
||||
@ -321,6 +328,7 @@ Future<void> _receiveMessage(RemoteMessage message, bool foreground) async {
|
||||
await Hive.openBox<SCNMessage>('scn-message-cache');
|
||||
await Hive.openBox<SCNRequest>('scn-requests');
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.writeRawFailure('Failed to init hive', {'exception': exc.toString(), 'trace': trace.toString(), 'data': message, 'foreground': foreground});
|
||||
ApplicationLog.error('Failed to init hive:' + exc.toString(), trace: trace);
|
||||
Notifier.showLocalNotification("", "@ERROR", "@ERROR", 'Error Channel', "Error", "Failed to receive SCN message (init failed)", null);
|
||||
return;
|
||||
@ -341,6 +349,7 @@ Future<void> _receiveMessage(RemoteMessage message, bool foreground) async {
|
||||
|
||||
Notifier.showLocalNotification(scn_msg_id, channel_id, channel, 'Channel: ${channel}', title, body, timestamp);
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.writeRawFailure('Failed to decode received FB message', {'exception': exc.toString(), 'trace': trace.toString(), 'data': message, 'foreground': foreground});
|
||||
ApplicationLog.error('Failed to decode received FB message' + exc.toString(), trace: trace);
|
||||
Notifier.showLocalNotification("", "@ERROR", "@ERROR", 'Error Channel', "Error", "Failed to receive SCN message (decode failed)", null);
|
||||
return;
|
||||
@ -349,6 +358,7 @@ Future<void> _receiveMessage(RemoteMessage message, bool foreground) async {
|
||||
try {
|
||||
FBMessageLog.insert(message);
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.writeRawFailure('Failed to persist received FB message', {'exception': exc.toString(), 'trace': trace.toString(), 'data': message, 'foreground': foreground});
|
||||
ApplicationLog.error('Failed to persist received FB message' + exc.toString(), trace: trace);
|
||||
Notifier.showLocalNotification("", "@ERROR", "@ERROR", 'Error Channel', "Error", "Failed to receive SCN message (persist failed)", null);
|
||||
return;
|
||||
@ -359,6 +369,7 @@ Future<void> _receiveMessage(RemoteMessage message, bool foreground) async {
|
||||
SCNDataCache().addToMessageCache([msg]);
|
||||
if (foreground) AppEvents().notifyMessageReceivedListeners(msg);
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.writeRawFailure('Failed to query+persist message', {'exception': exc.toString(), 'trace': trace.toString(), 'data': message, 'foreground': foreground});
|
||||
ApplicationLog.error('Failed to query+persist message: ' + exc.toString(), trace: trace);
|
||||
return;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/utils/notifier.dart';
|
||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||
@ -59,6 +60,19 @@ class _DebugActionsPageState extends State<DebugActionsPage> {
|
||||
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null),
|
||||
text: 'Show local notification',
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
UI.button(
|
||||
big: false,
|
||||
onPressed: () => ApplicationLog.writeRawFailure('test', {
|
||||
'text': "hello world",
|
||||
'object': {
|
||||
1: 2,
|
||||
4: 5,
|
||||
6: [7, 8, 9]
|
||||
},
|
||||
'trace': StackTrace.current
|
||||
}),
|
||||
text: 'asdf'),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,15 +1,20 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:simplecloudnotifier/models/channel.dart';
|
||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
||||
import 'package:simplecloudnotifier/pages/debug/debug_persistence_failurelogs.dart';
|
||||
import 'package:simplecloudnotifier/pages/debug/debug_persistence_hive.dart';
|
||||
import 'package:simplecloudnotifier/pages/debug/debug_persistence_sharedprefs.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/state/fb_message.dart';
|
||||
import 'package:simplecloudnotifier/state/globals.dart';
|
||||
import 'package:simplecloudnotifier/state/interfaces.dart';
|
||||
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class DebugPersistencePage extends StatefulWidget {
|
||||
@override
|
||||
@ -39,6 +44,7 @@ class _DebugPersistencePageState extends State<DebugPersistencePage> {
|
||||
_buildHiveCard(context, () => Hive.box<SCNMessage>('scn-message-cache'), 'scn-message-cache'),
|
||||
_buildHiveCard(context, () => Hive.box<Channel>('scn-channel-cache'), 'scn-channel-cache'),
|
||||
_buildHiveCard(context, () => Hive.box<FBMessage>('scn-fb-messages'), 'scn-fb-messages'),
|
||||
_buildFailureLogCard(context, Globals().rawFailureLogsDir),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -85,4 +91,25 @@ class _DebugPersistencePageState extends State<DebugPersistencePage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFailureLogCard(BuildContext context, Directory dir) {
|
||||
return Card.outlined(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navi.push(context, () => DebugFailureLogsPage(dir: dir.path));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 30, child: Text('')),
|
||||
Expanded(child: Text('Failure [/${path.basename(dir.path)}/]', style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
|
||||
SizedBox(width: 40, child: Text("${dir.listSync().length}", textAlign: TextAlign.end)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
import 'package:simplecloudnotifier/types/immediate_future.dart';
|
||||
|
||||
class DebugFailureLogFilePage extends StatefulWidget {
|
||||
final String path;
|
||||
|
||||
DebugFailureLogFilePage({required this.path}) {}
|
||||
|
||||
@override
|
||||
State<DebugFailureLogFilePage> createState() => _DebugFailureLogFilePageState();
|
||||
}
|
||||
|
||||
class _DebugFailureLogFilePageState extends State<DebugFailureLogFilePage> {
|
||||
ImmediateFuture<String>? _futureContent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_futureContent = ImmediateFuture.ofFuture(new File(this.widget.path).readAsString());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SCNScaffold(
|
||||
title: 'FailureLog',
|
||||
showSearch: false,
|
||||
child: () {
|
||||
if (_futureContent == null) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
return FutureBuilder(
|
||||
future: _futureContent!.future,
|
||||
builder: ((context, snapshot) {
|
||||
if (_futureContent?.value != null) {
|
||||
return _buildContent(context, _futureContent!.value!);
|
||||
} else if (snapshot.connectionState == ConnectionState.done && snapshot.hasError) {
|
||||
return Text('Error: ${snapshot.error}'); //TODO better error display
|
||||
} else if (snapshot.connectionState == ConnectionState.done) {
|
||||
return _buildContent(context, snapshot.data!);
|
||||
} else {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
}),
|
||||
);
|
||||
}(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context, String value) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(value, style: TextStyle(fontFamily: "monospace")),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
86
flutter/lib/pages/debug/debug_persistence_failurelogs.dart
Normal file
86
flutter/lib/pages/debug/debug_persistence_failurelogs.dart
Normal file
@ -0,0 +1,86 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
import 'package:simplecloudnotifier/pages/debug/debug_persistence_failurelogfile.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/state/globals.dart';
|
||||
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||
|
||||
class DebugFailureLogsPage extends StatefulWidget {
|
||||
final String dir;
|
||||
|
||||
DebugFailureLogsPage({required this.dir});
|
||||
|
||||
@override
|
||||
State<DebugFailureLogsPage> createState() => _DebugFailureLogsPageState();
|
||||
}
|
||||
|
||||
class _DebugFailureLogsPageState extends State<DebugFailureLogsPage> {
|
||||
List<String> files = [];
|
||||
|
||||
_DebugFailureLogsPageState() {
|
||||
files = _listFilesInRawLogFolder();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SCNScaffold(
|
||||
title: 'F-Logs',
|
||||
showSearch: false,
|
||||
child: ListView.separated(
|
||||
itemCount: files.length,
|
||||
itemBuilder: (context, listIndex) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navi.push(context, () => DebugFailureLogFilePage(path: files[listIndex]));
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: Text(path.basename(files[listIndex]), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12))),
|
||||
IconButton(
|
||||
icon: const Icon(FontAwesomeIcons.trash),
|
||||
tooltip: 'Delete',
|
||||
iconSize: 16,
|
||||
color: Colors.red,
|
||||
onPressed: () => _deleteFile(context, files[listIndex]),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Divider(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _listFilesInRawLogFolder() {
|
||||
final fse = Globals().rawFailureLogsDir.listSync();
|
||||
|
||||
ApplicationLog.debug("Found ${fse.length} files in raw log folder '${Globals().rawFailureLogsDir.path}'");
|
||||
|
||||
var paths = fse.where((element) => element is File).map((e) => e.path).toList();
|
||||
|
||||
paths.sort((a, b) => -1 * a.compareTo(b));
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
void _deleteFile(BuildContext context, String fil) {
|
||||
final file = File(fil);
|
||||
|
||||
file.deleteSync();
|
||||
|
||||
setState(() {
|
||||
files = _listFilesInRawLogFolder();
|
||||
});
|
||||
|
||||
Toaster.info("Okay", "File deleted");
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:simplecloudnotifier/state/globals.dart';
|
||||
import 'package:simplecloudnotifier/state/interfaces.dart';
|
||||
import 'package:xid/xid.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
part 'application_log.g.dart';
|
||||
|
||||
@ -76,6 +81,61 @@ class ApplicationLog {
|
||||
trace: trace?.toString() ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
static void writeRawFailure(String message, Map<String, dynamic> extraData) async {
|
||||
try {
|
||||
await Globals().init();
|
||||
|
||||
final fn = path.join(Globals().rawFailureLogsDir.path, 'failure-${DateTime.now().toIso8601String()}.log');
|
||||
|
||||
var txt = "[TEXT]\n${message}\n\n";
|
||||
for (var k in extraData.keys) {
|
||||
txt += "[${k}]\n${_debugToStr(extraData[k])}\n\n";
|
||||
}
|
||||
|
||||
await File(fn).writeAsString(txt);
|
||||
|
||||
ApplicationLog.debug("Wrote raw failure log to '${fn}' ('${message}')");
|
||||
} catch (e) {
|
||||
print("Failed to <writeRawFailure>: ${e}");
|
||||
}
|
||||
}
|
||||
|
||||
static String _debugToStr(dynamic v) {
|
||||
if (v is String) {
|
||||
return v;
|
||||
}
|
||||
if (v is StackTrace) {
|
||||
return v.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
final enc = new JsonEncoder.withIndent(" ");
|
||||
return enc.convert(v);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
return jsonEncode(v);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
return v.toString();
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
return "${v}";
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return "<[!]FAILED_TO_PRINT_OBJECT>";
|
||||
}
|
||||
}
|
||||
|
||||
@HiveType(typeId: 103)
|
||||
|
@ -2,7 +2,9 @@ import 'dart:io';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class Globals {
|
||||
static final Globals _singleton = Globals._internal();
|
||||
@ -26,6 +28,9 @@ class Globals {
|
||||
|
||||
late SharedPreferences sharedPrefs;
|
||||
|
||||
late Directory appDocumentsDir;
|
||||
late Directory rawFailureLogsDir;
|
||||
|
||||
bool get isInitialized => _initialized;
|
||||
|
||||
Future<void> init() async {
|
||||
@ -61,6 +66,11 @@ class Globals {
|
||||
|
||||
this.sharedPrefs = await SharedPreferences.getInstance();
|
||||
|
||||
this.appDocumentsDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
this.rawFailureLogsDir = Directory(path.join(Globals().appDocumentsDir.path, "rawlogs"));
|
||||
await this.rawFailureLogsDir.create(recursive: true);
|
||||
|
||||
this._initialized = true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user