7.0 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	
			7.0 KiB
		
	
	
	
	
	
	
	
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 
fromandtoURLs 
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:
{
  "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:
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 
sendBeaconfor reliable delivery on page unload 
Performance Optimizations
- Uses 
requestIdleCallbackwhen available - Non-blocking: doesn't interfere with UI interactions
 - Data size limit: 5 KB per record (truncated if exceeded)
 
🧪 Testing
Manual Testing
- 
Check Network Tab:
- Open DevTools → Network
 - Filter by 
/api/log - Perform actions (search, navigate, toggle theme)
 - Verify POST requests with correct payloads
 
 - 
Test Offline Behavior:
- Open DevTools → Network
 - Set throttling to "Offline"
 - Perform actions
 - Re-enable network
 - Verify queued logs are sent
 
 - 
Test with curl:
 
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:
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:
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 
LogRecordor array ofLogRecord[] 
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
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:
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.enabledistrue - Verify 
/api/logendpoint 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
 sendBeaconis used as fallback (best effort)- Some logs may be lost on hard refresh or forced close
 
📝 License
Same as ObsiViewer project.