cloudflareCloudflare Logs

Stream logs from Cloudflare Workers into Gonzo for real-time analysis, filtering, and AI-powered insights.

Stream Worker invocation traces into Gonzo with a jq normalizer. Cloudflare's wrangler tail streams pretty-printed JSON envelopes over WebSocket — one per invocation, containing all console.log calls and exceptions. The normalizer flattens these into per-line JSONL that Gonzo can parse.

Quick Start

wrangler tail --format json \
  | jq --unbuffered -c '
    . as $inv |
    (.logs[] | {
      timestamp: .timestamp,
      level: .level,
      message: (.message | map(if type == "string" then . else tostring end) | join(" ")),
      script: $inv.scriptName,
      outcome: $inv.outcome,
      url: $inv.event.request.url,
      method: $inv.event.request.method,
      status: ($inv.event.response.status // null),
      colo: $inv.event.request.cf.colo
    }),
    (.exceptions[]? | {
      timestamp: .timestamp,
      level: "error",
      message: (.name + ": " + .message),
      stack: .stack,
      script: $inv.scriptName,
      outcome: $inv.outcome,
      url: $inv.event.request.url,
      method: $inv.event.request.method,
      status: ($inv.event.response.status // null)
    })
  ' \
  | gonzo

The --unbuffered flag on jq is required — without it, output buffers and logs stall in the pipe.

Prerequisites

  • Gonzo installed

  • Wranglerarrow-up-right installed (npm install -g wrangler)

  • jq installed (brew install jq)

  • A Cloudflare account with at least one deployed Worker

What's Visible

Component
In tail?
Notes

console.log/warn/error

✅ Yes

All levels: log, warn, error, debug, info

Uncaught exceptions

✅ Yes

With stack traces

Request/response metadata

✅ Yes

URL, method, status, colo

Cron trigger invocations

✅ Yes

url/method/status will be null

D1 / KV / R2 / AI / Vectorize

❌ No

Instrument manually with console.log

Usage Patterns

Real-time streaming (WebSocket, default):

Tail a specific Worker by name:

Filter by status:

Filter by HTTP method:

Filter by search string:

Sample high-traffic Workers:

Capture to file for replay:

With local AI (logs never leave your machine):

Pages Functions

Pages Functions use the same Worker runtime and produce identical tail output. The normalizer works without modification.

When piping (non-interactive mode), the --environment flag or a deployment ID is required. Static asset requests don't trigger Functions and won't appear in the tail.

Rate Limits and Sampling

Streaming (wrangler tail) uses WebSocket. For high-traffic Workers, Cloudflare may auto-sample, dropping some invocation traces. Use --sampling-rate (0.0–1.0) to proactively reduce volume.

Concurrent sessions: Maximum 10 tail sessions per Worker.

There is no fetch mode with --lines or --since. wrangler tail is streaming only. For historical queries, use Workers Logsarrow-up-right in the Cloudflare dashboard.

Structured Logging Tips

Emit structured JSON via console.log for the best Gonzo experience. Objects passed directly also work — the normalizer stringifies them.

Troubleshooting

Symptom
Cause & fix

Logs not appearing

Missing --unbuffered on jq. Without it, jq buffers output and logs stall.

"Could not create tail"

Hit the 10 concurrent tail session limit. Close other wrangler tail sessions or dashboard live log views.

Cron logs show null fields

Expected. Cron triggers have no HTTP request — url, method, status, colo are null.

D1/KV operations missing

Binding operations don't produce tail events. Add console.log around binding calls.

Pretty-printed JSON instead of JSONL

Don't pipe raw wrangler tail output to Gonzo. The jq normalizer compacts it.

Out-of-order logs under concurrency

Workers execute at edge locations globally. Concurrent requests may arrive out of timestamp order.


Time to complete: 5 minutes Prerequisites: Wrangler, jq, Gonzo installed Full guide: guides/CLOUDFLARE_USAGE_GUIDE.mdarrow-up-right

Last updated