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.
 |