Add a very simple Debug screen

This will show all the debug messages, which include when a file is
being ignored and not loaded, and hopefully all the exceptions. So the
user should be able to better understand why a file has been ignored.

It's not an ideal solution, but it's a quick fix.

Fixes #122
This commit is contained in:
Vishesh Handa
2020-05-06 01:06:12 +02:00
parent 63e7d0e927
commit ea733aacb5
5 changed files with 137 additions and 14 deletions

View File

@ -18,3 +18,5 @@ settings:
analytics: Analytics
crashReports: Collect Anonymous Crash Reports
usageStats: Collect Anonymous Usage Statistics
debug: Debug App
debugLog: Look under the hood

View File

@ -32,7 +32,7 @@ import 'setup/screens.dart';
class JournalApp extends StatelessWidget {
static Future main(SharedPreferences pref) async {
Log.init();
await Log.init();
var appState = AppState(pref);
appState.dumpToLog();

View File

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gitjournal/utils/logger.dart';
class DebugScreen extends StatefulWidget {
@override
_DebugScreenState createState() => _DebugScreenState();
}
class _DebugScreenState extends State<DebugScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(tr('settings.debug')),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
),
body: ListView(
children: <Widget>[
for (var msg in Log.fetchLogs()) _buildLogWidget(msg),
],
),
);
}
Widget _buildLogWidget(LogMessage msg) {
var textStyle = Theme.of(context).textTheme.subhead;
textStyle = textStyle.copyWith(color: _colorForLevel(msg.l));
var str = DateTime.fromMillisecondsSinceEpoch(msg.t).toIso8601String() +
' ' +
msg.msg;
if (msg.ex != null) {
str += ' ' + msg.ex;
}
if (msg.stack != null) {
str += ' ' + msg.stack;
}
return Text(str, style: textStyle);
}
Color _colorForLevel(String l) {
switch (l) {
case 'e':
return Colors.red;
}
return Colors.black;
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gitjournal/core/notes_folder_fs.dart';
import 'package:gitjournal/screens/debug_screen.dart';
import 'package:gitjournal/screens/settings_editors.dart';
import 'package:gitjournal/settings.dart';
import 'package:gitjournal/state_container.dart';
@ -226,6 +227,16 @@ class SettingsListState extends State<SettingsList> {
},
),
VersionNumberTile(),
ListTile(
title: Text(tr('settings.debug')),
subtitle: Text(tr('settings.debugLog')),
onTap: () {
var route = MaterialPageRoute(
builder: (context) => DebugScreen(),
);
Navigator.of(context).push(route);
},
),
]);
}
}

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:fimber/fimber.dart';
import 'package:meta/meta.dart';
import 'package:flutter/foundation.dart' as foundation;
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
@ -10,7 +11,7 @@ class Log {
static String logFolderPath;
static RandomAccessFile logFile;
static void init() async {
static Future<void> init() async {
if (foundation.kDebugMode) {
Fimber.plantTree(DebugTree.elapsed(useColors: true));
} else {
@ -25,7 +26,7 @@ class Log {
// Ignore if it already exists
}
setLogCapture(true);
await setLogCapture(true);
}
static void v(String msg, {dynamic ex, StackTrace stacktrace}) {
@ -59,20 +60,25 @@ class Log {
dynamic ex,
StackTrace stackTrace,
) {
if (logFile == null) return;
var map = <String, dynamic>{
't': DateTime.now().millisecondsSinceEpoch,
'l': level,
'msg': msg.replaceAll('\n', ' '),
if (ex != null) 'ex': ex.toString().replaceAll('\n', ' '),
if (stackTrace != null)
'stack': stackTrace.toString().replaceAll('\n', ' '),
};
var str = json.encode(map);
if (logFile == null) {
return;
}
var logMsg = LogMessage(
t: DateTime.now().millisecondsSinceEpoch,
l: level,
msg: msg.replaceAll('\n', ' '),
ex: ex != null ? ex.toString().replaceAll('\n', ' ') : null,
stack: stackTrace != null
? stackTrace.toString().replaceAll('\n', ' ')
: null,
);
var str = json.encode(logMsg.toMap());
logFile.writeStringSync(str + '\n');
}
static void setLogCapture(bool state) async {
static Future<void> setLogCapture(bool state) async {
if (state) {
var today = DateTime.now().toString().substring(0, 10);
var logFilePath = p.join(logFolderPath, '$today.jsonl');
@ -85,4 +91,53 @@ class Log {
logFile = null;
}
}
static Iterable<LogMessage> fetchLogs() sync* {
var today = DateTime.now().toString().substring(0, 10);
for (var msg in fetchLogsForDate(today)) {
yield msg;
}
}
static Iterable<LogMessage> fetchLogsForDate(String date) sync* {
var file = File(p.join(logFolderPath, '$date.jsonl'));
var str = file.readAsStringSync();
for (var line in LineSplitter.split(str)) {
yield LogMessage.fromMap(json.decode(line));
}
}
}
class LogMessage {
int t;
String l;
String msg;
String ex;
String stack;
LogMessage({
@required this.t,
@required this.l,
@required this.msg,
this.ex,
this.stack,
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
't': t,
'l': l,
'msg': msg,
if (ex != null && ex.isNotEmpty) 'ex': ex,
if (stack != null && stack.isNotEmpty) 'stack': stack,
};
}
LogMessage.fromMap(Map<String, dynamic> map) {
t = map['t'];
l = map['l'];
msg = map['msg'];
ex = map['ex'];
stack = map['stack'];
}
}