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