feat: use sqlite for logging (#20414)
* feat: use drift for logging * fix: tests * feat: use the truncate limit from constants.ts as default * chore: move setupAll to top level and restructure * chore: code review changes * fix: inherits * feat: raise log line limit to 2000 * limit getAll to 250 lines * delete DLog and make LogRepository not a singleton * fix: drift build settings and `make migration` * fix: tests * remove sensitive log --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -1,27 +1,43 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
|
||||
|
||||
class IsarLogRepository extends IsarDatabaseRepository {
|
||||
final Isar _db;
|
||||
const IsarLogRepository(super.db) : _db = db;
|
||||
class LogRepository {
|
||||
final DriftLogger _db;
|
||||
const LogRepository(this._db);
|
||||
|
||||
Future<bool> deleteAll() async {
|
||||
await transaction(() async => await _db.loggerMessages.clear());
|
||||
await _db.logMessageEntity.deleteAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<List<LogMessage>> getAll() async {
|
||||
final logs = await _db.loggerMessages.where().sortByCreatedAtDesc().findAll();
|
||||
return logs.map((l) => l.toDto()).toList();
|
||||
Future<List<LogMessage>> getAll({int limit = 250}) async {
|
||||
final query = _db.logMessageEntity.select()
|
||||
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||
..limit(limit);
|
||||
|
||||
return query.map((log) => log.toDto()).get();
|
||||
}
|
||||
|
||||
LogMessageEntityCompanion _toEntityCompanion(LogMessage log) {
|
||||
return LogMessageEntityCompanion.insert(
|
||||
message: log.message,
|
||||
level: log.level,
|
||||
createdAt: log.createdAt,
|
||||
logger: Value(log.logger),
|
||||
details: Value(log.error),
|
||||
stack: Value(log.stack),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> insert(LogMessage log) async {
|
||||
final logEntity = LoggerMessage.fromDto(log);
|
||||
final logEntity = _toEntityCompanion(log);
|
||||
|
||||
try {
|
||||
await transaction(() => _db.loggerMessages.put(logEntity));
|
||||
await _db.logMessageEntity.insertOne(logEntity);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
@@ -30,19 +46,30 @@ class IsarLogRepository extends IsarDatabaseRepository {
|
||||
}
|
||||
|
||||
Future<bool> insertAll(Iterable<LogMessage> logs) async {
|
||||
await transaction(() async {
|
||||
final logEntities = logs.map((log) => LoggerMessage.fromDto(log)).toList();
|
||||
await _db.loggerMessages.putAll(logEntities);
|
||||
});
|
||||
final logEntities = logs.map(_toEntityCompanion).toList();
|
||||
await _db.logMessageEntity.insertAll(logEntities);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> truncate({int limit = 250}) async {
|
||||
await transaction(() async {
|
||||
final count = await _db.loggerMessages.count();
|
||||
if (count <= limit) return;
|
||||
final toRemove = count - limit;
|
||||
await _db.loggerMessages.where().limit(toRemove).deleteAll();
|
||||
});
|
||||
Future<void> deleteByLogger(String logger) async {
|
||||
await _db.logMessageEntity.deleteWhere((row) => row.logger.equals(logger));
|
||||
}
|
||||
|
||||
Stream<List<LogMessage>> watchMessages(String logger) {
|
||||
final query = _db.logMessageEntity.select()
|
||||
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||
..where((row) => row.logger.equals(logger));
|
||||
|
||||
return query.watch().map((rows) => rows.map((row) => row.toDto()).toList());
|
||||
}
|
||||
|
||||
Future<void> truncate({int limit = kLogTruncateLimit}) async {
|
||||
final totalCount = await _db.managers.logMessageEntity.count();
|
||||
if (totalCount > limit) {
|
||||
final rowsToDelete = totalCount - limit;
|
||||
|
||||
await _db.managers.logMessageEntity.orderBy((o) => o.createdAt.asc()).limit(rowsToDelete).delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
||||
|
||||
import 'logger_db.repository.drift.dart';
|
||||
|
||||
@DriftDatabase(tables: [LogMessageEntity])
|
||||
class DriftLogger extends $DriftLogger implements IDatabaseRepository {
|
||||
DriftLogger([QueryExecutor? executor])
|
||||
: super(
|
||||
executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)),
|
||||
);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
await customStatement('PRAGMA synchronous = NORMAL');
|
||||
await customStatement('PRAGMA journal_mode = WAL');
|
||||
await customStatement('PRAGMA busy_timeout = 500');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'
|
||||
as i1;
|
||||
|
||||
abstract class $DriftLogger extends i0.GeneratedDatabase {
|
||||
$DriftLogger(i0.QueryExecutor e) : super(e);
|
||||
$DriftLoggerManager get managers => $DriftLoggerManager(this);
|
||||
late final i1.$LogMessageEntityTable logMessageEntity = i1
|
||||
.$LogMessageEntityTable(this);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@override
|
||||
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [logMessageEntity];
|
||||
@override
|
||||
i0.DriftDatabaseOptions get options =>
|
||||
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
|
||||
}
|
||||
|
||||
class $DriftLoggerManager {
|
||||
final $DriftLogger _db;
|
||||
$DriftLoggerManager(this._db);
|
||||
i1.$$LogMessageEntityTableTableManager get logMessageEntity =>
|
||||
i1.$$LogMessageEntityTableTableManager(_db, _db.logMessageEntity);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
@@ -107,7 +106,6 @@ class SyncApiRepository {
|
||||
}
|
||||
stopwatch.stop();
|
||||
_logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");
|
||||
DLog.log("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");
|
||||
}
|
||||
|
||||
List<SyncEvent> _parseLines(List<String> lines) {
|
||||
|
||||
Reference in New Issue
Block a user