Observability
Find out what happened when a user reports a problem.
A user says the contact form failed. This page walks you from that report to the exact call, who made it, which version of your code handled it, and how it ended. Then it covers spotting patterns across many calls, seeing what each compile changed, and health checks.
Everything here is stored in ordinary Postgres tables, so you can read it with SQL. The parts an agent needs are also available over MCP and the CLI.
Trace one failed call
Every function response carries an
x-graft-correlation-idheader, whether the call worked or failed:curl -i -X POST https://cms.example.com/api/fn/submitContact \ -H 'content-type: application/json' \ -d '{"email":"a@b.co"}' # x-graft-correlation-id: 4f1c…Show it in your app's error message, or log it, so a user's report can include it.
select * from audit_log where correlation_id = '4f1c…';The lookup is indexed, so it is fast even on a large log.
The row tells you who called, what ran, how it ended, and which commit of your code served it. The next section lists every column.
What the audit log records
One row for every function call, whatever the outcome:
| Column | What it holds |
|---|---|
correlation_id | The id returned in the response header |
function_name, function_kind | What ran |
actor_kind, actor_id | Who ran it. actor_id is NULL for anonymous callers |
status | "ok", or the error code it failed with |
duration_ms | How long it took |
git_sha | The commit of the code that served it |
branch_id | Which content branch the call ran on |
rate_key | What the rate limit counted: the caller's id, or their IP |
created_at | When |
The inputs are not recorded, so the log never holds a copy of your users'
data. If you need the exact input of a call that waited for approval, it is on
that call's row in the approvals table.
A row is written even when the call is refused, for example for bad input or a missing permission. Graft writes the row as soon as it knows who is calling, then fills in the result.
git_sha comes from VERCEL_GIT_COMMIT_SHA or GITHUB_SHA when your platform
sets them, and graft serve reads it from the local checkout. It answers
"which deploy did this?" without searching your logs.
Spot patterns across calls
Because refused calls are logged too, status shows how your functions fail.
For example, the errors from the last day:
select status, count(*)
from audit_log
where created_at > now() - interval '1 day'
group by status
order by count(*) desc;
See what each compile changed
The compilations table records every compile: the branch, the Git commit it
read, the number of documents, and how many it added, changed, and removed.
| Where | How |
|---|---|
| CLI | graft compilations |
| MCP | list_compilations |
| Studio | Developer, then History |
| HTTP | GET /api/studio/v1/compilations |
Each row records the Git commit it compiled, so you can restore your content to exactly how it was then. See Developer views and the revert routes in the Studio API.
Review past approvals
The approvals table is a record in its own right. Every destructive call that
waited for approval has a row with who asked, the exact input, the decision,
and who decided. Which database login made the decision is recorded by the
database itself, so it cannot be faked by the app. See
Hardening.
Check the server is healthy
graft serve answers GET /healthz. It makes a real round trip to the
database before answering:
{
"ok": true,
"branch": "main",
"collections": 3,
"functions": 2,
"gitSha": "d7aa173…"
}
If the database does not answer, it returns 503 with
{ "ok": false, "error": "…" }. Point your platform's health check at it. The
gitSha field also confirms which build is live.
Keep the log a manageable size
Graft never deletes audit rows. On a busy site the table grows, and rate limits only read recent rows. Delete old ones on your own schedule, keeping whatever your compliance rules require:
delete from audit_log where created_at < now() - interval '90 days';
Run it with the operator login. A hardened app login cannot delete audit rows, on purpose.
What Graft leaves to your platform
- Logs. Graft writes to standard output. Collect it the way your host does.
- Metrics. There is no metrics endpoint. The audit log is the source, so aggregate it with SQL or export it.
- Tracing. Graft does not emit traces. Pass the correlation id into your own spans if you use tracing.
If writing an audit row fails, Graft logs the failure with the correlation id and still completes the call. Failing a user's request because the record could not be written would be worse than missing one record.
Next steps
- Audit & approvals explains what the log is for.
- Rate limits covers the other job these rows do.
- Deploy shows where
/healthzlives.