Skip to content

Commit 7f19fcb

Browse files
authored
feature to view all debug logs in application (#495)
1 parent f4947d7 commit 7f19fcb

File tree

8 files changed

+207
-1
lines changed

8 files changed

+207
-1
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:get/get.dart';
2+
3+
import '../controllers/logs_controller.dart';
4+
5+
class LogsBinding extends Bindings {
6+
@override
7+
void dependencies() {
8+
Get.lazyPut<LogsController>(
9+
() => LogsController(),
10+
);
11+
}
12+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:get/get.dart';
2+
import 'package:taskwarrior/app/utils/debug_logger/log_databse_helper.dart';
3+
4+
class LogsController extends GetxController {
5+
final LogDatabaseHelper _logDatabaseHelper = LogDatabaseHelper();
6+
var logs = <Map<String, dynamic>>[].obs;
7+
var isLoading = true.obs;
8+
9+
@override
10+
void onInit() {
11+
super.onInit();
12+
loadLogs();
13+
}
14+
15+
Future<void> loadLogs() async {
16+
isLoading.value = true;
17+
final fetchedLogs = await _logDatabaseHelper.getLogs();
18+
logs.assignAll(fetchedLogs); // Update the RxList
19+
isLoading.value = false;
20+
}
21+
22+
Future<void> clearLogs() async {
23+
await _logDatabaseHelper.clearLogs();
24+
loadLogs();
25+
}
26+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:get/get.dart';
3+
import 'package:taskwarrior/app/utils/debug_logger/log_databse_helper.dart';
4+
import '../controllers/logs_controller.dart';
5+
6+
class LogsView extends GetView<LogsController> {
7+
const LogsView({super.key});
8+
9+
@override
10+
Widget build(BuildContext context) {
11+
return Scaffold(
12+
appBar: AppBar(
13+
title: const Text('Debug Logs'),
14+
actions: [
15+
Obx(
16+
() => IconButton(
17+
icon: const Icon(Icons.refresh),
18+
onPressed:
19+
controller.isLoading.value ? null : controller.loadLogs,
20+
tooltip: 'Refresh Logs',
21+
),
22+
),
23+
Obx(
24+
() => IconButton(
25+
icon: const Icon(Icons.delete_forever),
26+
onPressed:
27+
controller.isLoading.value ? null : controller.clearLogs,
28+
tooltip: 'Clear All Logs',
29+
),
30+
),
31+
],
32+
),
33+
body: Obx(
34+
() {
35+
if (controller.isLoading.value) {
36+
return const Center(child: CircularProgressIndicator());
37+
} else if (controller.logs.isEmpty) {
38+
return const Center(
39+
child: Text(
40+
'No debug logs found.',
41+
style: TextStyle(fontSize: 16, color: Colors.grey),
42+
),
43+
);
44+
} else {
45+
return ListView.builder(
46+
padding: const EdgeInsets.all(8.0),
47+
itemCount: controller.logs.length,
48+
itemBuilder: (context, index) {
49+
final log = controller.logs[index];
50+
return Card(
51+
margin: const EdgeInsets.symmetric(vertical: 4.0),
52+
child: Padding(
53+
padding: const EdgeInsets.all(12.0),
54+
child: Column(
55+
crossAxisAlignment: CrossAxisAlignment.start,
56+
children: [
57+
Text(
58+
log[LogDatabaseHelper.columnMessage] ?? 'No message',
59+
style: const TextStyle(fontSize: 15),
60+
),
61+
const SizedBox(height: 4),
62+
Text(
63+
'Timestamp: ${log[LogDatabaseHelper.columnTimestamp] ?? 'N/A'}',
64+
style:
65+
const TextStyle(fontSize: 12, color: Colors.grey),
66+
),
67+
],
68+
),
69+
),
70+
);
71+
},
72+
);
73+
}
74+
},
75+
),
76+
);
77+
}
78+
}

lib/app/modules/settings/views/settings_page_body.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
55

66
import 'package:google_fonts/google_fonts.dart';
77
import 'package:taskwarrior/app/modules/settings/views/settings_page_taskchampion.dart';
8+
import 'package:taskwarrior/app/routes/app_pages.dart';
89
import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart';
910
import 'package:taskwarrior/app/utils/language/sentence_manager.dart';
1011
import 'package:taskwarrior/app/utils/themes/theme_extension.dart';
@@ -24,7 +25,8 @@ class SettingsPageBody extends StatelessWidget {
2425

2526
@override
2627
Widget build(BuildContext context) {
27-
TaskwarriorColorTheme tColors = Theme.of(context).extension<TaskwarriorColorTheme>()!;
28+
TaskwarriorColorTheme tColors =
29+
Theme.of(context).extension<TaskwarriorColorTheme>()!;
2830
return Obx(() {
2931
if (controller.isMovingDirectory.value) {
3032
return Center(
@@ -138,6 +140,16 @@ class SettingsPageBody extends StatelessWidget {
138140
controller: controller,
139141
),
140142
),
143+
const Divider(),
144+
SettingsPageListTile(
145+
title: "Logs ",
146+
subTitle: "check all debug logs here",
147+
trailing: IconButton(
148+
onPressed: () {
149+
Get.toNamed(Routes.LOGS);
150+
},
151+
icon: const Icon(Icons.login)),
152+
)
141153
],
142154
);
143155
}

lib/app/routes/app_pages.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import '../modules/detailRoute/bindings/detail_route_binding.dart';
66
import '../modules/detailRoute/views/detail_route_view.dart';
77
import '../modules/home/bindings/home_binding.dart';
88
import '../modules/home/views/home_view.dart';
9+
import '../modules/logs/bindings/logs_binding.dart';
10+
import '../modules/logs/views/logs_view.dart';
911
import '../modules/manageTaskServer/bindings/manage_task_server_binding.dart';
1012
import '../modules/manageTaskServer/views/manage_task_server_view.dart';
1113
import '../modules/manage_task_champion_creds/bindings/manage_task_champion_creds_binding.dart';
@@ -88,5 +90,10 @@ class AppPages {
8890
page: () => const ManageTaskChampionCredsView(),
8991
binding: ManageTaskChampionCredsBinding(),
9092
),
93+
GetPage(
94+
name: _Paths.LOGS,
95+
page: () => const LogsView(),
96+
binding: LogsBinding(),
97+
),
9198
];
9299
}

lib/app/routes/app_routes.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ abstract class Routes {
1616
static const SETTINGS = _Paths.SETTINGS;
1717
static const PERMISSION = _Paths.PERMISSION;
1818
static const MANAGE_TASK_CHAMPION_CREDS = _Paths.MANAGE_TASK_CHAMPION_CREDS;
19+
static const LOGS = _Paths.LOGS;
1920
}
2021

2122
abstract class _Paths {
@@ -31,4 +32,5 @@ abstract class _Paths {
3132
static const SETTINGS = '/settings';
3233
static const PERMISSION = '/permission';
3334
static const MANAGE_TASK_CHAMPION_CREDS = '/manage-task-champion-creds';
35+
static const LOGS = '/logs';
3436
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:sqflite/sqflite.dart';
2+
import 'package:path/path.dart';
3+
import 'package:path_provider/path_provider.dart';
4+
import 'dart:io';
5+
6+
class LogDatabaseHelper {
7+
static Database? _database;
8+
static const String tableName = 'logs';
9+
static const String columnId = 'id';
10+
static const String columnMessage = 'message';
11+
static const String columnTimestamp = 'timestamp';
12+
13+
Future<Database> get database async {
14+
if (_database != null) return _database!;
15+
_database = await initDB();
16+
return _database!;
17+
}
18+
19+
Future<Database> initDB() async {
20+
Directory documentsDirectory = await getApplicationDocumentsDirectory();
21+
String path = join(documentsDirectory.path, 'debug_logs.db');
22+
return await openDatabase(
23+
path,
24+
version: 1,
25+
onCreate: (db, version) async {
26+
await db.execute('''
27+
CREATE TABLE $tableName (
28+
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
29+
$columnMessage TEXT NOT NULL,
30+
$columnTimestamp TEXT NOT NULL
31+
)
32+
''');
33+
},
34+
);
35+
}
36+
37+
Future<void> insertLog(String message) async {
38+
final db = await database;
39+
await db.insert(
40+
tableName,
41+
{
42+
columnMessage: message,
43+
columnTimestamp: DateTime.now().toIso8601String(),
44+
},
45+
conflictAlgorithm: ConflictAlgorithm.replace,
46+
);
47+
}
48+
49+
Future<List<Map<String, dynamic>>> getLogs() async {
50+
final db = await database;
51+
return await db.query(tableName, orderBy: '$columnTimestamp DESC');
52+
}
53+
54+
Future<void> clearLogs() async {
55+
final db = await database;
56+
await db.delete(tableName);
57+
}
58+
}

lib/main.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
import 'package:flutter/foundation.dart';
12
import 'package:flutter/material.dart';
23
import 'package:get/get.dart';
34
import 'package:taskwarrior/app/utils/app_settings/app_settings.dart';
5+
import 'package:taskwarrior/app/utils/debug_logger/log_databse_helper.dart';
46
import 'package:taskwarrior/app/utils/themes/dark_theme.dart';
57
import 'package:taskwarrior/app/utils/themes/light_theme.dart';
68
import 'app/routes/app_pages.dart';
79

10+
LogDatabaseHelper _logDatabaseHelper = LogDatabaseHelper();
11+
812
void main() async {
13+
debugPrint = (String? message, {int? wrapWidth}) {
14+
if (message != null) {
15+
debugPrintSynchronously(message, wrapWidth: wrapWidth);
16+
_logDatabaseHelper.insertLog(message);
17+
}
18+
};
19+
920
WidgetsFlutterBinding.ensureInitialized();
1021
await AppSettings.init();
1122

0 commit comments

Comments
 (0)