Admin Console Log Forwarding
Admin Console Log Forwarding Browser console logs from admin users are forwarded in real-time to OpenObserve via a backend proxy, providing unified client+se...
Admin Console Log Forwarding
Browser console logs from admin users are forwarded in real-time to OpenObserve via a backend proxy, providing unified client+server observability.
Why This Exists
Debugging client-side issues requires seeing browser logs alongside server logs. Admin-only forwarding keeps the feature scoped (no regular user data leaves the browser) while providing a unified timeline in OpenObserve.
How It Works
Architecture
Browser (admin only)
logCollector.ts -- patches console.{log,info,warn,error,debug}
clientLogForwarder.ts -- batches every 5s, durable IndexedDB queue
| POST /v1/admin/client-logs (credentials: include, keepalive: true)
FastAPI (admin_client_logs.py)
| Loki-compat push (aiohttp)
OpenObserve :5080 (stream: "client-console", job="client-console")
Playwright E2E runs use the same collector and queue, but when the page is loaded
with #e2e-debug=...&e2e-token=..., clientLogForwarder.ts sends the main queue
only to POST /e2e/client-logs with X-E2E-Debug-Token. It intentionally skips
the admin endpoint in E2E mode so admin test accounts do not produce browser CORS
errors while the test-only log capture path remains active.
Forwarder Lifecycle
| Event | Action |
|---|---|
Login with is_admin: true |
clientLogForwarder.start() |
Session restore with is_admin |
clientLogForwarder.start() |
| Logout | clientLogForwarder.stop() (drains first) |
| Session expiry | clientLogForwarder.stop() |
| E2E debug hash detected | clientLogForwarder.startE2E() |
The is_admin flag comes from /v1/auth/login and /v1/auth/session responses, sourced from the Redis user profile cache. When admin status changes via AdminMethods.make_user_admin() / revoke_admin_privileges(), the cache key is deleted immediately.
Batching
- Flush interval: 5 seconds
- Max batch size: 50 entries
- Max batches per flush cycle: 25
- Durable IndexedDB queue (
openmates_admin_client_log_queue) survives page refreshes
Privacy
- Non-admin users: forwarder never starts. Logs stay in
logCollectorcircular buffers; only sent on explicit issue submission. - Messages sanitized client-side: API keys (
sk-api-*), bearer tokens, and common sensitive fields redacted. - OpenObserve endpoint never exposed to browser – all pushes go through the backend proxy.
Querying in OpenObserve
SELECT _timestamp, user_email, level, message
FROM "client-console"
WHERE _timestamp > NOW() - INTERVAL '1 hour'
ORDER BY _timestamp DESC
Stream labels: job="client-console", level, user_email, server_env, source="browser".
Environment Variables
| Variable | Where | Purpose |
|---|---|---|
OPENOBSERVE_ROOT_EMAIL |
Backend | Basic auth for server-side push |
OPENOBSERVE_ROOT_PASSWORD |
Backend | Basic auth for server-side push |
Edge Cases
- If IndexedDB is unavailable (e.g., private browsing), falls back to a volatile in-memory queue.
- Backend proxy failure: entries remain in IndexedDB queue and are retried on next flush cycle.
Related Docs
- Logging System – server-side logging architecture
- Server logs reach OpenObserve via Promtail (Docker socket + file discovery)