The gateway writes a structured JSON line for every request. Use it for compliance, debugging, and security analysis.
Enable
Section titled “Enable”qql-go serve--audit--audit-file audit.jsonl # omit to write to stderrLog Format
Section titled “Log Format”Each request produces one JSON line:
Successful request:
{ "ts": "2026-06-18T10:00:00Z", "subject": "usr_alice", "email": "alice@example.com", "tenant_id": "acme-corp", "roles": ["reader"], "operation": "EXEC", "collection": "docs", "query": "QUERY 'company' FROM docs LIMIT 5", "status": "ok", "latency_ms": 12, "allowed": true}Denied request:
{ "ts": "2026-06-18T10:00:01Z", "subject": "usr_bob", "operation": "EXEC", "status": "denied", "denied": "true", "denied_reason": "operation DELETE not permitted for current token", "latency_ms": 1}Fields
Section titled “Fields”| Field | Description |
|---|---|
ts | ISO 8601 timestamp |
subject | JWT sub claim |
email | JWT email claim (if present) |
tenant_id | Extracted tenant claim |
roles | Extracted role claim(s) |
operation | RPC type (EXEC, EXEC_BATCH, EXPLAIN, HEALTH, CONVERT) |
collection | Target collection name |
query | The QQL query string (as received) |
status | ok or denied |
latency_ms | End-to-end request latency in milliseconds |
allowed | true if the request passed policy |
denied | "true" if the request was denied |
denied_reason | Human-readable denial reason |
Implementation
Section titled “Implementation”Audit logging uses a context-passing pattern:
- The interceptor creates an empty
AuditMetain the request context. - The handler fills it with AST details (collection name, operation type, query text).
- After execution, the interceptor builds the final JSON entry and writes it.
Querying Audit Logs
Section titled “Querying Audit Logs”Since each line is JSON, you can query with jq:
All denied requestsSection titled “All denied requests”jq 'select(.status == "denied")' audit.jsonlRequests by tenantSection titled “Requests by tenant”jq 'select(.tenant_id == "acme-corp")' audit.jsonlSlow queries (> 100ms)Section titled “Slow queries (> 100ms)”jq 'select(.latency_ms > 100)' audit.jsonlAll DROP operationsSection titled “All DROP operations”jq 'select(.query | startswith("DROP"))' audit.jsonl