mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 20:25:32 -04:00
feat: dev logs
This commit is contained in:
parent
1db9bc04a6
commit
7e5f88e678
@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/models/local_album.model.dart';
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:immich_mobile/utils/diff.dart';
|
import 'package:immich_mobile/utils/diff.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
@ -41,15 +42,20 @@ class DeviceSyncService {
|
|||||||
try {
|
try {
|
||||||
if (await _nativeSyncApi.shouldFullSync()) {
|
if (await _nativeSyncApi.shouldFullSync()) {
|
||||||
_log.fine("Cannot use partial sync. Performing full sync");
|
_log.fine("Cannot use partial sync. Performing full sync");
|
||||||
|
DLog.log("Cannot use partial sync. Performing full sync");
|
||||||
return await fullSync();
|
return await fullSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
final delta = await _nativeSyncApi.getMediaChanges();
|
final delta = await _nativeSyncApi.getMediaChanges();
|
||||||
if (!delta.hasChanges) {
|
if (!delta.hasChanges) {
|
||||||
_log.fine("No media changes detected. Skipping sync");
|
_log.fine("No media changes detected. Skipping sync");
|
||||||
|
DLog.log("No media changes detected. Skipping sync");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLog.log("Delta updated: ${delta.updates.length}");
|
||||||
|
DLog.log("Delta deleted: ${delta.deletes.length}");
|
||||||
|
|
||||||
final deviceAlbums = await _nativeSyncApi.getAlbums();
|
final deviceAlbums = await _nativeSyncApi.getAlbums();
|
||||||
await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums());
|
await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums());
|
||||||
await _localAlbumRepository.processDelta(delta);
|
await _localAlbumRepository.processDelta(delta);
|
||||||
|
68
mobile/lib/presentation/pages/dev/dev_logger.dart
Normal file
68
mobile/lib/presentation/pages/dev/dev_logger.dart
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.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/log.repository.dart';
|
||||||
|
// ignore: import_rule_isar
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
|
const kDevLoggerTag = 'DEV';
|
||||||
|
|
||||||
|
abstract final class DLog {
|
||||||
|
const DLog();
|
||||||
|
|
||||||
|
static Stream<List<LogMessage>> watchLog() {
|
||||||
|
final db = Isar.getInstance();
|
||||||
|
if (db == null) {
|
||||||
|
debugPrint('Isar is not initialized');
|
||||||
|
return const Stream.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.loggerMessages
|
||||||
|
.filter()
|
||||||
|
.context1EqualTo(kDevLoggerTag)
|
||||||
|
.sortByCreatedAtDesc()
|
||||||
|
.watch(fireImmediately: true)
|
||||||
|
.map((logs) => logs.map((log) => log.toDto()).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearLog() {
|
||||||
|
final db = Isar.getInstance();
|
||||||
|
if (db == null) {
|
||||||
|
debugPrint('Isar is not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.writeTxnSync(() {
|
||||||
|
db.loggerMessages.filter().context1EqualTo(kDevLoggerTag).deleteAllSync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log(String message, [Object? error, StackTrace? stackTrace]) {
|
||||||
|
debugPrint('[$kDevLoggerTag] [${DateTime.now()}] $message');
|
||||||
|
if (error != null) {
|
||||||
|
debugPrint('Error: $error');
|
||||||
|
}
|
||||||
|
if (stackTrace != null) {
|
||||||
|
debugPrint('StackTrace: $stackTrace');
|
||||||
|
}
|
||||||
|
|
||||||
|
final isar = Isar.getInstance();
|
||||||
|
if (isar == null) {
|
||||||
|
debugPrint('Isar is not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final record = LogMessage(
|
||||||
|
message: message,
|
||||||
|
level: LogLevel.info,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
logger: kDevLoggerTag,
|
||||||
|
error: error?.toString(),
|
||||||
|
stack: stackTrace?.toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
unawaited(IsarLogRepository(isar).insert(record));
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,15 @@
|
|||||||
|
// ignore_for_file: avoid-local-functions
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart' hide Column;
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
@ -60,18 +66,26 @@ class FeatInDevPage extends StatelessWidget {
|
|||||||
title: const Text('Features in Development'),
|
title: const Text('Features in Development'),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: Column(
|
||||||
itemBuilder: (_, index) {
|
children: [
|
||||||
final feat = _features[index];
|
Flexible(
|
||||||
return Consumer(
|
child: ListView.builder(
|
||||||
builder: (ctx, ref, _) => ListTile(
|
itemBuilder: (_, index) {
|
||||||
title: Text(feat.name),
|
final feat = _features[index];
|
||||||
trailing: Icon(feat.icon),
|
return Consumer(
|
||||||
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
builder: (ctx, ref, _) => ListTile(
|
||||||
|
title: Text(feat.name),
|
||||||
|
trailing: Icon(feat.icon),
|
||||||
|
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: _features.length,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
const Divider(height: 0),
|
||||||
itemCount: _features.length,
|
const Flexible(child: _DevLogs()),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -88,3 +102,66 @@ class _Feature {
|
|||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Future<void> Function(BuildContext, WidgetRef _) onTap;
|
final Future<void> Function(BuildContext, WidgetRef _) onTap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore: prefer-single-widget-per-file
|
||||||
|
class _DevLogs extends StatelessWidget {
|
||||||
|
const _DevLogs();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: DLog.clearLog,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.delete_outline_rounded,
|
||||||
|
size: 20.0,
|
||||||
|
color: context.primaryColor,
|
||||||
|
semanticLabel: "Clear logs",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: StreamBuilder(
|
||||||
|
initialData: [],
|
||||||
|
stream: DLog.watchLog(),
|
||||||
|
builder: (_, logMessages) {
|
||||||
|
return ListView.separated(
|
||||||
|
itemBuilder: (ctx, index) {
|
||||||
|
// ignore: avoid-unsafe-collection-methods
|
||||||
|
final logMessage = logMessages.data![index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(
|
||||||
|
logMessage.message,
|
||||||
|
style: TextStyle(
|
||||||
|
color: ctx.colorScheme.onSurface,
|
||||||
|
fontSize: 14.0,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
"at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ctx.colorScheme.onSurfaceSecondary,
|
||||||
|
fontSize: 12.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
tileColor: Colors.transparent,
|
||||||
|
minLeadingWidth: 10,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, index) {
|
||||||
|
return const Divider(height: 0);
|
||||||
|
},
|
||||||
|
itemCount: logMessages.data?.length ?? 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user