mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-28 01:45:55 +08:00
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:
@ -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
|
||||
|
@ -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();
|
||||
|
55
lib/screens/debug_screen.dart
Normal file
55
lib/screens/debug_screen.dart
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
},
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user