1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-25 10:43:13 +02:00

feature(mobile): configurable log level (#2248)

* feature(mobile): configurable log level

* increase maxLogEntries to 500

---------

Co-authored-by: Fynn Petersen-Frey <zoodyy@users.noreply.github.com>
This commit is contained in:
Fynn Petersen-Frey 2023-04-14 15:50:46 +02:00 committed by GitHub
parent 4952b3a2d6
commit d500ef77cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 17 deletions

View File

@ -43,17 +43,14 @@ enum AppSettingsEnum<T> {
"selectedAlbumSortOrder", "selectedAlbumSortOrder",
0, 0,
), ),
advancedTroubleshooting<bool>( advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
StoreKey.advancedTroubleshooting, logLevel<int>(StoreKey.logLevel, null, 5) // Level.INFO = 5
"advancedTroubleshooting",
false,
),
; ;
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
final StoreKey<T> storeKey; final StoreKey<T> storeKey;
final String hiveKey; final String? hiveKey;
final T defaultValue; final T defaultValue;
} }

View File

@ -5,6 +5,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
import 'package:immich_mobile/modules/settings/ui/settings_switch_list_tile.dart'; import 'package:immich_mobile/modules/settings/ui/settings_switch_list_tile.dart';
import 'package:immich_mobile/shared/services/immich_logger.service.dart';
import 'package:logging/logging.dart';
class AdvancedSettings extends HookConsumerWidget { class AdvancedSettings extends HookConsumerWidget {
const AdvancedSettings({super.key}); const AdvancedSettings({super.key});
@ -13,16 +15,21 @@ class AdvancedSettings extends HookConsumerWidget {
final appSettingService = ref.watch(appSettingsServiceProvider); final appSettingService = ref.watch(appSettingsServiceProvider);
final isEnabled = final isEnabled =
useState(AppSettingsEnum.advancedTroubleshooting.defaultValue); useState(AppSettingsEnum.advancedTroubleshooting.defaultValue);
final levelId = useState(AppSettingsEnum.logLevel.defaultValue);
useEffect( useEffect(
() { () {
isEnabled.value = appSettingService.getSetting<bool>( isEnabled.value = appSettingService.getSetting<bool>(
AppSettingsEnum.advancedTroubleshooting, AppSettingsEnum.advancedTroubleshooting,
); );
levelId.value = appSettingService.getSetting(AppSettingsEnum.logLevel);
return null; return null;
}, },
[], [],
); );
final logLevel = Level.LEVELS[levelId.value].name;
return ExpansionTile( return ExpansionTile(
textColor: Theme.of(context).primaryColor, textColor: Theme.of(context).primaryColor,
title: const Text( title: const Text(
@ -46,6 +53,30 @@ class AdvancedSettings extends HookConsumerWidget {
title: "advanced_settings_troubleshooting_title".tr(), title: "advanced_settings_troubleshooting_title".tr(),
subtitle: "advanced_settings_troubleshooting_subtitle".tr(), subtitle: "advanced_settings_troubleshooting_subtitle".tr(),
), ),
ListTile(
dense: true,
title: Text(
// Not translated because the levels are only English
"Log level: $logLevel",
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Slider(
value: levelId.value.toDouble(),
onChanged: (double v) => levelId.value = v.toInt(),
onChangeEnd: (double v) {
appSettingService.setSetting(
AppSettingsEnum.logLevel,
v.toInt(),
);
ImmichLogger().level = Level.LEVELS[v.toInt()];
},
max: 8,
min: 1.0,
divisions: 7,
label: logLevel,
activeColor: Theme.of(context).primaryColor,
),
),
], ],
); );
} }

View File

@ -168,6 +168,7 @@ enum StoreKey<T> {
albumThumbnailCacheSize<int>(112, type: int), albumThumbnailCacheSize<int>(112, type: int),
selectedAlbumSortOrder<int>(113, type: int), selectedAlbumSortOrder<int>(113, type: int),
advancedTroubleshooting<bool>(114, type: bool), advancedTroubleshooting<bool>(114, type: bool),
logLevel<int>(115, type: int),
; ;
const StoreKey( const StoreKey(

View File

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:immich_mobile/shared/models/logger_message.model.dart'; import 'package:immich_mobile/shared/models/logger_message.model.dart';
import 'package:immich_mobile/shared/models/store.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -18,7 +19,7 @@ import 'package:share_plus/share_plus.dart';
/// and generate a csv file. /// and generate a csv file.
class ImmichLogger { class ImmichLogger {
static final ImmichLogger _instance = ImmichLogger._internal(); static final ImmichLogger _instance = ImmichLogger._internal();
final maxLogEntries = 200; final maxLogEntries = 500;
final Isar _db = Isar.getInstance()!; final Isar _db = Isar.getInstance()!;
List<LoggerMessage> _msgBuffer = []; List<LoggerMessage> _msgBuffer = [];
Timer? _timer; Timer? _timer;
@ -27,10 +28,13 @@ class ImmichLogger {
ImmichLogger._internal() { ImmichLogger._internal() {
_removeOverflowMessages(); _removeOverflowMessages();
Logger.root.level = Level.INFO; final int levelId = Store.get(StoreKey.logLevel, 5); // 5 is INFO
Logger.root.level = Level.LEVELS[levelId];
Logger.root.onRecord.listen(_writeLogToDatabase); Logger.root.onRecord.listen(_writeLogToDatabase);
} }
set level(Level level) => Logger.root.level = level;
List<LoggerMessage> get messages { List<LoggerMessage> get messages {
final inDb = final inDb =
_db.loggerMessages.where(sort: Sort.desc).anyId().findAllSync(); _db.loggerMessages.where(sort: Sort.desc).anyId().findAllSync();

View File

@ -389,7 +389,13 @@ class SyncService {
_addAlbumFromDevice(ape, existing, excludedAssets), _addAlbumFromDevice(ape, existing, excludedAssets),
onlySecond: (Album a) => _removeAlbumFromDb(a, deleteCandidates), onlySecond: (Album a) => _removeAlbumFromDb(a, deleteCandidates),
); );
_log.fine(
"Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete",
);
final pair = _handleAssetRemoval(deleteCandidates, existing, remote: false); final pair = _handleAssetRemoval(deleteCandidates, existing, remote: false);
_log.fine(
"${pair.first.length} assets to delete, ${pair.second.length} to update",
);
if (pair.first.isNotEmpty || pair.second.isNotEmpty) { if (pair.first.isNotEmpty || pair.second.isNotEmpty) {
await _db.writeTxn(() async { await _db.writeTxn(() async {
await _db.assets.deleteAll(pair.first); await _db.assets.deleteAll(pair.first);
@ -415,6 +421,7 @@ class SyncService {
bool forceRefresh = false, bool forceRefresh = false,
]) async { ]) async {
if (!forceRefresh && !await _hasAssetPathEntityChanged(ape, album)) { if (!forceRefresh && !await _hasAssetPathEntityChanged(ape, album)) {
_log.fine("Local album ${ape.name} has not changed. Skipping sync.");
return false; return false;
} }
if (!forceRefresh && if (!forceRefresh &&
@ -441,9 +448,18 @@ class SyncService {
album.name == ape.name && album.name == ape.name &&
album.modifiedAt == ape.lastModified) { album.modifiedAt == ape.lastModified) {
// changes only affeted excluded albums // changes only affeted excluded albums
_log.fine(
"Only excluded assets in local album ${ape.name} changed. Stopping sync.",
);
return false; return false;
} }
_log.fine(
"Syncing local album ${ape.name}. ${toAdd.length} assets to add, ${toUpdate.length} to update, ${toDelete.length} to delete",
);
final result = await _linkWithExistingFromDb(toAdd); final result = await _linkWithExistingFromDb(toAdd);
_log.fine(
"Linking assets to add with existing from db. ${result.first.length} existing, ${result.second.length} to update",
);
deleteCandidates.addAll(toDelete); deleteCandidates.addAll(toDelete);
existing.addAll(result.first); existing.addAll(result.first);
album.name = ape.name; album.name = ape.name;
@ -462,9 +478,9 @@ class SyncService {
album.thumbnail.value ??= await album.assets.filter().findFirst(); album.thumbnail.value ??= await album.assets.filter().findFirst();
await album.thumbnail.save(); await album.thumbnail.save();
}); });
_log.info("Synced changes of local album $ape to DB"); _log.info("Synced changes of local album ${ape.name} to DB");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to update synced album $ape in DB: $e"); _log.severe("Failed to update synced album ${ape.name} in DB: $e");
} }
return true; return true;
@ -499,9 +515,9 @@ class SyncService {
await album.assets.update(link: result.first + result.second); await album.assets.update(link: result.first + result.second);
await _db.albums.put(album); await _db.albums.put(album);
}); });
_log.info("Fast synced local album $ape to DB"); _log.info("Fast synced local album ${ape.name} to DB");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to fast sync local album $ape to DB: $e"); _log.severe("Failed to fast sync local album ${ape.name} to DB: $e");
return false; return false;
} }
@ -515,7 +531,7 @@ class SyncService {
List<Asset> existing, [ List<Asset> existing, [
Set<String>? excludedAssets, Set<String>? excludedAssets,
]) async { ]) async {
_log.info("Syncing a new local album to DB: $ape"); _log.info("Syncing a new local album to DB: ${ape.name}");
final Album a = Album.local(ape); final Album a = Album.local(ape);
final result = await _linkWithExistingFromDb( final result = await _linkWithExistingFromDb(
await ape.getAssets(excludedAssets: excludedAssets), await ape.getAssets(excludedAssets: excludedAssets),
@ -531,9 +547,9 @@ class SyncService {
a.thumbnail.value = thumb; a.thumbnail.value = thumb;
try { try {
await _db.writeTxn(() => _db.albums.store(a)); await _db.writeTxn(() => _db.albums.store(a));
_log.info("Added a new local album to DB: $ape"); _log.info("Added a new local album to DB: ${ape.name}");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to add new local album $ape to DB: $e"); _log.severe("Failed to add new local album ${ape.name} to DB: $e");
} }
} }
@ -574,7 +590,11 @@ class SyncService {
return true; return true;
} }
}, },
onlyFirst: (Asset a) => {}, onlyFirst: (Asset a) => _log.finer(
"_linkWithExistingFromDb encountered asset only in DB: $a",
null,
StackTrace.current,
),
onlySecond: (Asset b) => toUpsert.add(b), onlySecond: (Asset b) => toUpsert.add(b),
); );
return Pair(existing, toUpsert); return Pair(existing, toUpsert);
@ -663,6 +683,7 @@ Pair<List<int>, List<Asset>> _handleAssetRemoval(
compare: Asset.compareById, compare: Asset.compareById,
remote: remote, remote: remote,
); );
assert(triple.first.isEmpty, "toAdd should be empty in _handleAssetRemoval");
return Pair(triple.third.map((e) => e.id).toList(), triple.second); return Pair(triple.third.map((e) => e.id).toList(), triple.second);
} }

View File

@ -112,7 +112,9 @@ FutureOr<void> _migrateDuplicatedAssetsBox(Box<HiveDuplicatedAssets> box) {
Future<void> _migrateAppSettingsBox(Box box) async { Future<void> _migrateAppSettingsBox(Box box) async {
for (AppSettingsEnum s in AppSettingsEnum.values) { for (AppSettingsEnum s in AppSettingsEnum.values) {
await _migrateKey(box, s.hiveKey, s.storeKey); if (s.hiveKey != null) {
await _migrateKey(box, s.hiveKey!, s.storeKey);
}
} }
} }