Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3e5fc21
feat: add logger service with memory buffer and Riverpod provider
BraCR10 Jan 7, 2026
96bf7b1
fix : update config with logger constants
BraCR10 Jan 7, 2026
f31cdb6
feat : connect logs screen to logger service with real-time display
BraCR10 Jan 7, 2026
6b59aee
feat: sync logging toggle with MemoryLogOutput
BraCR10 Jan 7, 2026
062eef6
refactor: migrate files to use logger singleton
BraCR10 Jan 7, 2026
f5ed78b
docs: updating docs
BraCR10 Jan 7, 2026
e75a6d3
Merge branch 'feat/logging-phase-1' into feat/logging-phase-2
BraCR10 Jan 7, 2026
ae88c0c
Merge branch 'feat/logging-phase-1' into feat/logging-phase-2
BraCR10 Jan 7, 2026
c204c95
Merge remote-tracking branch 'origin/main' into feat/logging-phase-2
BraCR10 Jan 9, 2026
d040ffe
Migrate NostrService to use logger singleton
BraCR10 Jan 12, 2026
64fc240
Migrate mostro_storage to use logger singleton
BraCR10 Jan 12, 2026
c16c74a
Update docs for Phase 3 completion
BraCR10 Jan 12, 2026
5b7dd99
Enable isolate log receiver for background services
BraCR10 Jan 12, 2026
389b4be
Update docs for Phase 4 completion
BraCR10 Jan 12, 2026
cfd4fe7
Add bottom padding to logs list to prevent overlap with system buttons
BraCR10 Jan 13, 2026
80c6415
Localize relative time strings using timeAgoWithLocale extension
BraCR10 Jan 13, 2026
04c4482
Increase bottom padding in logs list to prevent overlap with system n…
BraCR10 Jan 13, 2026
6b8f801
Merge branch 'feat/logging-phase-2' into feat/logging-phases-3&4
BraCR10 Jan 13, 2026
d36ff88
Merge branch 'main' into feat/logging-phases-3&4
BraCR10 Jan 13, 2026
8382b55
Fix typo in LOGGING_IMPLEMENTATION.md
BraCR10 Jan 13, 2026
2e03a1e
Configure logger with IsolateLogOutput in mobile background isolate
BraCR10 Jan 13, 2026
8e2e557
Print background isolate logs to console only in debug mode
BraCR10 Jan 13, 2026
3ea02f5
Merge remote-tracking branch 'origin/feat/logging-phases-3&4' into fe…
BraCR10 Jan 13, 2026
30546b7
Add fallback logging for errors before logger initialization
BraCR10 Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions docs/LOGGING_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ Implementation of a comprehensive logging system for MostroP2P mobile app with i
- Connect UI to actual logger
- Test with 2 files: RelaysNotifier, SubscriptionManager

### Phase 3: Core Services Migration (Current)
### Phase 3: Core Services Migration (Completed)
- NostrService
- MostroService
- DeepLinkService
- 2 additional core files
- mostro_storage
- AbstractMostroNotifier + subclasses (AddOrderNotifier, OrderNotifier)

### Phase 4: Background Services
- Mobile and desktop background service
- Isolate logging
### Phase 4: Background Services (Completed)
- Mobile background service with isolate logging
- Desktop background service with isolate logging
- Isolate log receiver initialized in main.dart

