On this page
  1. What’s in a record
  2. Filter by event or by editor
  3. Tail it live during a deploy
  4. Match a symptom to its event
  5. See also

Read cairn’s logs

Every operationally meaningful action, from a sign-in to a rejected upload, writes one JSON record through console.log, console.warn, or console.error. Log events is the full vocabulary those records draw from. Reading them in production takes one setting. observability.enabled must already be true in your wrangler.jsonc. The Deploy to Cloudflare step covers turning it on. Without it the records still fire, but Workers Logs keeps none of them.

What’s in a record

Every record carries the same three-field envelope, plus whatever fields that event defines:

{
  "level": "warn",
  "event": "commit.failed",
  "timestamp": "2026-07-03T14:02:11.401Z",
  "concept": "posts",
  "id": "2026-07-01-summer-picnic",
  "editor": "jamie@ourclub.org",
  "reason": "conflict",
  "branch": "cairn/posts/2026-07-01-summer-picnic"
}

Because each record is a JSON object, Workers Logs indexes event, editor, reason, and every other key as its own field. You query a field directly instead of text-searching a message string. Filter on event. level, one of info, warn, or error, tells you its severity.

The records never carry a magic-link token or a session ID, only an editor’s email address for attribution. A record is safe to read, copy, and paste into a bug report or a chat message.

Filter by event or by editor

Open your Worker’s Logs view in the Cloudflare dashboard (Workers & Pages > your Worker > Logs) to search the persisted history, kept for up to seven days. Its search bar takes field queries directly against the keys cairn writes:

To findQuery
Every failed commitevent = "commit.failed"
One editor’s activityeditor = "jamie@ourclub.org"
A CSRF-rejected admin requestevent = "guard.rejected" AND reason = "csrf"
Warnings and errors onlylevel != "info"

Cloudflare’s own request metadata sits alongside cairn’s fields under a $metadata. prefix ($metadata.trigger, $metadata.service), if you need to correlate a log record with the request that produced it.

Tail it live during a deploy

wrangler dev already prints your records straight to the terminal you’re running it in, so tailing only matters for a deployed Worker. From your site’s project directory:

npx wrangler tail --format pretty --search commit.failed

--search matches the raw text of each record, so it casts a coarser net than the dashboard’s field queries. Use it to watch one event name scroll by while you reproduce a bug. Tailing streams live. It keeps nothing, so the records disappear once the session ends. The dashboard’s Live tab gives you the same stream in the browser.

Match a symptom to its event

SymptomEvent to look forWhat it tells you
An editor can’t sign inauth.link.send_failed or guard.rejectedA send failure’s conditionId, or the guard’s reason
A save does nothingcommit.failedreason: "conflict" is a stale edit; an error field is GitHub’s own refusal
Publishing doesn’t deploypublish.failedSame shape as commit.failed
An upload, replace, or delete doesn’t go throughmedia.upload_failed, media.replace_blocked, media.delete_blockedmedia.upload_failed’s reason names the cause; *_blocked means cairn refused a still-referenced or unconfirmed operation, with foundIn counting the blocking entries

Troubleshooting walks each of these from symptom to fix.

See also

Edit this page on GitHub(opens in a new tab)