added logs

This commit is contained in:
Mike Schwörer 2024-05-26 00:20:25 +02:00
parent 51e89ce901
commit dae5182f90
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
20 changed files with 588 additions and 110 deletions

View File

@ -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;
}
}

View File

@ -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(),
),

View File

@ -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,
);

View File

@ -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';

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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),
],
),
),
);
}
}

View File

@ -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)),
],
),
),
),
),
],
),
);
}
}

View 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(),
),
);
}
}

View 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(),
),
);
}
}

View 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(),
),
);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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),
];
}
}

View File

@ -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;
}

View File

@ -0,0 +1,3 @@
abstract class FieldDebuggable {
List<(String, String)> debugFieldList();
}

View File

@ -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),
];
}
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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: