mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -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/services/store.service.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:logging/logging.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
@ -41,15 +42,20 @@ class DeviceSyncService {
|
||||
try {
|
||||
if (await _nativeSyncApi.shouldFullSync()) {
|
||||
_log.fine("Cannot use partial sync. Performing full sync");
|
||||
DLog.log("Cannot use partial sync. Performing full sync");
|
||||
return await fullSync();
|
||||
}
|
||||
|
||||
final delta = await _nativeSyncApi.getMediaChanges();
|
||||
if (!delta.hasChanges) {
|
||||
_log.fine("No media changes detected. Skipping sync");
|
||||
DLog.log("No media changes detected. Skipping sync");
|
||||
return;
|
||||
}
|
||||
|
||||
DLog.log("Delta updated: ${delta.updates.length}");
|
||||
DLog.log("Delta deleted: ${delta.deletes.length}");
|
||||
|
||||
final deviceAlbums = await _nativeSyncApi.getAlbums();
|
||||
await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums());
|
||||
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 '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: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/infrastructure/db.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'),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemBuilder: (_, index) {
|
||||
final feat = _features[index];
|
||||
return Consumer(
|
||||
builder: (ctx, ref, _) => ListTile(
|
||||
title: Text(feat.name),
|
||||
trailing: Icon(feat.icon),
|
||||
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
||||
body: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (_, index) {
|
||||
final feat = _features[index];
|
||||
return Consumer(
|
||||
builder: (ctx, ref, _) => ListTile(
|
||||
title: Text(feat.name),
|
||||
trailing: Icon(feat.icon),
|
||||
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: _features.length,
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: _features.length,
|
||||
),
|
||||
const Divider(height: 0),
|
||||
const Flexible(child: _DevLogs()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -88,3 +102,66 @@ class _Feature {
|
||||
final IconData icon;
|
||||
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