From d40dc0617147f5ac9709ec4ffb0a1d8cb49d48cb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sun, 1 Sep 2024 22:53:24 +0200 Subject: [PATCH] [mob][photos] MVP logs working in isolate --- .../core/error-reporting/isolate_logging.dart | 59 +++++++++++++++++++ .../core/error-reporting/super_logging.dart | 8 ++- .../face_clustering_service.dart | 7 ++- .../machine_learning/ml_computer.dart | 31 +++++++++- .../machine_learning/ml_indexing_isolate.dart | 5 +- .../ui/settings/ml/ml_user_dev_screen.dart | 11 ++++ 6 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 mobile/lib/core/error-reporting/isolate_logging.dart diff --git a/mobile/lib/core/error-reporting/isolate_logging.dart b/mobile/lib/core/error-reporting/isolate_logging.dart new file mode 100644 index 0000000000..0d686178f8 --- /dev/null +++ b/mobile/lib/core/error-reporting/isolate_logging.dart @@ -0,0 +1,59 @@ +import "dart:collection" show Queue; +import "dart:convert" show jsonEncode, jsonDecode; + +import "package:logging/logging.dart"; +import "package:photos/core/error-reporting/super_logging.dart"; + +class IsolateLogString { + final String logString; + final Object? error; + + IsolateLogString(this.logString, this.error); + + String toJsonString() => jsonEncode({ + 'logString': logString, + 'error': error, + }); + + static IsolateLogString fromJsonString(String jsonString) { + final json = jsonDecode(jsonString); + return IsolateLogString( + json['logString'] as String, + json['error'], + ); + } +} + +class IsolateLogger { + final Queue fileQueueEntries = Queue(); + + Future onLogRecordInIsolate(LogRecord rec) async { + final str = "[ISOLATE]" + rec.toPrettyString(); + + // write to stdout + SuperLogging.printLog(str); + + // push to log queue + fileQueueEntries.add(IsolateLogString(str, rec.error != null)); + } + + /// WARNING: only call this from the isolate + Queue getLogStringsAndClear() { + if (fileQueueEntries.isEmpty) return Queue(); + final result = Queue(); + while (fileQueueEntries.isNotEmpty) { + final entry = fileQueueEntries.removeFirst(); + result.add(entry.toJsonString()); + } + return result; + } + + /// WARNING: only call this from the main thread + static void handLogStringsToMainLogger(Queue logs) { + while (logs.isNotEmpty) { + final logString = logs.removeFirst(); + final log = IsolateLogString.fromJsonString(logString); + SuperLogging.saveLogString(log.logString, log.error); + } + } +} diff --git a/mobile/lib/core/error-reporting/super_logging.dart b/mobile/lib/core/error-reporting/super_logging.dart index 2a677b3fd3..f146b1b14c 100644 --- a/mobile/lib/core/error-reporting/super_logging.dart +++ b/mobile/lib/core/error-reporting/super_logging.dart @@ -270,6 +270,10 @@ class SuperLogging { // write to stdout printLog(str); + saveLogString(str, rec.error); + } + + static void saveLogString(String str, Object? error) { // push to log queue if (fileIsEnabled) { fileQueueEntries.add(str + '\n'); @@ -279,8 +283,8 @@ class SuperLogging { } // add error to sentry queue - if (sentryIsEnabled && rec.error != null) { - _sendErrorToSentry(rec.error!, null).ignore(); + if (sentryIsEnabled && error != null) { + _sendErrorToSentry(error, null).ignore(); } } diff --git a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart index 66fa306dcd..fefe2910d2 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart @@ -4,7 +4,7 @@ import "dart:isolate"; import "dart:typed_data" show Uint8List; import "package:computer/computer.dart"; -import "package:flutter/foundation.dart" show kDebugMode; +import "package:flutter/foundation.dart" show debugPrint, kDebugMode; import "package:logging/logging.dart"; import "package:ml_linalg/dtype.dart"; import "package:ml_linalg/vector.dart"; @@ -120,7 +120,10 @@ class FaceClusteringService { /// The main execution function of the isolate. static void _isolateMain(SendPort mainSendPort) async { Logger.root.level = kDebugMode ? Level.ALL : Level.INFO; - Logger.root.onRecord.listen(SuperLogging.onLogRecord); + // TODO:lau move to right isolate logging + Logger.root.onRecord.listen((LogRecord rec) { + debugPrint('[MLIsolate] ${rec.toPrettyString()}'); + }); final receivePort = ReceivePort(); mainSendPort.send(receivePort.sendPort); diff --git a/mobile/lib/services/machine_learning/ml_computer.dart b/mobile/lib/services/machine_learning/ml_computer.dart index 78743bbd84..f027208758 100644 --- a/mobile/lib/services/machine_learning/ml_computer.dart +++ b/mobile/lib/services/machine_learning/ml_computer.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import "dart:collection" show Queue; import "dart:io" show File; import 'dart:isolate'; import 'dart:typed_data' show Uint8List; @@ -6,7 +7,7 @@ import 'dart:typed_data' show Uint8List; import "package:dart_ui_isolate/dart_ui_isolate.dart"; import "package:flutter/foundation.dart" show kDebugMode; import "package:logging/logging.dart"; -import "package:photos/core/error-reporting/super_logging.dart"; +import "package:photos/core/error-reporting/isolate_logging.dart"; import "package:photos/models/ml/face/box.dart"; import "package:photos/services/machine_learning/ml_model.dart"; import "package:photos/services/machine_learning/semantic_search/clip/clip_text_encoder.dart"; @@ -20,6 +21,7 @@ enum MLComputerOperation { loadModel, initializeClipTokenizer, runClipText, + testLogging, } class MLComputer { @@ -62,7 +64,8 @@ class MLComputer { @pragma('vm:entry-point') static void _isolateMain(SendPort mainSendPort) async { Logger.root.level = kDebugMode ? Level.ALL : Level.INFO; - Logger.root.onRecord.listen(SuperLogging.onLogRecord); + final IsolateLogger isolateLogger = IsolateLogger(); + Logger.root.onRecord.listen(isolateLogger.onLogRecordInIsolate); final receivePort = ReceivePort(); mainSendPort.send(receivePort.sendPort); @@ -106,6 +109,14 @@ class MLComputer { final textEmbedding = await ClipTextEncoder.predict(args); sendPort.send(List.from(textEmbedding, growable: false)); break; + case MLComputerOperation.testLogging: + final logger = Logger('XXX MLComputerTestLogging'); + logger.info("XXX logging from isolate is working!!!"); + final Queue logStrings = + isolateLogger.getLogStringsAndClear(); + final test = [List.from(logStrings)]; + sendPort.send(test); + break; } } catch (e, stackTrace) { sendPort @@ -221,4 +232,20 @@ class MLComputer { } }); } + + Future testLogging() async { + try { + final test = await _runInIsolate( + ( + MLComputerOperation.testLogging, + {}, + ), + ) as List>; + IsolateLogger.handLogStringsToMainLogger(Queue.from(test[0])); + return; + } catch (e, s) { + _logger.severe("XXX Could not test logging in isolate", e, s); + rethrow; + } + } } diff --git a/mobile/lib/services/machine_learning/ml_indexing_isolate.dart b/mobile/lib/services/machine_learning/ml_indexing_isolate.dart index 66772dd40b..996a766d64 100644 --- a/mobile/lib/services/machine_learning/ml_indexing_isolate.dart +++ b/mobile/lib/services/machine_learning/ml_indexing_isolate.dart @@ -67,7 +67,10 @@ class MLIndexingIsolate { @pragma('vm:entry-point') static void _isolateMain(SendPort mainSendPort) async { Logger.root.level = kDebugMode ? Level.ALL : Level.INFO; - Logger.root.onRecord.listen(SuperLogging.onLogRecord); + // TODO:lau move to right isolate logging + Logger.root.onRecord.listen((LogRecord rec) { + debugPrint('[MLIsolate] ${rec.toPrettyString()}'); + }); final receivePort = ReceivePort(); mainSendPort.send(receivePort.sendPort); receivePort.listen((message) async { diff --git a/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart b/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart index 8d70cee21b..7c4d808592 100644 --- a/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart +++ b/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart @@ -3,6 +3,7 @@ import "package:photos/core/event_bus.dart"; import "package:photos/db/ml/clip_db.dart"; import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; +import "package:photos/services/machine_learning/ml_computer.dart"; import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/components/buttons/button_widget.dart"; @@ -55,6 +56,16 @@ class MLUserDeveloperOptions extends StatelessWidget { await deleteAllLocalML(context); }, ), + // TODO:lau remove below code + const SizedBox(height: 24), + ButtonWidget( + buttonType: ButtonType.neutral, + labelText: "Log something in isolate", + onTap: () async { + await MLComputer.instance.testLogging(); + showShortToast(context, "Done"); + }, + ), const SafeArea( child: SizedBox( height: 12,