Skip to content

Commit 71950e9

Browse files
authored
Docs: Adding In-App Logging System documentation (#393)
* docs: Adding In-App Logging System documentation * Update LOGGING_IMPLEMENTATION.md
1 parent 51cc91f commit 71950e9

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

docs/LOGGING_IMPLEMENTATION.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# In-App Logging System Implementation
2+
3+
## Overview
4+
5+
Implementation of a comprehensive logging system for MostroP2P mobile app with in-memory capture, background isolate support, privacy sanitization, and export capabilities.
6+
7+
## Design Approach
8+
9+
### Why This Architecture?
10+
11+
This implementation uses a **singleton logger pattern with centralized output management** rather than individual logger instances per service. While this approach requires updating all logging calls across the codebase, it provides critical advantages:
12+
13+
**1. Guaranteed Log Capture**
14+
- Single source of truth ensures NO logs are missed
15+
- All application logs automatically captured in memory
16+
- Background isolates seamlessly integrated via SendPort
17+
18+
**2. Consistent Privacy Protection**
19+
- Centralized sanitization means sensitive data CANNOT leak
20+
- Single `cleanMessage()` function applies to all logs uniformly
21+
- No risk of individual services bypassing sanitization
22+
23+
**3. Memory Safety**
24+
- Centralized buffer with strict size limits prevents OOM issues
25+
- Batch deletion strategy maintains performance
26+
- Predictable memory footprint regardless of log volume
27+
28+
**4. Development Experience**
29+
- Simple API: `logger.i()`, `logger.e()`, `logger.d()`
30+
- No configuration needed per service
31+
- Stack trace extraction for debugging
32+
- Maintains normal console output while providing a minimized log view for the UI
33+
34+
### Trade-offs
35+
36+
**Cons of Singleton Approach**:
37+
- Requires updating ~30+ files to replace `print()` calls
38+
- One-time migration effort across codebase
39+
40+
## Architecture
41+
42+
```
43+
Main Isolate:
44+
logger (singleton) → MemoryLogOutput → UI (LogsScreen)
45+
↘ ConsoleOutput
46+
47+
Background Isolate:
48+
logger → IsolateLogOutput → SendPort → Main Isolate ReceivePort → MemoryLogOutput
49+
```
50+
51+
## Critical Implementation Details
52+
53+
### 1. Logger Usage Pattern
54+
55+
**IMPORTANT**: Always use the singleton `logger` instance, never instantiate `Logger()` directly.
56+
57+
```dart
58+
// CORRECT
59+
import 'package:mostro_mobile/services/logger_service.dart';
60+
61+
logger.i('Info message');
62+
logger.e('Error occurred', error: e, stackTrace: stack);
63+
64+
// WRONG - Do not instantiate directly
65+
final myLogger = Logger();
66+
```
67+
68+
### 2. Configuration (`lib/core/config.dart`)
69+
70+
71+
```dart
72+
// Logger configuration
73+
static const int logMaxEntries = 1000; // Maximum logs in memory
74+
static const int logBatchDeleteSize = 100; // Batch delete when limit exceeded
75+
static bool fullLogsInfo = true; // true = PrettyPrinter, false = SimplePrinter
76+
```
77+
78+
**Buffer Management**:
79+
- When buffer exceeds `logMaxEntries`, oldest `logBatchDeleteSize` entries are deleted
80+
- FIFO queue ensures recent logs are preserved
81+
- Prevents memory exhaustion from log flooding
82+
83+
### 3. Background Isolate Integration
84+
85+
Background services MUST initialize logging with `IsolateLogOutput`:
86+
87+
```dart
88+
@pragma('vm:entry-point')
89+
void backgroundServiceEntryPoint(SendPort sendPort) async {
90+
// Get log sender from main isolate
91+
final logSender = isolateLogSenderPort;
92+
93+
// Initialize logger for this isolate
94+
Logger.level = Level.debug;
95+
final logger = Logger(
96+
printer: SimplePrinter(),
97+
output: IsolateLogOutput(logSender),
98+
);
99+
100+
// Now logs from this isolate appear in main app
101+
logger.i('Background service started');
102+
}
103+
```
104+
105+
**Main isolate setup** (in `main.dart`):
106+
```dart
107+
void main() async {
108+
WidgetsFlutterBinding.ensureInitialized();
109+
110+
// Initialize log receiver BEFORE starting background services
111+
initIsolateLogReceiver();
112+
113+
// ... rest of initialization
114+
}
115+
```
116+
117+
### 4. Privacy Sanitization
118+
119+
The `cleanMessage()` function automatically redacts:
120+
121+
| Pattern | Replacement | Example |
122+
|---------|-------------|---------|
123+
| `nsec[0-9a-z]+` | `[PRIVATE_KEY]` | `nsec1abc...``[PRIVATE_KEY]` |
124+
| `"privateKey":"..."` | `"privateKey":"[REDACTED]"` | JSON field redacted |
125+
| `"mnemonic":"..."` | `"mnemonic":"[REDACTED]"` | Seed phrase redacted |
126+
| ANSI codes | Removed | Color/formatting stripped |
127+
| Emojis | Removed | All emoji ranges |
128+
129+
### 5. File Storage & Permissions
130+
131+
**Android/iOS**:
132+
```dart
133+
// External storage (requires permissions in future)
134+
final directory = await getExternalStorageDirectory();
135+
final logsDir = Directory('${directory.path}/MostroLogs');
136+
```
137+
138+
**PERMISSIONS REQUIRED** (not yet implemented):
139+
- Android: `WRITE_EXTERNAL_STORAGE` permission
140+
- iOS: Automatic with app sandbox
141+
142+
**Desktop/Web**:
143+
```dart
144+
// Application documents (no special permissions)
145+
final directory = await getApplicationDocumentsDirectory();
146+
```
147+
148+
**Storage location display**:
149+
- UI shows storage path to user
150+
- Configurable via `Settings.customLogStorageDirectory`
151+
152+
## Migration Example
153+
154+
### Service Integration
155+
156+
```dart
157+
// lib/services/nostr_service.dart
158+
159+
import 'package:mostro_mobile/services/logger_service.dart';
160+
161+
class NostrService {
162+
Future<void> connect() async {
163+
logger.i('Connecting to Nostr relays');
164+
165+
try {
166+
await _connectToRelays();
167+
logger.i('Successfully connected to ${relays.length} relays');
168+
} catch (e, stack) {
169+
logger.e('Failed to connect to relays', error: e, stackTrace: stack);
170+
rethrow;
171+
}
172+
}
173+
}
174+
```
175+
176+
### Background Service
177+
178+
```dart
179+
// lib/background/mobile_background_service.dart
180+
181+
@pragma('vm:entry-point')
182+
void backgroundMain(SendPort sendPort) async {
183+
final logSender = isolateLogSenderPort;
184+
final logger = Logger(
185+
printer: SimplePrinter(),
186+
output: IsolateLogOutput(logSender),
187+
);
188+
189+
logger.i('Background service started');
190+
191+
try {
192+
await performBackgroundTask();
193+
} catch (e, stack) {
194+
logger.e('Background task failed', error: e, stackTrace: stack);
195+
}
196+
}
197+
```
198+
199+
200+
---
201+
202+
**Version**: 1.0
203+
**Status**: Phase 1 - Planning Complete

lib/core/config.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ class Config {
4242
// Notification configuration
4343
static String notificationChannelId = 'mostro_mobile';
4444
static int notificationId = 38383;
45+
46+
// Logger configuration
47+
static const int logMaxEntries = 1000;
48+
static const int logBatchDeleteSize = 100;
49+
static bool fullLogsInfo = true; // false = simple logs, true = PrettyPrinter
4550
}

0 commit comments

Comments
 (0)