mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-06-02 20:22:12 +00:00
add optional database tracing
Some checks failed
Flutter analyze & test / flutter_analyze_and_test (push) Has been cancelled
Some checks failed
Flutter analyze & test / flutter_analyze_and_test (push) Has been cancelled
This commit is contained in:
parent
7559434f86
commit
dc0ef25d73
6 changed files with 193 additions and 2 deletions
164
lib/src/database/drift_logging_interceptor.dart
Normal file
164
lib/src/database/drift_logging_interceptor.dart
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import 'dart:async';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
||||
class DriftLoggingInterceptor extends QueryInterceptor {
|
||||
bool get _isEnabled {
|
||||
try {
|
||||
if (!userService.isUserCreated) return false;
|
||||
return userService.currentUser.enableDatabaseLogging;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _findUuids(dynamic value) {
|
||||
if (value == null) return const [];
|
||||
final uuids = <String>[];
|
||||
final uuidRegex = RegExp(
|
||||
'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}',
|
||||
);
|
||||
if (value is String) {
|
||||
for (final match in uuidRegex.allMatches(value)) {
|
||||
uuids.add(match.group(0)!);
|
||||
}
|
||||
} else if (value is Iterable) {
|
||||
for (final element in value) {
|
||||
uuids.addAll(_findUuids(element));
|
||||
}
|
||||
} else if (value is Map) {
|
||||
for (final element in value.values) {
|
||||
uuids.addAll(_findUuids(element));
|
||||
}
|
||||
} else {
|
||||
final str = value.toString();
|
||||
for (final match in uuidRegex.allMatches(str)) {
|
||||
uuids.add(match.group(0)!);
|
||||
}
|
||||
}
|
||||
return uuids.toSet().toList();
|
||||
}
|
||||
|
||||
Future<T> _run<T>(
|
||||
String operation,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
Future<T> Function() query,
|
||||
) async {
|
||||
if (!_isEnabled) {
|
||||
return query();
|
||||
}
|
||||
final stopwatch = Stopwatch()..start();
|
||||
try {
|
||||
final result = await query();
|
||||
final elapsed = stopwatch.elapsedMilliseconds;
|
||||
final uuids = _findUuids(args);
|
||||
if (uuids.isNotEmpty) {
|
||||
Log.info(
|
||||
'[DriftDB] $operation succeeded in ${elapsed}ms: "$statement" | UUIDs: $uuids',
|
||||
);
|
||||
} else {
|
||||
Log.info(
|
||||
'[DriftDB] $operation succeeded in ${elapsed}ms: "$statement"',
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
final elapsed = stopwatch.elapsedMilliseconds;
|
||||
final uuids = _findUuids(args);
|
||||
if (uuids.isNotEmpty) {
|
||||
Log.info(
|
||||
'[DriftDB] $operation failed after ${elapsed}ms ($e): "$statement" | UUIDs: $uuids',
|
||||
);
|
||||
} else {
|
||||
Log.info(
|
||||
'[DriftDB] $operation failed after ${elapsed}ms ($e): "$statement"',
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInsert(
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run('INSERT', statement, args, () => executor.runInsert(statement, args));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runUpdate(
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run('UPDATE', statement, args, () => executor.runUpdate(statement, args));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runDelete(
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run('DELETE', statement, args, () => executor.runDelete(statement, args));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runCustom(
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run('CUSTOM', statement, args, () => executor.runCustom(statement, args));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runBatched(
|
||||
QueryExecutor executor,
|
||||
BatchedStatements statements,
|
||||
) async {
|
||||
if (!_isEnabled) {
|
||||
return executor.runBatched(statements);
|
||||
}
|
||||
final stopwatch = Stopwatch()..start();
|
||||
try {
|
||||
await executor.runBatched(statements);
|
||||
final elapsed = stopwatch.elapsedMilliseconds;
|
||||
final uuids = <String>[];
|
||||
for (final batchArg in statements.arguments) {
|
||||
uuids.addAll(_findUuids(batchArg.arguments));
|
||||
}
|
||||
final statementsStr = statements.statements.join('; ');
|
||||
if (uuids.isNotEmpty) {
|
||||
Log.info(
|
||||
'[DriftDB] BATCH succeeded in ${elapsed}ms: "$statementsStr" | UUIDs: $uuids',
|
||||
);
|
||||
} else {
|
||||
Log.info(
|
||||
'[DriftDB] BATCH succeeded in ${elapsed}ms: "$statementsStr"',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
final elapsed = stopwatch.elapsedMilliseconds;
|
||||
final uuids = <String>[];
|
||||
for (final batchArg in statements.arguments) {
|
||||
uuids.addAll(_findUuids(batchArg.arguments));
|
||||
}
|
||||
final statementsStr = statements.statements.join('; ');
|
||||
if (uuids.isNotEmpty) {
|
||||
Log.info(
|
||||
'[DriftDB] BATCH failed after ${elapsed}ms ($e): "$statementsStr" | UUIDs: $uuids',
|
||||
);
|
||||
} else {
|
||||
Log.info(
|
||||
'[DriftDB] BATCH failed after ${elapsed}ms ($e): "$statementsStr"',
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import 'package:drift/drift.dart';
|
|||
import 'package:drift_flutter/drift_flutter.dart'
|
||||
show DriftNativeOptions, driftDatabase;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
import 'package:twonly/src/database/daos/groups.dao.dart';
|
||||
import 'package:twonly/src/database/daos/key_verification.dao.dart';
|
||||
|
|
@ -11,6 +12,7 @@ import 'package:twonly/src/database/daos/reactions.dao.dart';
|
|||
import 'package:twonly/src/database/daos/receipts.dao.dart';
|
||||
import 'package:twonly/src/database/daos/shortcuts.dao.dart';
|
||||
import 'package:twonly/src/database/daos/user_discovery.dao.dart';
|
||||
import 'package:twonly/src/database/drift_logging_interceptor.dart';
|
||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||
|
|
@ -83,7 +85,7 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
int get schemaVersion => 17;
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
final connection = driftDatabase(
|
||||
name: 'twonly',
|
||||
native: DriftNativeOptions(
|
||||
databaseDirectory: getApplicationSupportDirectory,
|
||||
|
|
@ -96,6 +98,12 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
},
|
||||
),
|
||||
);
|
||||
try {
|
||||
if (userService.isUserCreated && userService.currentUser.enableDatabaseLogging) {
|
||||
return connection.interceptWith(DriftLoggingInterceptor());
|
||||
}
|
||||
} catch (_) {}
|
||||
return connection;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ class UserData {
|
|||
@JsonKey(defaultValue: false)
|
||||
bool requestedAudioPermission = false;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
bool enableDatabaseLogging = false;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
bool automaticallyMarkEqualMediaFilesAsOpened = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
|||
..defaultShowTime = (json['defaultShowTime'] as num?)?.toInt()
|
||||
..requestedAudioPermission =
|
||||
json['requestedAudioPermission'] as bool? ?? false
|
||||
..enableDatabaseLogging = json['enableDatabaseLogging'] as bool? ?? false
|
||||
..automaticallyMarkEqualMediaFilesAsOpened =
|
||||
json['automaticallyMarkEqualMediaFilesAsOpened'] as bool? ?? false
|
||||
..videoStabilizationEnabled =
|
||||
|
|
@ -135,6 +136,7 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
|||
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
||||
'defaultShowTime': instance.defaultShowTime,
|
||||
'requestedAudioPermission': instance.requestedAudioPermission,
|
||||
'enableDatabaseLogging': instance.enableDatabaseLogging,
|
||||
'automaticallyMarkEqualMediaFilesAsOpened':
|
||||
instance.automaticallyMarkEqualMediaFilesAsOpened,
|
||||
'videoStabilizationEnabled': instance.videoStabilizationEnabled,
|
||||
|
|
|
|||
|
|
@ -263,6 +263,12 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> toggleDatabaseLogging() async {
|
||||
await UserService.update(
|
||||
(u) => u.enableDatabaseLogging = !u.enableDatabaseLogging,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
@ -282,6 +288,14 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
|||
onChanged: (_) => toggleDeveloperSettings(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Enable Database Logging'),
|
||||
onTap: toggleDatabaseLogging,
|
||||
trailing: Switch(
|
||||
value: userService.currentUser.enableDatabaseLogging,
|
||||
onChanged: (_) => toggleDatabaseLogging(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('User ID'),
|
||||
subtitle: Text(userService.currentUser.userId.toString()),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
|
|||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.2.23+132
|
||||
version: 0.2.24+133
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue