added logs
This commit is contained in:
parent
51e89ce901
commit
dae5182f90
@ -6,6 +6,7 @@ import 'package:http/http.dart' as http;
|
||||
import 'package:simplecloudnotifier/models/api_error.dart';
|
||||
import 'package:simplecloudnotifier/models/key_token_auth.dart';
|
||||
import 'package:simplecloudnotifier/models/user.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/state/globals.dart';
|
||||
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||
import 'package:simplecloudnotifier/models/channel.dart';
|
||||
@ -67,7 +68,8 @@ class APIClient {
|
||||
responseHeaders = response.headers;
|
||||
} catch (exc, trace) {
|
||||
RequestLog.addRequestException(name, t0, method, uri, req.body, req.headers, exc, trace);
|
||||
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||
showPlatformToast(child: Text('Request "${name}" failed'), context: ToastProvider.context);
|
||||
ApplicationLog.error('Request "${name}" failed: ' + exc.toString(), trace: trace);
|
||||
rethrow;
|
||||
}
|
||||
|
||||
@ -78,7 +80,9 @@ class APIClient {
|
||||
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
|
||||
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||
throw Exception(apierr.message);
|
||||
} catch (_) {}
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.warn('Failed to decode api response as error-object', additional: exc.toString() + "\nBody:\n" + responseBody, trace: trace);
|
||||
}
|
||||
|
||||
RequestLog.addRequestErrorStatuscode(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders);
|
||||
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||
@ -99,6 +103,7 @@ class APIClient {
|
||||
} catch (exc, trace) {
|
||||
RequestLog.addRequestDecodeError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, exc, trace);
|
||||
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||
ApplicationLog.error('Failed to decode response: ' + exc.toString(), additional: "\nBody:\n" + responseBody, trace: trace);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
@ -17,29 +17,31 @@ void main() async {
|
||||
|
||||
Hive.registerAdapter(SCNRequestAdapter());
|
||||
Hive.registerAdapter(SCNLogAdapter());
|
||||
Hive.registerAdapter(SCNLogLevelAdapter());
|
||||
|
||||
try {
|
||||
await Hive.openBox<SCNRequest>('scn-requests');
|
||||
await Hive.openBox<SCNLog>('scn-logs');
|
||||
} catch (e) {
|
||||
print(e);
|
||||
} catch (exc, trace) {
|
||||
Hive.deleteBoxFromDisk('scn-requests');
|
||||
Hive.deleteBoxFromDisk('scn-logs');
|
||||
await Hive.openBox<SCNRequest>('scn-requests');
|
||||
await Hive.openBox<SCNLog>('scn-logs');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-requests: ' + exc.toString(), trace: trace);
|
||||
}
|
||||
|
||||
try {
|
||||
await Hive.openBox<SCNLog>('scn-logs');
|
||||
} catch (exc, trace) {
|
||||
Hive.deleteBoxFromDisk('scn-logs');
|
||||
await Hive.openBox<SCNLog>('scn-logs');
|
||||
ApplicationLog.error('Failed to open Hive-Box: scn-logs: ' + exc.toString(), trace: trace);
|
||||
}
|
||||
|
||||
ApplicationLog.debug('Application started');
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => UserAccount(),
|
||||
lazy: false,
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => AppTheme(),
|
||||
lazy: false,
|
||||
),
|
||||
ChangeNotifierProvider(create: (context) => UserAccount(), lazy: false),
|
||||
ChangeNotifierProvider(create: (context) => AppTheme(), lazy: false),
|
||||
],
|
||||
child: const SCNApp(),
|
||||
),
|
||||
|
@ -35,13 +35,13 @@ class Message {
|
||||
senderUserID: json['sender_user_id'] as String,
|
||||
channelInternalName: json['channel_internal_name'] as String,
|
||||
channelID: json['channel_id'] as String,
|
||||
senderName: json['sender_name'] as String,
|
||||
senderName: json['sender_name'] as String?,
|
||||
senderIP: json['sender_ip'] as String,
|
||||
timestamp: json['timestamp'] as String,
|
||||
title: json['title'] as String,
|
||||
content: json['content'] as String,
|
||||
content: json['content'] as String?,
|
||||
priority: json['priority'] as int,
|
||||
userMessageID: json['usr_message_id'] as String,
|
||||
userMessageID: json['usr_message_id'] as String?,
|
||||
usedKeyID: json['used_key_id'] as String,
|
||||
trimmed: json['trimmed'] as bool,
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/app_bar.dart';
|
||||
import 'package:simplecloudnotifier/pages/channel_list/root.dart';
|
||||
import 'package:simplecloudnotifier/pages/channel_list/channel_list.dart';
|
||||
import 'package:simplecloudnotifier/pages/send/root.dart';
|
||||
import 'package:simplecloudnotifier/components/bottom_fab/fab_bottom_app_bar.dart';
|
||||
import 'package:simplecloudnotifier/pages/account/root.dart';
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/models/key_token_auth.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/state/user_account.dart';
|
||||
|
||||
class AccountLoginPage extends StatefulWidget {
|
||||
@ -75,7 +76,7 @@ class _AccountLoginPageState extends State<AccountLoginPage> {
|
||||
if (verified) {
|
||||
msgr.showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Data ok'),
|
||||
content: Text('Data ok'), //TODO use toast?
|
||||
),
|
||||
);
|
||||
prov.setToken(KeyTokenAuth(userId: uid, token: tok));
|
||||
@ -84,12 +85,12 @@ class _AccountLoginPageState extends State<AccountLoginPage> {
|
||||
} else {
|
||||
msgr.showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Failed to verify token'),
|
||||
content: Text('Failed to verify token'), //TODO use toast?
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
//TODO
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/models/channel.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/state/user_account.dart';
|
||||
import 'package:simplecloudnotifier/pages/channel_list/channel_list_item.dart';
|
||||
|
||||
@ -44,8 +45,9 @@ class _ChannelRootPageState extends State<ChannelRootPage> {
|
||||
final items = await APIClient.getChannelList(acc.auth!, ChannelSelector.all);
|
||||
|
||||
_pagingController.appendLastPage(items);
|
||||
} catch (error) {
|
||||
_pagingController.error = error;
|
||||
} catch (exc, trace) {
|
||||
_pagingController.error = exc.toString();
|
||||
ApplicationLog.error('Failed to list channels: ' + exc.toString(), trace: trace);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
|
||||
class DebugLogsPage extends StatefulWidget {
|
||||
@override
|
||||
@ -6,8 +9,97 @@ class DebugLogsPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _DebugLogsPageState extends State<DebugLogsPage> {
|
||||
Box<SCNLog> logBox = Hive.box<SCNLog>('scn-logs');
|
||||
|
||||
static final _dateFormat = DateFormat('yyyy-MM-dd kk:mm');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(/* Add your UI components here */);
|
||||
return Container(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: logBox.listenable(),
|
||||
builder: (context, Box<SCNLog> box, _) {
|
||||
return ListView.builder(
|
||||
itemCount: logBox.length,
|
||||
itemBuilder: (context, listIndex) {
|
||||
final log = logBox.getAt(logBox.length - listIndex - 1)!;
|
||||
switch (log.level) {
|
||||
case SCNLogLevel.debug:
|
||||
return buildItem(context, log, Theme.of(context).hintColor);
|
||||
case SCNLogLevel.info:
|
||||
return buildItem(context, log, Colors.blueAccent);
|
||||
case SCNLogLevel.warning:
|
||||
return buildItem(context, log, Colors.orangeAccent);
|
||||
case SCNLogLevel.error:
|
||||
return buildItem(context, log, Colors.redAccent);
|
||||
case SCNLogLevel.fatal:
|
||||
return buildItem(context, log, Colors.black);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildItem(BuildContext context, SCNLog log, Color tagColor) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
|
||||
child: Card.filled(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.fromLTRB(12, 1, 12, 1),
|
||||
decoration: BoxDecoration(
|
||||
color: tagColor,
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(8)),
|
||||
),
|
||||
child: Text(
|
||||
log.level.name.toUpperCase(),
|
||||
style: TextStyle(fontWeight: FontWeight.bold, color: Theme.of(context).cardColor, fontSize: 14),
|
||||
),
|
||||
),
|
||||
Expanded(child: SizedBox()),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
|
||||
child: Text(_dateFormat.format(log.timestamp), style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
if (log.message.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Text(log.message, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
),
|
||||
if (log.additional.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: SelectableText(
|
||||
log.additional,
|
||||
style: TextStyle(fontSize: 12),
|
||||
minLines: 1,
|
||||
maxLines: 10,
|
||||
),
|
||||
),
|
||||
if (log.trace.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: SelectableText(
|
||||
log.trace,
|
||||
style: TextStyle(fontSize: 12),
|
||||
minLines: 1,
|
||||
maxLines: 10,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:shared_preferences/shared_preferences.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/request_log.dart';
|
||||
|
||||
class DebugPersistencePage extends StatefulWidget {
|
||||
@override
|
||||
@ -6,8 +12,78 @@ class DebugPersistencePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _DebugPersistencePageState extends State<DebugPersistencePage> {
|
||||
SharedPreferences? prefs = null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
SharedPreferences.getInstance().then((value) => setState(() => prefs = value));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(/* Add your UI components here */);
|
||||
return Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Card.outlined(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute<DebugSharedPrefPage>(builder: (context) => DebugSharedPrefPage(sharedPref: prefs!)));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 30, child: Text('')),
|
||||
Expanded(child: Text('Shared Preferences', style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
|
||||
SizedBox(width: 30, child: Text('${prefs?.getKeys().length.toString()}', textAlign: TextAlign.end)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Card.outlined(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute<DebugHiveBoxPage>(builder: (context) => DebugHiveBoxPage(boxName: 'scn-requests', box: Hive.box<SCNRequest>('scn-requests'))));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 30, child: Text('')),
|
||||
Expanded(child: Text('Hive [scn-requests]', style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
|
||||
SizedBox(width: 30, child: Text('${Hive.box<SCNRequest>('scn-requests').length.toString()}', textAlign: TextAlign.end)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Card.outlined(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute<DebugHiveBoxPage>(builder: (context) => DebugHiveBoxPage(boxName: 'scn-requests', box: Hive.box<SCNLog>('scn-logs'))));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 30, child: Text('')),
|
||||
Expanded(child: Text('Hive [scn-logs]', style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
|
||||
SizedBox(width: 30, child: Text('${Hive.box<SCNLog>('scn-logs').length.toString()}', textAlign: TextAlign.end)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
35
flutter/lib/pages/debug/debug_persistence_hive.dart
Normal file
35
flutter/lib/pages/debug/debug_persistence_hive.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
import 'package:simplecloudnotifier/pages/debug/debug_persistence_hiveentry.dart';
|
||||
import 'package:simplecloudnotifier/state/interfaces.dart';
|
||||
|
||||
class DebugHiveBoxPage extends StatelessWidget {
|
||||
final String boxName;
|
||||
final Box<FieldDebuggable> box;
|
||||
|
||||
DebugHiveBoxPage({required this.boxName, required this.box});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SCNScaffold(
|
||||
title: 'Hive: ' + boxName,
|
||||
showSearch: false,
|
||||
showDebug: false,
|
||||
child: ListView.separated(
|
||||
itemCount: box.length,
|
||||
itemBuilder: (context, listIndex) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute<DebugHiveEntryPage>(builder: (context) => DebugHiveEntryPage(value: box.getAt(listIndex)!)));
|
||||
},
|
||||
child: ListTile(
|
||||
title: Text(box.getAt(listIndex).toString(), style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Divider(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
29
flutter/lib/pages/debug/debug_persistence_hiveentry.dart
Normal file
29
flutter/lib/pages/debug/debug_persistence_hiveentry.dart
Normal file
@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
import 'package:simplecloudnotifier/state/interfaces.dart';
|
||||
|
||||
class DebugHiveEntryPage extends StatelessWidget {
|
||||
final FieldDebuggable value;
|
||||
final List<(String, String)> fields;
|
||||
|
||||
DebugHiveEntryPage({required this.value}) : fields = value.debugFieldList();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SCNScaffold(
|
||||
title: 'HiveEntry',
|
||||
showSearch: false,
|
||||
showDebug: false,
|
||||
child: ListView.separated(
|
||||
itemCount: fields.length,
|
||||
itemBuilder: (context, listIndex) {
|
||||
return ListTile(
|
||||
title: Text(fields[listIndex].$1, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text(fields[listIndex].$2, style: TextStyle(fontFamily: "monospace")),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Divider(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
29
flutter/lib/pages/debug/debug_persistence_sharedprefs.dart
Normal file
29
flutter/lib/pages/debug/debug_persistence_sharedprefs.dart
Normal file
@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
|
||||
class DebugSharedPrefPage extends StatelessWidget {
|
||||
final SharedPreferences sharedPref;
|
||||
final List<String> keys;
|
||||
|
||||
DebugSharedPrefPage({required this.sharedPref}) : keys = sharedPref.getKeys().toList();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SCNScaffold(
|
||||
title: 'SharedPreferences',
|
||||
showSearch: false,
|
||||
showDebug: false,
|
||||
child: ListView.separated(
|
||||
itemCount: sharedPref.getKeys().length,
|
||||
itemBuilder: (context, listIndex) {
|
||||
return ListTile(
|
||||
title: Text(keys[listIndex], style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text(sharedPref.get(keys[listIndex]).toString()),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Divider(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/models/channel.dart';
|
||||
import 'package:simplecloudnotifier/models/message.dart';
|
||||
import 'package:simplecloudnotifier/pages/message_view/message_view.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:simplecloudnotifier/state/user_account.dart';
|
||||
import 'package:simplecloudnotifier/pages/message_list/message_list_item.dart';
|
||||
|
||||
@ -57,10 +58,9 @@ class _MessageListPageState extends State<MessageListPage> {
|
||||
} else {
|
||||
_pagingController.appendPage(newItems, npt);
|
||||
}
|
||||
} catch (error) {
|
||||
print("API-Error: "); //TODO remove me, proper error handling
|
||||
print(error); //TODO remove me, proper error handling
|
||||
_pagingController.error = error;
|
||||
} catch (exc, trace) {
|
||||
_pagingController.error = exc.toString();
|
||||
ApplicationLog.error('Failed to list messages: ' + exc.toString(), trace: trace);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:simplecloudnotifier/state/user_account.dart';
|
||||
|
||||
@ -151,8 +152,8 @@ class _SendRootPageState extends State<SendRootPage> {
|
||||
} else {
|
||||
// TODO ("Cannot open URL");
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO ('Cannot open URL');
|
||||
} catch (exc, trace) {
|
||||
ApplicationLog.error('Failed to open URL: ' + exc.toString(), additional: 'URL: ${url}', trace: trace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,128 @@
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:simplecloudnotifier/state/interfaces.dart';
|
||||
import 'package:xid/xid.dart';
|
||||
|
||||
part 'application_log.g.dart';
|
||||
|
||||
class ApplicationLog {}
|
||||
class ApplicationLog {
|
||||
static void debug(String message, {String? additional, StackTrace? trace}) {
|
||||
print('[DEBUG] ${message}: ${additional ?? ''}');
|
||||
|
||||
enum SCNLogLevel { debug, info, warning, error, fatal }
|
||||
Hive.box<SCNLog>('scn-logs').add(SCNLog(
|
||||
id: Xid().toString(),
|
||||
timestamp: DateTime.now(),
|
||||
level: SCNLogLevel.debug,
|
||||
message: message,
|
||||
additional: additional ?? '',
|
||||
trace: trace?.toString() ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
static void info(String message, {String? additional, StackTrace? trace}) {
|
||||
print('[INFO] ${message}: ${additional ?? ''}');
|
||||
|
||||
Hive.box<SCNLog>('scn-logs').add(SCNLog(
|
||||
id: Xid().toString(),
|
||||
timestamp: DateTime.now(),
|
||||
level: SCNLogLevel.info,
|
||||
message: message,
|
||||
additional: additional ?? '',
|
||||
trace: trace?.toString() ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
static void warn(String message, {String? additional, StackTrace? trace}) {
|
||||
print('[WARN] ${message}: ${additional ?? ''}');
|
||||
|
||||
Hive.box<SCNLog>('scn-logs').add(SCNLog(
|
||||
id: Xid().toString(),
|
||||
timestamp: DateTime.now(),
|
||||
level: SCNLogLevel.warning,
|
||||
message: message,
|
||||
additional: additional ?? '',
|
||||
trace: trace?.toString() ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
static void error(String message, {String? additional, StackTrace? trace}) {
|
||||
print('[ERROR] ${message}: ${additional ?? ''}');
|
||||
|
||||
Hive.box<SCNLog>('scn-logs').add(SCNLog(
|
||||
id: Xid().toString(),
|
||||
timestamp: DateTime.now(),
|
||||
level: SCNLogLevel.error,
|
||||
message: message,
|
||||
additional: additional ?? '',
|
||||
trace: trace?.toString() ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
static void fatal(String message, {String? additional, StackTrace? trace}) {
|
||||
print('[FATAL] ${message}: ${additional ?? ''}');
|
||||
|
||||
Hive.box<SCNLog>('scn-logs').add(SCNLog(
|
||||
id: Xid().toString(),
|
||||
timestamp: DateTime.now(),
|
||||
level: SCNLogLevel.fatal,
|
||||
message: message,
|
||||
additional: additional ?? '',
|
||||
trace: trace?.toString() ?? '',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@HiveType(typeId: 103)
|
||||
enum SCNLogLevel {
|
||||
@HiveField(0)
|
||||
debug,
|
||||
@HiveField(1)
|
||||
info,
|
||||
@HiveField(2)
|
||||
warning,
|
||||
@HiveField(3)
|
||||
error,
|
||||
@HiveField(4)
|
||||
fatal
|
||||
}
|
||||
|
||||
@HiveType(typeId: 101)
|
||||
class SCNLog extends HiveObject {
|
||||
class SCNLog extends HiveObject implements FieldDebuggable {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@HiveField(10)
|
||||
final DateTime timestamp;
|
||||
@HiveField(1)
|
||||
@HiveField(11)
|
||||
final SCNLogLevel level;
|
||||
@HiveField(2)
|
||||
@HiveField(12)
|
||||
final String message;
|
||||
@HiveField(3)
|
||||
@HiveField(13)
|
||||
final String additional;
|
||||
@HiveField(4)
|
||||
@HiveField(14)
|
||||
final String trace;
|
||||
|
||||
SCNLog(
|
||||
this.timestamp,
|
||||
this.level,
|
||||
this.message,
|
||||
this.additional,
|
||||
this.trace,
|
||||
);
|
||||
SCNLog({
|
||||
required this.id,
|
||||
required this.timestamp,
|
||||
required this.level,
|
||||
required this.message,
|
||||
required this.additional,
|
||||
required this.trace,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SCNLog[${this.id}]';
|
||||
}
|
||||
|
||||
List<(String, String)> debugFieldList() {
|
||||
return [
|
||||
('id', this.id),
|
||||
('timestamp', this.timestamp.toIso8601String()),
|
||||
('level', this.level.name),
|
||||
('message', this.message),
|
||||
('additional', this.additional),
|
||||
('trace', this.trace),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -17,27 +17,30 @@ class SCNLogAdapter extends TypeAdapter<SCNLog> {
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return SCNLog(
|
||||
fields[0] as DateTime,
|
||||
fields[1] as SCNLogLevel,
|
||||
fields[2] as String,
|
||||
fields[3] as String,
|
||||
fields[4] as String,
|
||||
id: fields[0] as String,
|
||||
timestamp: fields[10] as DateTime,
|
||||
level: fields[11] as SCNLogLevel,
|
||||
message: fields[12] as String,
|
||||
additional: fields[13] as String,
|
||||
trace: fields[14] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, SCNLog obj) {
|
||||
writer
|
||||
..writeByte(5)
|
||||
..writeByte(6)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(10)
|
||||
..write(obj.timestamp)
|
||||
..writeByte(1)
|
||||
..writeByte(11)
|
||||
..write(obj.level)
|
||||
..writeByte(2)
|
||||
..writeByte(12)
|
||||
..write(obj.message)
|
||||
..writeByte(3)
|
||||
..writeByte(13)
|
||||
..write(obj.additional)
|
||||
..writeByte(4)
|
||||
..writeByte(14)
|
||||
..write(obj.trace);
|
||||
}
|
||||
|
||||
@ -51,3 +54,57 @@ class SCNLogAdapter extends TypeAdapter<SCNLog> {
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
||||
class SCNLogLevelAdapter extends TypeAdapter<SCNLogLevel> {
|
||||
@override
|
||||
final int typeId = 103;
|
||||
|
||||
@override
|
||||
SCNLogLevel read(BinaryReader reader) {
|
||||
switch (reader.readByte()) {
|
||||
case 0:
|
||||
return SCNLogLevel.debug;
|
||||
case 1:
|
||||
return SCNLogLevel.info;
|
||||
case 2:
|
||||
return SCNLogLevel.warning;
|
||||
case 3:
|
||||
return SCNLogLevel.error;
|
||||
case 4:
|
||||
return SCNLogLevel.fatal;
|
||||
default:
|
||||
return SCNLogLevel.debug;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, SCNLogLevel obj) {
|
||||
switch (obj) {
|
||||
case SCNLogLevel.debug:
|
||||
writer.writeByte(0);
|
||||
break;
|
||||
case SCNLogLevel.info:
|
||||
writer.writeByte(1);
|
||||
break;
|
||||
case SCNLogLevel.warning:
|
||||
writer.writeByte(2);
|
||||
break;
|
||||
case SCNLogLevel.error:
|
||||
writer.writeByte(3);
|
||||
break;
|
||||
case SCNLogLevel.fatal:
|
||||
writer.writeByte(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is SCNLogLevelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
3
flutter/lib/state/interfaces.dart
Normal file
3
flutter/lib/state/interfaces.dart
Normal file
@ -0,0 +1,3 @@
|
||||
abstract class FieldDebuggable {
|
||||
List<(String, String)> debugFieldList();
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:simplecloudnotifier/models/api_error.dart';
|
||||
import 'package:simplecloudnotifier/state/interfaces.dart';
|
||||
import 'package:xid/xid.dart';
|
||||
|
||||
part 'request_log.g.dart';
|
||||
|
||||
class RequestLog {
|
||||
static void addRequestException(String name, DateTime tStart, String method, Uri uri, String reqbody, Map<String, String> reqheaders, dynamic e, StackTrace trace) {
|
||||
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||
id: Xid().toString(),
|
||||
timestampStart: tStart,
|
||||
timestampEnd: DateTime.now(),
|
||||
name: name,
|
||||
@ -24,6 +27,7 @@ class RequestLog {
|
||||
|
||||
static void addRequestAPIError(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders, APIError apierr) {
|
||||
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||
id: Xid().toString(),
|
||||
timestampStart: t0,
|
||||
timestampEnd: DateTime.now(),
|
||||
name: name,
|
||||
@ -42,6 +46,7 @@ class RequestLog {
|
||||
|
||||
static void addRequestErrorStatuscode(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders) {
|
||||
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||
id: Xid().toString(),
|
||||
timestampStart: t0,
|
||||
timestampEnd: DateTime.now(),
|
||||
name: name,
|
||||
@ -60,6 +65,7 @@ class RequestLog {
|
||||
|
||||
static void addRequestSuccess(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders) {
|
||||
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||
id: Xid().toString(),
|
||||
timestampStart: t0,
|
||||
timestampEnd: DateTime.now(),
|
||||
name: name,
|
||||
@ -78,6 +84,7 @@ class RequestLog {
|
||||
|
||||
static void addRequestDecodeError(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders, Object exc, StackTrace trace) {
|
||||
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||
id: Xid().toString(),
|
||||
timestampStart: t0,
|
||||
timestampEnd: DateTime.now(),
|
||||
name: name,
|
||||
@ -96,37 +103,41 @@ class RequestLog {
|
||||
}
|
||||
|
||||
@HiveType(typeId: 100)
|
||||
class SCNRequest extends HiveObject {
|
||||
class SCNRequest extends HiveObject implements FieldDebuggable {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@HiveField(10)
|
||||
final DateTime timestampStart;
|
||||
@HiveField(1)
|
||||
@HiveField(11)
|
||||
final DateTime timestampEnd;
|
||||
@HiveField(2)
|
||||
@HiveField(12)
|
||||
final String name;
|
||||
@HiveField(3)
|
||||
final String type;
|
||||
@HiveField(4)
|
||||
@HiveField(13)
|
||||
final String type; // SUCCESS | EXCEPTION | API_ERROR | ERROR_STATUSCODE | DECODE_ERROR
|
||||
@HiveField(14)
|
||||
final String error;
|
||||
@HiveField(5)
|
||||
@HiveField(15)
|
||||
final String stackTrace;
|
||||
|
||||
@HiveField(6)
|
||||
@HiveField(21)
|
||||
final String method;
|
||||
@HiveField(7)
|
||||
@HiveField(22)
|
||||
final String url;
|
||||
@HiveField(8)
|
||||
@HiveField(23)
|
||||
final Map<String, String> requestHeaders;
|
||||
@HiveField(12)
|
||||
@HiveField(24)
|
||||
final String requestBody;
|
||||
|
||||
@HiveField(9)
|
||||
@HiveField(31)
|
||||
final int responseStatusCode;
|
||||
@HiveField(10)
|
||||
@HiveField(32)
|
||||
final Map<String, String> responseHeaders;
|
||||
@HiveField(11)
|
||||
@HiveField(33)
|
||||
final String responseBody;
|
||||
|
||||
SCNRequest({
|
||||
required this.id,
|
||||
required this.timestampStart,
|
||||
required this.timestampEnd,
|
||||
required this.name,
|
||||
@ -141,4 +152,28 @@ class SCNRequest extends HiveObject {
|
||||
required this.error,
|
||||
required this.stackTrace,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SCNRequest[${this.id}]';
|
||||
}
|
||||
|
||||
List<(String, String)> debugFieldList() {
|
||||
return [
|
||||
('id', this.id),
|
||||
('timestampStart', this.timestampStart.toIso8601String()),
|
||||
('timestampEnd', this.timestampEnd.toIso8601String()),
|
||||
('name', this.name),
|
||||
('method', this.method),
|
||||
('url', this.url),
|
||||
for (var (idx, item) in this.requestHeaders.entries.indexed) ('requestHeaders[$idx]', '${item.key}=${item.value}'),
|
||||
('requestBody', this.requestBody),
|
||||
('responseStatusCode', this.responseStatusCode.toString()),
|
||||
for (var (idx, item) in this.responseHeaders.entries.indexed) ('responseHeaders[$idx]', '${item.key}=${item.value}'),
|
||||
('responseBody', this.responseBody),
|
||||
('type', this.type),
|
||||
('error', this.error),
|
||||
('stackTrace', this.stackTrace),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -17,51 +17,54 @@ class SCNRequestAdapter extends TypeAdapter<SCNRequest> {
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return SCNRequest(
|
||||
timestampStart: fields[0] as DateTime,
|
||||
timestampEnd: fields[1] as DateTime,
|
||||
name: fields[2] as String,
|
||||
method: fields[6] as String,
|
||||
url: fields[7] as String,
|
||||
requestHeaders: (fields[8] as Map).cast<String, String>(),
|
||||
requestBody: fields[12] as String,
|
||||
responseStatusCode: fields[9] as int,
|
||||
responseHeaders: (fields[10] as Map).cast<String, String>(),
|
||||
responseBody: fields[11] as String,
|
||||
type: fields[3] as String,
|
||||
error: fields[4] as String,
|
||||
stackTrace: fields[5] as String,
|
||||
id: fields[0] as String,
|
||||
timestampStart: fields[10] as DateTime,
|
||||
timestampEnd: fields[11] as DateTime,
|
||||
name: fields[12] as String,
|
||||
method: fields[21] as String,
|
||||
url: fields[22] as String,
|
||||
requestHeaders: (fields[23] as Map).cast<String, String>(),
|
||||
requestBody: fields[24] as String,
|
||||
responseStatusCode: fields[31] as int,
|
||||
responseHeaders: (fields[32] as Map).cast<String, String>(),
|
||||
responseBody: fields[33] as String,
|
||||
type: fields[13] as String,
|
||||
error: fields[14] as String,
|
||||
stackTrace: fields[15] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, SCNRequest obj) {
|
||||
writer
|
||||
..writeByte(13)
|
||||
..writeByte(14)
|
||||
..writeByte(0)
|
||||
..write(obj.timestampStart)
|
||||
..writeByte(1)
|
||||
..write(obj.timestampEnd)
|
||||
..writeByte(2)
|
||||
..write(obj.name)
|
||||
..writeByte(3)
|
||||
..write(obj.type)
|
||||
..writeByte(4)
|
||||
..write(obj.error)
|
||||
..writeByte(5)
|
||||
..write(obj.stackTrace)
|
||||
..writeByte(6)
|
||||
..write(obj.method)
|
||||
..writeByte(7)
|
||||
..write(obj.url)
|
||||
..writeByte(8)
|
||||
..write(obj.requestHeaders)
|
||||
..writeByte(12)
|
||||
..write(obj.requestBody)
|
||||
..writeByte(9)
|
||||
..write(obj.responseStatusCode)
|
||||
..write(obj.id)
|
||||
..writeByte(10)
|
||||
..write(obj.responseHeaders)
|
||||
..write(obj.timestampStart)
|
||||
..writeByte(11)
|
||||
..write(obj.timestampEnd)
|
||||
..writeByte(12)
|
||||
..write(obj.name)
|
||||
..writeByte(13)
|
||||
..write(obj.type)
|
||||
..writeByte(14)
|
||||
..write(obj.error)
|
||||
..writeByte(15)
|
||||
..write(obj.stackTrace)
|
||||
..writeByte(21)
|
||||
..write(obj.method)
|
||||
..writeByte(22)
|
||||
..write(obj.url)
|
||||
..writeByte(23)
|
||||
..write(obj.requestHeaders)
|
||||
..writeByte(24)
|
||||
..write(obj.requestBody)
|
||||
..writeByte(31)
|
||||
..write(obj.responseStatusCode)
|
||||
..writeByte(32)
|
||||
..write(obj.responseHeaders)
|
||||
..writeByte(33)
|
||||
..write(obj.responseBody);
|
||||
}
|
||||
|
||||
|
@ -280,7 +280,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
hive:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hive
|
||||
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||
@ -884,6 +884,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
xid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: xid
|
||||
sha256: "58b61c7c1234810afa500cde7556d7c407ce72154e0d5a8a5618690f03848dbd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -10,6 +10,7 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
font_awesome_flutter: '>= 4.7.0'
|
||||
cupertino_icons: ^1.0.2
|
||||
http: ^1.2.0
|
||||
@ -20,10 +21,10 @@ dependencies:
|
||||
infinite_scroll_pagination: ^4.0.0
|
||||
intl: ^0.19.0
|
||||
path_provider: ^2.1.3
|
||||
hive: ^2.2.3
|
||||
hive_flutter: ^1.1.0
|
||||
package_info_plus: ^8.0.0
|
||||
fl_toast: ^3.2.0
|
||||
xid: ^1.2.1
|
||||
|
||||
|
||||
dependency_overrides:
|
||||
|
Loading…
Reference in New Issue
Block a user