Logs
The logging system captures server-side activity into an in-memory ring buffer with REST and SSE streaming access. Logs are ideal for debugging extensions, cron jobs, and API calls without needing external log aggregation.
Log Entry Structure
interface LogEntry {
id: string; // Unique entry ID
timestamp: string; // ISO timestamp
level: 'debug' | 'info' | 'warn' | 'error';
source: 'extension' | 'api' | 'emitter' | 'database' | 'auth' | 'system';
message: string;
data?: Record<string, any>; // Optional structured data
extensionId?: string;
extensionName?: string;
collection?: string;
requestId?: string;
userId?: string;
}Configuration
| Environment variable | Default | Description |
|---|---|---|
LOG_MAX_ENTRIES | 1000 | Ring buffer size |
LOG_MIN_LEVEL | debug | Minimum log level (debug, info, warn, error) |
LOG_CONSOLE_OUTPUT | true | Also write to stdout |
REST API
Get logs
GET /api/logsRequires authentication (unauthenticated requests receive 401). Access is granted to admins and to any user whose policy grants the system:logs module access key (Policy editor → Module-Level Access); other users receive 403. The same gate applies to GET /api/logs/stream. Clearing logs (DELETE /api/logs) remains admin-only.
Query parameters:
| Parameter | Description | Example |
|---|---|---|
limit | Number of entries to return (default: 50, max: 500) | ?limit=100 |
level | Filter by level | ?level=error |
source | Filter by source | ?source=extension |
extensionId | Filter by extension UUID | ?extensionId=<uuid> |
extensionName | Filter by extension name (partial match) | ?extensionName=slug |
collection | Filter by collection | ?collection=articles |
since | Entries after this timestamp or relative time (1h, 30m, 1d) | ?since=2025-01-01T00:00:00Z |
until | Entries before this timestamp | ?until=2025-01-02T00:00:00Z |
search | Text search in message | ?search=validation |
Response:
{
"data": {
"count": 1,
"entries": [
{
"id": "log-uuid",
"timestamp": "2025-01-15T10:00:00Z",
"level": "error",
"source": "extension",
"message": "Validation failed: title is required",
"extensionName": "article-validator",
"collection": "articles"
}
]
}
}Clear logs
DELETE /api/logsClears all log entries from the in-memory buffer. Requires admin access.
Stream logs (SSE)
GET /api/logs/streamReturns a text/event-stream response. Each new log entry is pushed as a Server-Sent Event. Supports level, source, and extensionName query parameters for filtering.
const es = new EventSource('/api/logs/stream?level=error', {
headers: { Authorization: `Bearer ${token}` },
});
es.onmessage = (event) => {
const entry = JSON.parse(event.data);
console.log(entry.message);
};MCP Tool
The logs MCP tool exposes log access to AI agents. The tool is admin-only — all actions (including tail and read) require an admin token; the system:logs key applies to the REST API and Logs page only.
// Tail the last 50 entries
{ "action": "tail", "lines": 50, "level": "error" }
// Read with filters
{ "action": "read", "since": "2025-01-15T00:00:00Z", "source": "extension" }
// Search log messages
{ "action": "search", "query": "validation failed", "source": "extension" }
// Get buffer statistics
{ "action": "stats" }
// Clear all logs (admin only)
{ "action": "clear" }The log buffer is in-memory and is cleared on server restart. In clustered deployments each worker process keeps its own buffer, so /api/logs, the SSE stream, and the Logs page show only the output of the worker that happened to serve your request. For persistent and complete log storage, aggregate container stdout — every worker echoes its logs to the console when LOG_CONSOLE_OUTPUT is enabled (the default), so stdout captures the whole cluster.