# ObsiViewer - Frontend Logging System ## ๐Ÿ“‹ Overview ObsiViewer implements a robust frontend โ†’ backend logging system that tracks user interactions and application lifecycle events. All logs are sent to the `/api/log` endpoint with structured JSON payloads. ## ๐ŸŽฏ Tracked Events ### Application Lifecycle - **APP_START**: Emitted when the application initializes - **APP_STOP**: Emitted before page unload or app termination - **VISIBILITY_CHANGE**: Emitted when page visibility changes (background/foreground) ### Navigation - **NAVIGATE**: Emitted on every route change with `from` and `to` URLs ### User Actions - **SEARCH_EXECUTED**: Emitted when user performs a search - **BOOKMARKS_OPEN**: Emitted when bookmarks view is opened - **BOOKMARKS_MODIFY**: Emitted when bookmarks are added/updated/deleted - **GRAPH_VIEW_OPEN**: Emitted when graph view is opened - **GRAPH_VIEW_CLOSE**: Emitted when graph view is closed - **GRAPH_VIEW_SETTINGS_CHANGE**: Emitted when graph settings are modified - **CALENDAR_SEARCH_EXECUTED**: Emitted when calendar search is performed - **THEME_CHANGE**: Emitted when theme is toggled ## ๐Ÿ“ Log Record Structure Each log record follows this schema: ```json { "ts": "2025-10-05T14:21:33.123Z", "level": "info", "app": "ObsiViewer", "sessionId": "9b2c8f1f-7e2f-4d6f-9f5b-0e3e1c9f7c3a", "userAgent": "Mozilla/5.0...", "context": { "route": "/search?q=test", "vault": "Main", "theme": "dark", "version": "0.0.0" }, "event": "SEARCH_EXECUTED", "data": { "query": "test", "queryLength": 4 } } ``` ### Fields - **ts**: ISO 8601 timestamp - **level**: Log level (`info`, `warn`, `error`) - **app**: Always `"ObsiViewer"` - **sessionId**: UUID v4 persisted in sessionStorage - **userAgent**: Browser user agent string - **context**: Automatic context (route, theme, vault, version) - **event**: Event type (see list above) - **data**: Event-specific metadata (optional) ## โš™๏ธ Configuration Configuration is in `src/core/logging/environment.ts`: ```typescript export const environment = { production: false, appVersion: '0.0.0', logging: { enabled: true, // Enable/disable logging endpoint: '/api/log', // Backend endpoint batchSize: 5, // Batch size (records) debounceMs: 2000, // Debounce delay (ms) maxRetries: 5, // Max retry attempts circuitBreakerThreshold: 5, // Failures before circuit opens circuitBreakerResetMs: 30000, // Circuit breaker reset time (ms) }, }; ``` ## ๐Ÿ”ง Features ### Batching & Debouncing - Logs are batched (default: 5 records or 2 seconds, whichever comes first) - Reduces network overhead and backend load ### Retry with Exponential Backoff - Failed requests are retried up to 5 times - Backoff delays: 500ms, 1s, 2s, 4s, 8s ### Circuit Breaker - After 5 consecutive failures, logging pauses for 30 seconds - Prevents overwhelming the backend during outages ### Offline Support - Logs are queued in memory and localStorage - Automatically flushed when connection is restored - Uses `sendBeacon` for reliable delivery on page unload ### Performance Optimizations - Uses `requestIdleCallback` when available - Non-blocking: doesn't interfere with UI interactions - Data size limit: 5 KB per record (truncated if exceeded) ## ๐Ÿงช Testing ### Manual Testing 1. **Check Network Tab**: - Open DevTools โ†’ Network - Filter by `/api/log` - Perform actions (search, navigate, toggle theme) - Verify POST requests with correct payloads 2. **Test Offline Behavior**: - Open DevTools โ†’ Network - Set throttling to "Offline" - Perform actions - Re-enable network - Verify queued logs are sent 3. **Test with curl**: ```bash curl -X POST http://localhost:4200/api/log \ -H "Content-Type: application/json" \ -d '{ "ts": "2025-10-05T14:21:33.123Z", "level": "info", "app": "ObsiViewer", "sessionId": "test-session", "event": "APP_START", "data": {} }' ``` ### Unit Tests Run tests with: ```bash npm test ``` Tests cover: - Log record construction - Batch and debounce logic - Retry mechanism - Circuit breaker - Router and visibility listeners ### E2E Tests Run E2E tests with: ```bash npm run test:e2e ``` E2E tests verify: - APP_START on page load - NAVIGATE on route changes - SEARCH_EXECUTED on search - GRAPH_VIEW_OPEN on graph view - Offline queue and flush ## ๐Ÿ› ๏ธ Architecture ``` src/core/logging/ โ”œโ”€โ”€ log.model.ts # Types and interfaces โ”œโ”€โ”€ log.service.ts # Main logging service โ”œโ”€โ”€ log.sender.ts # HTTP sender (pure function) โ”œโ”€โ”€ log.router-listener.ts # Router event listener โ”œโ”€โ”€ log.visibility-listener.ts # Visibility event listener โ”œโ”€โ”€ environment.ts # Configuration โ””โ”€โ”€ index.ts # Public API ``` ### Key Components - **LogService**: Singleton service managing queue, batching, retry, and circuit breaker - **sendBatch**: Pure function for HTTP POST to backend - **Router Listener**: Tracks navigation events - **Visibility Listener**: Tracks app lifecycle (visibility, beforeunload, pagehide) ## ๐Ÿ”’ Security & Privacy - **No note content**: Only metadata (paths, titles, counts) is logged - **Data sanitization**: Large objects are truncated - **Safe serialization**: Prevents circular references and functions ## ๐Ÿ“Š Backend Requirements The backend must implement: **Endpoint**: `POST /api/log` **Request**: - Content-Type: `application/json` - Body: Single `LogRecord` or array of `LogRecord[]` **Response**: - Status: `2xx` (any 2xx status indicates success) - Body: `{"ok": true}` (optional, ignored by client) **Error Handling**: - Non-2xx responses trigger retry logic - Network errors trigger retry logic ## ๐Ÿš€ Usage ### Logging Custom Events ```typescript import { LogService } from './core/logging/log.service'; @Component({...}) export class MyComponent { private logService = inject(LogService); onCustomAction(): void { this.logService.log('CUSTOM_EVENT', { action: 'button_click', buttonId: 'submit', }); } } ``` ### Disabling Logging Set `logging.enabled` to `false` in `environment.ts`: ```typescript export const environment = { logging: { enabled: false, // Disable all logging // ... }, }; ``` ## ๐Ÿ“ˆ Monitoring Monitor logs on the backend to: - Track user engagement (searches, navigation patterns) - Identify popular features (graph view, bookmarks) - Detect errors and performance issues - Analyze session duration and activity ## ๐Ÿ› Troubleshooting ### Logs not appearing in Network tab - Check `environment.logging.enabled` is `true` - Verify `/api/log` endpoint exists and returns 2xx - Check browser console for errors ### Circuit breaker opened - Check backend availability - Verify endpoint returns 2xx for valid requests - Check network connectivity ### Logs not sent on page unload - Modern browsers may block async requests on unload - `sendBeacon` is used as fallback (best effort) - Some logs may be lost on hard refresh or forced close ## ๐Ÿ“ License Same as ObsiViewer project.