Remote Logging
Remote Logging lets you send debug, info, warning, and error logs from your game to Quest Data. View and search them in the dashboard — no need to ask players for log files.
Why Remote Logging?
Section titled “Why Remote Logging?”- Debug production issues without asking players to send log files
- Filter by log level to focus on errors or warnings
- Search by player to investigate specific user reports
- 30-day retention with automatic cleanup
Sending Logs from Godot
Section titled “Sending Logs from Godot”The SDK provides helper functions for each log level:
# Error — crashes, exceptions, critical failuresQuestData.log_error("Failed to load save data", { "save_slot": 2, "error": "corrupted JSON"})
# Warning — performance issues, degraded experienceQuestData.log_warning("Frame drop detected: 8 FPS", { "scene": "castle_boss", "avg_fps": 8, "duration_ms": 3000})
# Info — player milestones, important state changesQuestData.log_info("Player reached level 5", { "level": 5, "time_played": 1200})
# Debug — verbose diagnostics (disabled in production by default)QuestData.log_debug("Physics tick: 142 active bodies", { "bodies": 142, "sleeping": 58})SDK Behavior
Section titled “SDK Behavior”The SDK logging system works similarly to event tracking:
- Batching: Logs are queued in memory and sent every 10 seconds or when 50 logs are queued
- Offline persistence: If the server is unreachable, logs are saved to
user://quest_log_queue.saveand retried on next flush - Level filtering: On init, the SDK fetches the game’s minimum log level from the server (
GET /v1/logs/config). Logs below this level are silently discarded without sending - Auto-enrichment:
player_idandsession_idare automatically added to each log - Crash-safe: Logs are saved to disk on game exit (
_exit_tree)
# Check current configured log levelvar level = QuestData.get_log_level() # e.g. "warning"Log Structure
Section titled “Log Structure”Each log entry contains:
| Field | Type | Required | Description |
|---|---|---|---|
level | string | Yes | debug, info, warning, or error |
message | string | Yes | Human-readable log message |
context | object | No | Arbitrary structured data (scene, stack trace, device info) |
player_id | string | No | Player identifier (auto-set by SDK if available) |
session_id | string | No | Session identifier (auto-set by SDK) |
REST API
Section titled “REST API”If you’re not using the Godot SDK, send logs via the REST API directly.
Send a Single Log
Section titled “Send a Single Log”curl -X POST https://api.questdata.io/v1/logs \ -H "Content-Type: application/json" \ -H "x-game-api-key: YOUR_API_KEY" \ -d '{ "level": "error", "message": "NullReferenceException in PlayerController", "context": {"scene": "forest_1", "line": 142}, "player_id": "player-123", "session_id": "sess-abc" }'Response: 202 Accepted
{ "received": 1 }Send a Batch of Logs
Section titled “Send a Batch of Logs”Send up to 100 logs in a single request for better performance:
curl -X POST https://api.questdata.io/v1/logs \ -H "Content-Type: application/json" \ -H "x-game-api-key: YOUR_API_KEY" \ -d '[ {"level": "error", "message": "Crash in scene forest_1"}, {"level": "warning", "message": "Low memory: 45MB free"}, {"level": "info", "message": "Checkpoint saved"} ]'Response: 202 Accepted
{ "received": 2, "filtered": 1 }The filtered field shows how many logs were dropped because they were below the game’s configured minimum log level.
Read Logs
Section titled “Read Logs”curl "https://api.questdata.io/v1/logs?level=error&limit=20" \ -H "x-game-api-key: YOUR_API_KEY"Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
level | string | — | Filter by level: debug, info, warning, error |
player_id | string | — | Filter by player |
session_id | string | — | Filter by session |
search | string | — | Full-text search in message (uses PostgreSQL tsvector) |
context_search | JSON string | — | Filter by context fields (e.g. {"scene":"forest"}) |
from | ISO date | — | Start date |
to | ISO date | — | End date |
limit | number | 50 | Results per page (max 200) |
offset | number | 0 | Pagination offset |
Response:
{ "logs": [ { "id": "a1b2c3d4-...", "level": "error", "message": "NullReferenceException in PlayerController", "context": {"scene": "forest_1", "line": 142}, "player_id": "player-123", "session_id": "sess-abc", "server_timestamp": "2026-04-07T12:30:00.000Z" } ], "total": 42}Get Log Level Configuration
Section titled “Get Log Level Configuration”The SDK fetches this on init to know which logs to send:
curl "https://api.questdata.io/v1/logs/config" \ -H "x-game-api-key: YOUR_API_KEY"Response:
{ "log_level": "warning" }Only logs at or above this level are accepted. Debug and info logs would be filtered out in this example.
Update Log Level (Dashboard API)
Section titled “Update Log Level (Dashboard API)”curl -X PATCH "https://api.questdata.io/v1/games/GAME_ID/log-level" \ -H "Authorization: Bearer YOUR_JWT" \ -H "Content-Type: application/json" \ -d '{"log_level": "info"}'Response:
{ "log_level": "info" }Requires Admin or Owner role.
Log Level Statistics
Section titled “Log Level Statistics”curl "https://api.questdata.io/v1/logs/stats" \ -H "x-game-api-key: YOUR_API_KEY"Response:
{ "debug": 150, "info": 89, "warning": 34, "error": 12, "total": 285}Dashboard
Section titled “Dashboard”The Logs page in the dashboard (under Configuration > Logs) provides:
- Min Level Config — Set the minimum log level for the game (Debug/Info/Warning/Error). Logs below this level are rejected by the backend, saving bandwidth and storage. Default: Warning.
- Level Stats Cards — Quick overview of error, warning, info, and debug counts (last 7 days)
- Filter Controls — Dropdown for log level, text inputs for player ID and message search
- Log List — Color-coded entries sorted by time, click to expand context JSON
- Pagination — Navigate through large log sets
- Refresh — Manual refresh button (WebSocket live-tail coming soon)
Level Color Coding
Section titled “Level Color Coding”| Level | Color | Use Case |
|---|---|---|
error | Red | Crashes, exceptions, data corruption |
warning | Yellow | FPS drops, memory pressure, network issues |
info | Blue | Player milestones, state changes |
debug | Gray | Verbose diagnostics, physics ticks |
Best Practices
Section titled “Best Practices”-
Use context objects liberally — Include scene name, device info, stack traces. This is what makes remote debugging possible.
-
Don’t log on every frame — Batch periodic metrics (e.g. average FPS every 10 seconds) instead of per-frame logging.
-
Configure log level in the dashboard — Set the minimum level to
warningorerrorin production. The backend filters out logs below this threshold automatically, so you don’t need to change SDK code between environments. -
Include player_id — Without it, you can’t correlate logs to specific player reports.
-
Keep messages short and searchable — Use the
contextobject for details, keep themessagefield as a one-line summary. Messages are limited to 10,000 characters. -
Keep context objects small — Maximum 50 properties per context object. Use flat structures instead of deeply nested objects.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Message length | 10,000 characters |
| Context properties | 50 per log entry |
| Player/Session ID | 255 characters |
| Batch size | 500 logs per request |
| Rate limit | 1,000 requests/min per API key |
| Effective throughput | Up to 500,000 logs/min (500 per batch x 1,000 req/min) |
| Data retention | 30 days (automatic cleanup) |
| Stats time window | Last 7 days |
Data Retention
Section titled “Data Retention”Logs have per-level retention policies, cleaned up daily:
| Level | Retention |
|---|---|
| Debug | 7 days |
| Info | 14 days |
| Warning | 30 days |
| Error | 90 days |
TimescaleDB’s 30-day retention policy acts as a safety net. The level statistics (error/warning/info/debug counts) cover the last 7 days.
Error Handling
Section titled “Error Handling”| HTTP Status | Cause |
|---|---|
202 | Logs accepted successfully |
400 | Invalid log level, missing message, or invalid schema |
401 | Missing or invalid x-game-api-key header |
413 | Batch too large (over 500 logs) |
429 | Rate limit exceeded (1,000 req/min per API key) |