### Phase 5: File Export & Persistence
- Auto-save to storage
Expand Down Expand Up @@ -204,6 +206,6 @@ void backgroundMain(SendPort sendPort) async {

---

**Version**: 3
**Status**: Phase 2 - Ready
**Last Updated**: 2026-01-06
**Version**: 5
**Status**: Phase 4 - Completed
**Last Updated**: 2026-01-12
20 changes: 19 additions & 1 deletion lib/background/background.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
Expand All @@ -8,13 +9,16 @@ import 'package:mostro_mobile/data/repositories/event_storage.dart';
import 'package:mostro_mobile/features/settings/settings.dart';
import 'package:mostro_mobile/features/notifications/services/background_notification_service.dart' as notification_service;
import 'package:mostro_mobile/services/nostr_service.dart';
import 'package:mostro_mobile/services/logger_service.dart' as logger_service;
import 'package:mostro_mobile/shared/providers/mostro_database_provider.dart';

bool isAppForeground = true;
String currentLanguage = 'en';

@pragma('vm:entry-point')
Future<void> serviceMain(ServiceInstance service) async {
SendPort? loggerSendPort;
Logger? logger;

final Map<String, Map<String, dynamic>> activeSubscriptions = {};
final nostrService = NostrService();
Expand All @@ -31,6 +35,14 @@ Future<void> serviceMain(ServiceInstance service) async {
final settingsMap = data['settings'];
if (settingsMap == null) return;

loggerSendPort = data['loggerSendPort'] as SendPort?;

logger = Logger(
printer: logger_service.SimplePrinter(),
output: logger_service.IsolateLogOutput(loggerSendPort),
level: Level.debug,
);

final settings = Settings.fromJson(settingsMap);
currentLanguage = settings.selectedLanguage ?? PlatformDispatcher.instance.locale.languageCode;
await nostrService.init(settings);
Expand Down Expand Up @@ -74,7 +86,13 @@ Future<void> serviceMain(ServiceInstance service) async {
}
await notification_service.retryNotification(event);
} catch (e) {
Logger().e('Error processing event', error: e);
final currentLogger = logger;
if (currentLogger != null) {
currentLogger.e('Error processing event', error: e);
} else {
// ignore: avoid_print
print('ERROR (logger not ready): Error processing event: $e');
}
}
});
});
Expand Down
9 changes: 8 additions & 1 deletion lib/background/desktop_background_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:logger/logger.dart';
import 'package:mostro_mobile/data/models/nostr_filter.dart';
import 'package:mostro_mobile/features/settings/settings.dart';
import 'package:mostro_mobile/services/nostr_service.dart';
import 'package:mostro_mobile/services/logger_service.dart' as logger_service;
import 'abstract_background_service.dart';

