271 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # 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.
 |