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.