class DesktopBackgroundService implements BackgroundService {
Expand All @@ -22,13 +23,19 @@ class DesktopBackgroundService implements BackgroundService {
final isolateReceivePort = ReceivePort();
final mainSendPort = args[0] as SendPort;
final token = args[1] as RootIsolateToken;
final loggerSendPort = args.length > 2 ? args[2] as SendPort? : null;

mainSendPort.send(isolateReceivePort.sendPort);

BackgroundIsolateBinaryMessenger.ensureInitialized(token);

final logger = Logger(
printer: logger_service.SimplePrinter(),
output: logger_service.IsolateLogOutput(loggerSendPort),
level: Level.debug,
);

final nostrService = NostrService();
final logger = Logger();
bool isAppForeground = true;

isolateReceivePort.listen((message) async {
Expand Down
19 changes: 10 additions & 9 deletions lib/background/mobile_background_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import 'dart:async';

import 'package:dart_nostr/nostr/model/request/filter.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:logger/logger.dart';
import 'package:mostro_mobile/background/background.dart';
import 'package:mostro_mobile/features/settings/settings.dart';
import 'package:mostro_mobile/services/logger_service.dart' as logger_service;
import 'abstract_background_service.dart';

class MobileBackgroundService implements BackgroundService {
Expand All @@ -16,7 +16,6 @@ class MobileBackgroundService implements BackgroundService {

final _subscriptions = <String, Map<String, dynamic>>{};
bool _isRunning = false;
final _logger = Logger();
bool _serviceReady = false;
final List<Function> _pendingOperations = [];

Expand Down Expand Up @@ -44,19 +43,20 @@ class MobileBackgroundService implements BackgroundService {
_isRunning = true;
service.invoke('start', {
'settings': _settings.toJson(),
'loggerSendPort': logger_service.isolateLogSenderPort,
});
_logger.d(
logger_service.logger.d(
'Service started with settings: ${_settings.toJson()}',
);
});

service.on('on-stop').listen((event) {
_isRunning = false;
_logger.i('Service stopped');
logger_service.logger.i('Service stopped');
});

service.on('service-ready').listen((data) {
_logger.i("Service confirmed it's ready");
logger_service.logger.i("Service confirmed it's ready");
_serviceReady = true;
_processPendingOperations();
});
Expand All @@ -68,7 +68,7 @@ class MobileBackgroundService implements BackgroundService {
_subscriptions[subId] = {'filters': filters};

_executeWhenReady(() {
_logger.i("Sending subscription to service");
logger_service.logger.i("Sending subscription to service");
service.invoke('create-subscription', {
'id': subId,
'filters': filters.map((f) => f.toMap()).toList(),
Expand Down Expand Up @@ -127,7 +127,7 @@ class MobileBackgroundService implements BackgroundService {
try {
await _startService();
} catch (e) {
_logger.e('Error starting service: $e');
logger_service.logger.e('Error starting service: $e');
// Retry with a delay if needed
await Future.delayed(Duration(seconds: 1));
await _startService();
Expand All @@ -137,7 +137,7 @@ class MobileBackgroundService implements BackgroundService {
}

Future<void> _startService() async {
_logger.i("Starting service");
logger_service.logger.i("Starting service");
await service.startService();
_serviceReady = false; // Reset ready state when starting

Expand All @@ -152,9 +152,10 @@ class MobileBackgroundService implements BackgroundService {
await Future.delayed(const Duration(milliseconds: 50));
}

_logger.i("Service running, sending settings");
logger_service.logger.i("Service running, sending settings");
service.invoke('start', {
'settings': _settings.toJson(),
'loggerSendPort': logger_service.isolateLogSenderPort,
});
}

Expand Down
14 changes: 7 additions & 7 deletions lib/data/repositories/mostro_storage.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'package:logger/logger.dart';
import 'package:mostro_mobile/services/logger_service.dart';
import 'package:mostro_mobile/data/models/payload.dart';
import 'package:sembast/sembast.dart';
import 'package:mostro_mobile/data/models/mostro_message.dart';
import 'package:mostro_mobile/data/repositories/base_storage.dart';

class MostroStorage extends BaseStorage<MostroMessage> {
final Logger _logger = Logger();


MostroStorage({required Database db})
: super(db, stringMapStoreFactory.store('orders'));
Expand All @@ -21,11 +21,11 @@ class MostroStorage extends BaseStorage<MostroMessage> {
dbMap['timestamp'] = message.timestamp;

await store.record(id).put(db, dbMap);
_logger.i(
logger.i(
'Saved message of type ${message.action} with order id ${message.id}',
);
} catch (e, stack) {
_logger.e(
logger.e(
'addMessage failed for $id',
error: e,
stackTrace: stack,
Expand All @@ -39,7 +39,7 @@ class MostroStorage extends BaseStorage<MostroMessage> {
try {
return await getAll();
} catch (e, stack) {
_logger.e('getAllMessages failed', error: e, stackTrace: stack);
logger.e('getAllMessages failed', error: e, stackTrace: stack);
return <MostroMessage>[];
}
}
Expand All @@ -48,9 +48,9 @@ class MostroStorage extends BaseStorage<MostroMessage> {
Future<void> deleteAllMessages() async {
try {
await deleteAll();
_logger.i('All messages deleted');
logger.i('All messages deleted');
} catch (e, stack) {
_logger.e('deleteAllMessages failed', error: e, stackTrace: stack);
logger.e('deleteAllMessages failed', error: e, stackTrace: stack);
rethrow;
}
}
Expand Down
21 changes: 10 additions & 11 deletions lib/features/order/notfiers/abstract_mostro_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import 'package:mostro_mobile/features/chat/providers/chat_room_providers.dart';
import 'package:mostro_mobile/features/notifications/providers/notifications_provider.dart';
import 'package:mostro_mobile/features/notifications/utils/notification_data_extractor.dart';
import 'package:mostro_mobile/features/settings/settings_provider.dart';
import 'package:logger/logger.dart';
import 'package:mostro_mobile/services/logger_service.dart';

class AbstractMostroNotifier extends StateNotifier<OrderState> {
final String orderId;
final Ref ref;
final logger = Logger();

late Session session;

Expand Down Expand Up @@ -504,17 +503,17 @@ class AbstractMostroNotifier extends StateNotifier<OrderState> {
_sessionTimeouts[orderId] = Timer(const Duration(seconds: 10), () {
try {
ref.read(sessionNotifierProvider.notifier).deleteSession(orderId);
Logger().i('Session cleaned up after 10s timeout: $orderId');
logger.i('Session cleaned up after 10s timeout: $orderId');

// Show timeout message to user and navigate to order book
_showTimeoutNotificationAndNavigate(ref);
} catch (e) {
Logger().e('Failed to cleanup session: $orderId', error: e);
logger.e('Failed to cleanup session: $orderId', error: e);
}
_sessionTimeouts.remove(orderId);
});

Logger().i('Started 10s timeout timer for order: $orderId');
logger.i('Started 10s timeout timer for order: $orderId');
}

/// Shows timeout notification and navigates to order book
Expand All @@ -528,7 +527,7 @@ class AbstractMostroNotifier extends StateNotifier<OrderState> {
final navProvider = ref.read(navigationProvider.notifier);
navProvider.go('/');
} catch (e) {
Logger().e('Failed to show timeout notification or navigate', error: e);
logger.e('Failed to show timeout notification or navigate', error: e);
}
}

Expand All @@ -541,17 +540,17 @@ class AbstractMostroNotifier extends StateNotifier<OrderState> {
_sessionTimeouts[key] = Timer(const Duration(seconds: 10), () {
try {
ref.read(sessionNotifierProvider.notifier).deleteSessionByRequestId(requestId);
Logger().i('Session cleaned up after 10s timeout for requestId: $requestId');
logger.i('Session cleaned up after 10s timeout for requestId: $requestId');

// Show timeout message to user and navigate to order book
_showTimeoutNotificationAndNavigate(ref);
} catch (e) {
Logger().e('Failed to cleanup session for requestId: $requestId', error: e);
logger.e('Failed to cleanup session for requestId: $requestId', error: e);
}
_sessionTimeouts.remove(key);
});

Logger().i('Started 10s timeout timer for requestId: $requestId');
logger.i('Started 10s timeout timer for requestId: $requestId');
}

/// Cancels the timeout timer for a specific orderId
Expand All @@ -560,7 +559,7 @@ class AbstractMostroNotifier extends StateNotifier<OrderState> {
if (timer != null) {
timer.cancel();
_sessionTimeouts.remove(orderId);
Logger().i('Cancelled 10s timeout timer for order: $orderId - Mostro responded');
logger.i('Cancelled 10s timeout timer for order: $orderId - Mostro responded');
}
}

Expand All @@ -571,7 +570,7 @@ class AbstractMostroNotifier extends StateNotifier<OrderState> {
if (timer != null) {
timer.cancel();
_sessionTimeouts.remove(key);
Logger().i('Cancelled 10s timeout timer for requestId: $requestId - Mostro responded');
logger.i('Cancelled 10s timeout timer for requestId: $requestId - Mostro responded');
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/features/order/notfiers/add_order_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:mostro_mobile/shared/providers.dart';
import 'package:mostro_mobile/features/order/notfiers/abstract_mostro_notifier.dart';
import 'package:mostro_mobile/features/order/providers/order_notifier_provider.dart';
import 'package:mostro_mobile/features/order/models/order_state.dart';
import 'package:mostro_mobile/services/logger_service.dart';
import 'package:mostro_mobile/services/mostro_service.dart';

class AddOrderNotifier extends AbstractMostroNotifier {
Expand Down
1 change: 1 addition & 0 deletions lib/features/order/notfiers/order_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:mostro_mobile/features/order/models/order_state.dart';
import 'package:mostro_mobile/features/notifications/providers/notifications_provider.dart';
import 'package:mostro_mobile/shared/providers.dart';
import 'package:mostro_mobile/features/order/notfiers/abstract_mostro_notifier.dart';
import 'package:mostro_mobile/services/logger_service.dart';
import 'package:mostro_mobile/services/mostro_service.dart';

class OrderNotifier extends AbstractMostroNotifier {
Expand Down
3 changes: 3 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import 'package:mostro_mobile/shared/providers/background_service_provider.dart'
import 'package:mostro_mobile/shared/providers/providers.dart';
import 'package:mostro_mobile/shared/utils/biometrics_helper.dart';
import 'package:mostro_mobile/shared/utils/notification_permission_helper.dart';
import 'package:mostro_mobile/services/logger_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timeago/timeago.dart' as timeago;

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

initIsolateLogReceiver();

await requestNotificationPermissionIfNeeded();

final biometricsHelper = BiometricsHelper();
Expand Down
Loading