Skip to content

Audit log

With --audit-log, every request produces one JSON line in a dedicated file (- for stdout), separate from the operational log. Authentication failures and denials produce events too.

{"ts":"...","event":"query","request_id":"1a1147f8f9b0...","user":"analyst","roles":["analyst"],
"remote_addr":"10.0.0.7","database":"sales","statement_type":"SELECT",
"sql":"SELECT count(*) FROM orders WHERE amount > ?","sql_sha256":"...","params_count":0,
"tables":["sales.main.orders"],"resolved":true,"decision":"allow","decided_by":"policy",
"policy_sha256":"...","status":200,"rows":1,"bytes":42,
"timing_ms":{"queue":0,"inspect":1.2,"authorize":0.25,"execute":1.7,"total":3.3},
"curral_version":"0.4.1"}
  • decision: allow, deny or error.
  • decided_by: policy, engine, auth, queue, audit, request, protection or concurrency.
  • policy_sha256: proves which version of the rules decided.
  • request_id: also in the X-Request-Id response header and the operational log.

By default SQL literals become ?, because they may contain personal data. Parameter values are never logged, only their count, and passwords never appear.

--audit-sql
redacted (default) literals replaced by ?
full the SQL as sent
hash only sql_sha256

If the SQL uses constructs the tokenizer cannot read safely, only the hash is kept.

Before executing, curral reserves room in the audit queue (--audit-queue, 4096 events). If the file cannot be written (full disk, permissions) or the queue is full, the query gets 503 and does not run. Recovery is checked every 5 seconds and logged as audit_recovered.

A process crash between a write’s commit and its event being recorded can still lose that event.

Move the file and send SIGHUP. The new file starts with an audit_reopened event; copytruncate is not needed.